chiark / gitweb /
libsystemd-dhcp: expose received DNS server
[elogind.git] / src / libsystemd-dhcp / dhcp-client.c
index 102353894c08089e2675a1a85ba9c22237f95343..dbec869816532fc93ebef5e1f199bf0284ae7610 100644 (file)
@@ -41,6 +41,7 @@ struct DHCPLease {
         be32_t server_address;
         be32_t subnet_mask;
         be32_t router;
+        be32_t dns;
 };
 
 typedef struct DHCPLease DHCPLease;
@@ -54,6 +55,7 @@ struct sd_dhcp_client {
         union sockaddr_union link;
         sd_event_source *receive_message;
         uint8_t *req_opts;
+        size_t req_opts_allocated;
         size_t req_opts_size;
         be32_t last_addr;
         struct ether_addr mac_addr;
@@ -115,11 +117,11 @@ int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option)
                 if (client->req_opts[i] == option)
                         return -EEXIST;
 
-        if (!GREEDY_REALLOC(client->req_opts, client->req_opts_size,
+        if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
                             client->req_opts_size + 1))
                 return -ENOMEM;
 
-        client->req_opts[client->req_opts_size - 1] = option;
+        client->req_opts[client->req_opts_size++] = option;
 
         return 0;
 }
@@ -208,6 +210,33 @@ int sd_dhcp_client_get_netmask(sd_dhcp_client *client, struct in_addr *addr)
         return 0;
 }
 
