chiark / gitweb /
sd-dhcp-client: fix REBOOT state handling
[elogind.git] / src / libsystemd-network / sd-dhcp-client.c
index 7cf1b80d29925451b7d0712883e4c17733e1d9df..2906f2cb3a018de58c11f0ffc161fb2efdc9cb6e 100644 (file)
@@ -23,6 +23,7 @@
 #include <stdio.h>
 #include <net/ethernet.h>
 #include <net/if_arp.h>
+#include <linux/if_infiniband.h>
 #include <netinet/ether.h>
 #include <sys/param.h>
 #include <sys/ioctl.h>
@@ -37,6 +38,8 @@
 #include "dhcp-lease-internal.h"
 #include "sd-dhcp-client.h"
 
+#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
+
 struct sd_dhcp_client {
         RefCount n_ref;
 
@@ -57,12 +60,14 @@ struct sd_dhcp_client {
                 uint8_t type;
                 struct ether_addr mac_addr;
         } _packed_ client_id;
+        uint8_t mac_addr[MAX_MAC_ADDR_LEN];
+        size_t mac_addr_len;
+        uint16_t arp_type;
         char *hostname;
         char *vendor_class_identifier;
         uint32_t mtu;
         uint32_t xid;
         usec_t start_time;
-        uint16_t secs;
         unsigned int attempt;
         usec_t request_sent;
         sd_event_source *timeout_t1;
@@ -163,15 +168,25 @@ int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
         return 0;
 }
 
-int sd_dhcp_client_set_mac(sd_dhcp_client *client,
-                           const struct ether_addr *addr) {
+int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
+                           size_t addr_len, uint16_t arp_type) {
         DHCP_CLIENT_DONT_DESTROY(client);
         bool need_restart = false;
 
         assert_return(client, -EINVAL);
         assert_return(addr, -EINVAL);
+        assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
+        assert_return(arp_type > 0, -EINVAL);
+
+        if (arp_type == ARPHRD_ETHER)
+                assert_return(addr_len == ETH_ALEN, -EINVAL);
+        else if (arp_type == ARPHRD_INFINIBAND)
+                assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
+        else
+                return -EINVAL;
 
-        if (memcmp(&client->client_id.mac_addr, addr, ETH_ALEN) == 0)
+        if (client->mac_addr_len == addr_len &&
+            memcmp(&client->mac_addr, addr, addr_len) == 0)
                 return 0;
 
         if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
@@ -181,6 +196,10 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client,
                 client_stop(client, DHCP_EVENT_STOP);
         }
 
+        memcpy(&client->mac_addr, addr, addr_len);
+        client->mac_addr_len = addr_len;
+        client->arp_type = arp_type;
+
         memcpy(&client->client_id.mac_addr, addr, ETH_ALEN);
         client->client_id.type = 0x01;
 
@@ -301,10 +320,12 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
         _cleanup_free_ DHCPPacket *packet;
         size_t optlen, optoffset, size;
         be16_t max_size;
+        usec_t time_now;
+        uint16_t secs;
         int r;
 
         assert(client);
-        assert(client->secs);
+        assert(client->start_time);
         assert(ret);
         assert(_optlen);
         assert(_optoffset);
@@ -318,13 +339,21 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
                 return -ENOMEM;
 
         r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
-                              optlen, &optoffset);
+                              client->arp_type, optlen, &optoffset);
         if (r < 0)
                 return r;
 
         /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
            refuse to issue an DHCP lease if 'secs' is set to zero */
-        packet->dhcp.secs = htobe16(client->secs);
+        r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
+        if (r < 0)
+                return r;
+        assert(time_now >= client->start_time);
+
+        /* seconds between sending first and last DISCOVER
+         * must always be strictly positive to deal with broken servers */
+        secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
+        packet->dhcp.secs = htobe16(secs);
 
         /* RFC2132 section 4.1
            A client that cannot receive unicast IP datagrams until its protocol
@@ -337,14 +366,17 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
            Note: some interfaces needs this to be enabled, but some networks
            needs this to be disabled as broadcasts are filteretd, so this
            needs to be configurable */
-        if (client->request_broadcast)
+        if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
                 packet->dhcp.flags = htobe16(0x8000);
 
         /* RFC2132 section 4.1.1:
            The client MUST include its hardware address in the â€™chaddr’ field, if
-           necessary for delivery of DHCP reply messages.
+           necessary for delivery of DHCP reply messages.  Non-Ethernet
+           interfaces will leave 'chaddr' empty and use the client identifier
+           instead (eg, RFC 4390 section 2.1).
          */
-        memcpy(&packet->dhcp.chaddr, &client->client_id.mac_addr, ETH_ALEN);
+        if (client->arp_type == ARPHRD_ETHER)
+                memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN);
 
         /* Some DHCP servers will refuse to issue an DHCP lease if the Client
            Identifier option is not set */
@@ -418,24 +450,12 @@ static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
 static int client_send_discover(sd_dhcp_client *client) {
         _cleanup_free_ DHCPPacket *discover = NULL;
         size_t optoffset, optlen;
-        usec_t time_now;
         int r;
 
         assert(client);
         assert(client->state == DHCP_STATE_INIT ||
                client->state == DHCP_STATE_SELECTING);
 
-        /* See RFC2131 section 4.4.1 */
-
-        r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
-        if (r < 0)
-                return r;
-        assert(time_now >= client->start_time);
-
-        /* seconds between sending first and last DISCOVER
-         * must always be strictly positive to deal with broken servers */
-        client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
-
         r = client_message_init(client, &discover, DHCP_DISCOVER,
                                 &optlen, &optoffset);
         if (r < 0)
@@ -700,8 +720,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
         if (r < 0)
                 goto error;
 
-        r = sd_event_source_set_name(client->timeout_resend,
-                                     "dhcp4-resend-timer");
+        r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
         if (r < 0)
                 goto error;
 
@@ -778,8 +797,7 @@ static int client_initialize_io_events(sd_dhcp_client *client,
         if (r < 0)
                 goto error;
 
-        r = sd_event_source_set_name(client->receive_message,
-                                     "dhcp4-receive-message");
+        r = sd_event_source_set_description(client->receive_message, "dhcp4-receive-message");
         if (r < 0)
                 goto error;
 
@@ -809,8 +827,7 @@ static int client_initialize_time_events(sd_dhcp_client *client) {
         r = sd_event_source_set_priority(client->timeout_resend,
                                          client->event_priority);
 
-        r = sd_event_source_set_name(client->timeout_resend,
-                                     "dhcp4-resend-timer");
+        r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
         if (r < 0)
                 goto error;
 
@@ -843,17 +860,17 @@ static int client_start(sd_dhcp_client *client) {
 
         client->xid = random_u32();
 
-        r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid, client->client_id.mac_addr);
+        r = dhcp_network_bind_raw_socket(client->index, &client->link,
+                                         client->xid, client->mac_addr,
+                                         client->mac_addr_len, client->arp_type);
         if (r < 0) {
                 client_stop(client, r);
                 return r;
         }
         client->fd = r;
 
-        if (client->state == DHCP_STATE_INIT) {
+        if (client->state == DHCP_STATE_INIT || client->state == DHCP_STATE_INIT_REBOOT)
                 client->start_time = now(clock_boottime_or_monotonic());
-                client->secs = 0;
-        }
 
         return client_initialize_events(client, client_receive_message_raw);
 }
@@ -887,7 +904,9 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
         client->state = DHCP_STATE_REBINDING;
         client->attempt = 1;
 
-        r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid, client->client_id.mac_addr);
+        r = dhcp_network_bind_raw_socket(client->index, &client->link,
+                                         client->xid, client->mac_addr,
+                                         client->mac_addr_len, client->arp_type);
         if (r < 0) {
                 client_stop(client, r);
                 return 0;
@@ -919,7 +938,7 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
 
         r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
         if (r != DHCP_OFFER) {
-                log_dhcp_client(client, "receieved message was not an OFFER, ignoring");
+                log_dhcp_client(client, "received message was not an OFFER, ignoring");
                 return -ENOMSG;
         }
 
@@ -930,7 +949,7 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
         if (lease->address == INADDR_ANY ||
             lease->server_address == INADDR_ANY ||
             lease->lifetime == 0) {
-                log_dhcp_client(client, "receieved lease lacks address, server "
+                log_dhcp_client(client, "received lease lacks address, server "
                                 "address or lease lifetime, ignoring");
                 return -ENOMSG;
         }
@@ -938,7 +957,7 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
         if (lease->subnet_mask == INADDR_ANY) {
                 r = dhcp_lease_set_default_subnet_mask(lease);
                 if (r < 0) {
-                        log_dhcp_client(client, "receieved lease lacks subnet "
+                        log_dhcp_client(client, "received lease lacks subnet "
                                         "mask, and a fallback one can not be "
                                         "generated, ignoring");
                         return -ENOMSG;
@@ -983,7 +1002,7 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
         }
 
         if (r != DHCP_ACK) {
-                log_dhcp_client(client, "receieved message was not an ACK, ignoring");
+                log_dhcp_client(client, "received message was not an ACK, ignoring");
                 return -ENOMSG;
         }
 
@@ -994,7 +1013,7 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
         if (lease->address == INADDR_ANY ||
             lease->server_address == INADDR_ANY ||
             lease->lifetime == 0) {
-                log_dhcp_client(client, "receieved lease lacks address, server "
+                log_dhcp_client(client, "received lease lacks address, server "
                                 "address or lease lifetime, ignoring");
                 return -ENOMSG;
         }
@@ -1002,7 +1021,7 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
         if (lease->subnet_mask == INADDR_ANY) {
                 r = dhcp_lease_set_default_subnet_mask(lease);
                 if (r < 0) {
-                        log_dhcp_client(client, "receieved lease lacks subnet "
+                        log_dhcp_client(client, "received lease lacks subnet "
                                         "mask, and a fallback one can not be "
                                         "generated, ignoring");
                         return -ENOMSG;
@@ -1122,8 +1141,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
         if (r < 0)
                 return r;
 
-        r = sd_event_source_set_name(client->timeout_expire,
-                                     "dhcp4-lifetime");
+        r = sd_event_source_set_description(client->timeout_expire, "dhcp4-lifetime");
         if (r < 0)
                 return r;
 
@@ -1150,8 +1168,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
         if (r < 0)
                 return r;
 
-        r = sd_event_source_set_name(client->timeout_t2,
-                                     "dhcp4-t2-timeout");
+        r = sd_event_source_set_description(client->timeout_t2, "dhcp4-t2-timeout");
         if (r < 0)
                 return r;
 
@@ -1177,8 +1194,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
         if (r < 0)
                 return r;
 
-        r = sd_event_source_set_name(client->timeout_t1,
-                                     "dhcp4-t1-timer");
+        r = sd_event_source_set_description(client->timeout_t1, "dhcp4-t1-timer");
         if (r < 0)
                 return r;
 
@@ -1223,8 +1239,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
                         if (r < 0)
                                 goto error;
 
-                        r = sd_event_source_set_name(client->timeout_resend,
-                                                     "dhcp4-resend-timer");
+                        r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
                         if (r < 0)
                                 goto error;
                 } else if (r == -ENOMSG)
@@ -1242,6 +1257,9 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
                 if (r >= 0) {
                         client->timeout_resend =
                                 sd_event_source_unref(client->timeout_resend);
+                        client->receive_message =
+                                sd_event_source_unref(client->receive_message);
+                        client->fd = asynchronous_close(client->fd);
 
                         if (IN_SET(client->state, DHCP_STATE_REQUESTING,
                                    DHCP_STATE_REBOOTING))
@@ -1331,6 +1349,9 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
         sd_dhcp_client *client = userdata;
         _cleanup_free_ DHCPMessage *message = NULL;
         int buflen = 0, len, r;
+        const struct ether_addr zero_mac = { { 0, 0, 0, 0, 0, 0 } };
+        const struct ether_addr *expected_chaddr = NULL;
+        uint8_t expected_hlen = 0;
 
         assert(s);
         assert(client);
@@ -1367,13 +1388,26 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
                 return 0;
         }
 
-        if (message->htype != ARPHRD_ETHER || message->hlen != ETHER_ADDR_LEN) {
-                log_dhcp_client(client, "not an ethernet packet");
+        if (message->htype != client->arp_type) {
+                log_dhcp_client(client, "packet type does not match client type");
+                return 0;
+        }
+
+        if (client->arp_type == ARPHRD_ETHER) {
+                expected_hlen = ETH_ALEN;
+                expected_chaddr = (const struct ether_addr *) &client->mac_addr;
+        } else {
+               /* Non-ethernet links expect zero chaddr */
+               expected_hlen = 0;
+               expected_chaddr = &zero_mac;
+        }
+
+        if (message->hlen != expected_hlen) {
+                log_dhcp_client(client, "unexpected packet hlen %d", message->hlen);
                 return 0;
         }
 
-        if (memcmp(&message->chaddr[0], &client->client_id.mac_addr,
-                   ETH_ALEN)) {
+        if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) {
                 log_dhcp_client(client, "received chaddr does not match "
                                 "expected: ignoring");
                 return 0;
@@ -1455,7 +1489,6 @@ static int client_receive_message_raw(sd_event_source *s, int fd,
 }
 
 int sd_dhcp_client_start(sd_dhcp_client *client) {
-        char buffer[ETHER_ADDR_TO_STRING_MAX];
         int r;
 
         assert_return(client, -EINVAL);
@@ -1469,9 +1502,7 @@ int sd_dhcp_client_start(sd_dhcp_client *client) {
 
         r = client_start(client);
         if (r >= 0)
-                log_dhcp_client(client, "STARTED on ifindex %u with address %s",
-                                client->index,
-                                ether_addr_to_string(&client->client_id.mac_addr, buffer));
+                log_dhcp_client(client, "STARTED on ifindex %u", client->index);
 
         return r;
 }