+int sd_dhcp_client_get_dns(sd_dhcp_client *client, struct in_addr *addr)
+{
+        assert_return(client, -EINVAL);
+        assert_return(addr, -EINVAL);
+
+        switch (client->state) {
+        case DHCP_STATE_INIT:
+        case DHCP_STATE_SELECTING:
+        case DHCP_STATE_INIT_REBOOT:
+        case DHCP_STATE_REBOOTING:
+        case DHCP_STATE_REQUESTING:
+                return -EADDRNOTAVAIL;
+
+        case DHCP_STATE_BOUND:
+        case DHCP_STATE_RENEWING:
+        case DHCP_STATE_REBINDING:
+                if (client->lease->dns)
+                        addr->s_addr = client->lease->dns;
+                else
+                        return -ENOENT;
+
+                break;
+        }
+
+        return 0;
+}
+
 int sd_dhcp_client_prefixlen(const struct in_addr *addr)
 {
         int len = 0;
@@ -259,8 +288,6 @@ static int client_notify(sd_dhcp_client *client, int event)
 static int client_stop(sd_dhcp_client *client, int error)
 {
         assert_return(client, -EINVAL);
-        assert_return(client->state != DHCP_STATE_INIT &&
-                      client->state != DHCP_STATE_INIT_REBOOT, -EALREADY);
 
         client->receive_message =
                 sd_event_source_unref(client->receive_message);
@@ -328,7 +355,8 @@ static int client_packet_init(sd_dhcp_client *client, uint8_t type,
            refuse to issue an DHCP lease if 'secs' is set to zero */
         message->secs = htobe16(secs);
 
-        if (client->state == DHCP_STATE_RENEWING)
+        if (client->state == DHCP_STATE_RENEWING ||
+            client->state == DHCP_STATE_REBINDING)
                 message->ciaddr = client->lease->address;
 
         memcpy(&message->chaddr, &client->mac_addr, ETH_ALEN);
@@ -532,13 +560,21 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
 
                 break;
 
+        case DHCP_STATE_REBINDING:
+
+                time_left = (client->lease->lifetime - client->lease->t2)/2;
+                if (time_left < 60)
+                        time_left = 60;
+
+                next_timeout = usec + time_left * USEC_PER_SEC;
+                break;
+
         case DHCP_STATE_INIT:
         case DHCP_STATE_INIT_REBOOT:
         case DHCP_STATE_REBOOTING:
         case DHCP_STATE_SELECTING:
         case DHCP_STATE_REQUESTING:
         case DHCP_STATE_BOUND:
-        case DHCP_STATE_REBINDING:
 
                 if (client->attempt < 64)
                         client->attempt *= 2;
@@ -548,7 +584,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
                 break;
         }
 
-        next_timeout += (random_u() & 0x1fffff);
+        next_timeout += (random_u32() & 0x1fffff);
 
         err = sd_event_add_monotonic(client->event, next_timeout,
                                      10 * USEC_PER_MSEC,
@@ -581,6 +617,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
 
         case DHCP_STATE_REQUESTING:
         case DHCP_STATE_RENEWING:
+        case DHCP_STATE_REBINDING:
                 err = client_send_request(client, secs);
                 if (err < 0 && client->attempt >= 64)
                          goto error;
@@ -592,7 +629,6 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
         case DHCP_STATE_INIT_REBOOT:
         case DHCP_STATE_REBOOTING:
         case DHCP_STATE_BOUND:
-        case DHCP_STATE_REBINDING:
 
                 break;
         }
@@ -607,6 +643,28 @@ error:
         return 0;
 }
 
+static int client_initialize_events(sd_dhcp_client *client, usec_t usec)
+{
+        int r;
+
+        r = sd_event_add_io(client->event, client->fd, EPOLLIN,
+                            client_receive_message, client,
+                            &client->receive_message);
+        if (r < 0)
+                goto error;
+
+        r = sd_event_add_monotonic(client->event, usec, 0,
+                                   client_timeout_resend, client,
+                                   &client->timeout_resend);
+
+error:
+        if (r < 0)
+                client_stop(client, r);
+
+        return 0;
+
+}
+
 static int client_timeout_expire(sd_event_source *s, uint64_t usec,
                                  void *userdata)
 {
@@ -619,7 +677,28 @@ static int client_timeout_expire(sd_event_source *s, uint64_t usec,
 
 static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
 {
-        return 0;
+        sd_dhcp_client *client = userdata;
+        int r;
+
+        if (client->fd >= 0) {
+                client->receive_message =
+                        sd_event_source_unref(client->receive_message);
+                close(client->fd);
+                client->fd = -1;
+        }
+
+        client->state = DHCP_STATE_REBINDING;
+        client->attempt = 1;
+
+        r = dhcp_network_bind_raw_socket(client->index, &client->link);
+        if (r < 0) {
+                client_stop(client, r);
+                return 0;
+        }
+
+        client->fd = r;
+
+        return client_initialize_events(client, usec);
 }
 
 static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata)
@@ -632,28 +711,14 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata)
 
         r = dhcp_network_bind_udp_socket(client->index,
                                          client->lease->address);
-        if (r < 0)
-                goto error;
+        if (r < 0) {
+                client_stop(client, r);
+                return 0;
+        }
 
         client->fd = r;
-        r = sd_event_add_io(client->event, client->fd, EPOLLIN,
-                            client_receive_message, client,
-                            &client->receive_message);
-        if (r < 0)
-                goto error;
 
-        r = sd_event_add_monotonic(client->event, usec, 0,
-                                   client_timeout_resend, client,
-                                   &client->timeout_resend);
-        if (r < 0)
-                goto error;
-
-        return 0;
-
-error:
-        client_stop(client, r);
-
-        return 0;
+        return client_initialize_events(client, usec);
 }
 
 static int client_parse_offer(uint8_t code, uint8_t len, const uint8_t *option,
@@ -690,6 +755,12 @@ static int client_parse_offer(uint8_t code, uint8_t len, const uint8_t *option,
 
                 break;
 
+        case DHCP_OPTION_DOMAIN_NAME_SERVER:
+                if (len >= 4)
+                        memcpy(&lease->dns, option, 4);
+
+                break;
+
         case DHCP_OPTION_RENEWAL_T1_TIME:
                 if (len == 4) {
                         memcpy(&val, option, 4);
@@ -855,7 +926,7 @@ static uint64_t client_compute_timeout(uint64_t request_sent,
                                        uint32_t lifetime)
 {
         return request_sent + (lifetime - 3) * USEC_PER_SEC +
-                + (random_u() & 0x1fffff);
+                + (random_u32() & 0x1fffff);
 }
 
 static int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec)
@@ -964,6 +1035,7 @@ static int client_receive_message(sd_event_source *s, int fd,
 
         case DHCP_STATE_REQUESTING:
         case DHCP_STATE_RENEWING:
+        case DHCP_STATE_REBINDING:
 
                 r = client_receive_ack(client, buf, len);
 
@@ -1005,7 +1077,6 @@ static int client_receive_message(sd_event_source *s, int fd,
         case DHCP_STATE_INIT_REBOOT:
         case DHCP_STATE_REBOOTING:
         case DHCP_STATE_BOUND:
-        case DHCP_STATE_REBINDING:
 
                 break;
         }
@@ -1019,42 +1090,26 @@ error:
 
 int sd_dhcp_client_start(sd_dhcp_client *client)
 {
-        int err;
+        int r;
 
         assert_return(client, -EINVAL);
-        assert_return(client->index >= 0, -EINVAL);
+        assert_return(client->index > 0, -EINVAL);
         assert_return(client->state == DHCP_STATE_INIT ||
                       client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
 
-        client->xid = random_u();
+        client->xid = random_u32();
 
-        client->fd = dhcp_network_bind_raw_socket(client->index,
-                                                  &client->link);
+        r = dhcp_network_bind_raw_socket(client->index, &client->link);
 
-        if (client->fd < 0) {
-                err = client->fd;
-                goto error;
+        if (r < 0) {
+                client_stop(client, r);
+                return r;
         }
 
-        err = sd_event_add_io(client->event, client->fd, EPOLLIN,
-                              client_receive_message, client,
-                              &client->receive_message);
-        if (err < 0)
-                goto error;
-
+        client->fd = r;
         client->start_time = now(CLOCK_MONOTONIC);
-        err = sd_event_add_monotonic(client->event, client->start_time, 0,
-                                     client_timeout_resend, client,
-                                     &client->timeout_resend);
-        if (err < 0)
-                goto error;
 
-        return 0;
-
-error:
-        client_stop(client, err);
-
-        return err;
+        return client_initialize_events(client, client->start_time);
 }
 
 int sd_dhcp_client_stop(sd_dhcp_client *client)