[systemd-commits] 3 commits - src/libsystemd-network src/network

Tom Gundersen tomegun at kemper.freedesktop.org
Fri Mar 21 09:52:46 PDT 2014


 src/libsystemd-network/dhcp-lease-internal.h |    2 
 src/libsystemd-network/sd-dhcp-client.c      |  211 +++++++++++++++++----------
 src/libsystemd-network/sd-dhcp-lease.c       |   26 +++
 src/network/networkd-link.c                  |   25 +++
 4 files changed, 192 insertions(+), 72 deletions(-)

New commits:
commit 9765ce69e15fd294db9553777b621515fa57f2c6
Author: Brandon Philips <brandon at ifup.co>
Date:   Thu Mar 20 11:28:12 2014 -0700

    network: dhcp: create explicit host route to gateway
    
    Some DHCP servers gives you a netmask of 255.255.255.255 so the gateway is not
    routable. Other DHCP client implementations look through the existing routes to
    figure out if they should add an explicit host route. See below for a link.
    
    However, it makes sense to just create the route explicitly whether it is
    needed or not since it is explicit, makes the dhcp route entries independent of
    other entries and saves us from knowing the state of the kernel tables.
    
    After patch route table on a machine with a network (common case):
    
    default via 10.0.2.2 dev ens3
    10.0.2.0/24 dev ens3  proto kernel  scope link  src 10.0.2.15
    10.0.2.2 dev ens3  scope link
    
    After patch route table on a machine without a network (this case):
    
    default via 10.240.0.1 dev ens4v1
    10.240.0.1 dev ens4v1  scope link
    
    The code from dhcpcd that works around this issue is on line 637.
    https://android.googlesource.com/platform/external/dhcpcd/+/master/configure.c

diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index a7ba466..14cc871 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -228,6 +228,7 @@ static int link_enter_set_routes(Link *link) {
 
         if (link->dhcp_lease) {
                 _cleanup_route_free_ Route *route = NULL;
+                _cleanup_route_free_ Route *route_gw = NULL;
                 struct in_addr gateway;
 
                 r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
@@ -244,6 +245,30 @@ static int link_enter_set_routes(Link *link) {
                         return r;
                 }
 
+                r = route_new_dynamic(&route_gw);
+                if (r < 0) {
+                        log_error_link(link, "Could not allocate route: %s",
+                                       strerror(-r));
+                        return r;
+                }
+
+                /* The dhcp netmask may mask out the gateway. Add an explicit
+                 * route for the gw host so that we can route no matter the
+                 * netmask or existing kernel route tables. */
+                route_gw->family = AF_INET;
+                route_gw->dst_addr.in = gateway;
+                route_gw->dst_prefixlen = 32;
+                route_gw->scope = RT_SCOPE_LINK;
+
+                r = route_configure(route_gw, link, &route_handler);
+                if (r < 0) {
+                        log_warning_link(link,
+                                         "could not set host route: %s", strerror(-r));
+                        return r;
+                }
+
+                link->route_messages ++;
+
                 route->family = AF_INET;
                 route->in_addr.in = gateway;
 

commit 9e64dd72765ddc2583554ebed24eb2c824564838
Author: Tom Gundersen <teg at jklm.no>
Date:   Wed Mar 19 16:05:44 2014 +0100

    sd-dhcp-client: add fallback subnet masks
    
    The DHCP RFC does not require the DHCP server to send a subnet mask, so if it
    is missing, let's try to use the default subnet masks based on address class.
    In case the class the address belongs to does not have a default subnet mask,
    we fail as before.
    
    Also improve logging when handling invalid dhcp messages, and simply ignore them
    rather than stop the whole dhcp client.

diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h
index 70f1aa1..e478902 100644
--- a/src/libsystemd-network/dhcp-lease-internal.h
+++ b/src/libsystemd-network/dhcp-lease-internal.h
@@ -57,5 +57,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
 int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file);
 int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret);
 
+int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease);
+
 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_lease*, sd_dhcp_lease_unref);
 #define _cleanup_dhcp_lease_unref_ _cleanup_(sd_dhcp_lease_unrefp)
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index aff4c21..59cd30c 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -673,8 +673,10 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
                 return r;
 
         r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
-        if (r != DHCP_OFFER)
+        if (r != DHCP_OFFER) {
+                log_dhcp_client(client, "receieved message was not an OFFER, ignoring");
                 return -ENOMSG;
+        }
 
         lease->next_server = offer->siaddr;
 
@@ -682,9 +684,21 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
 
         if (lease->address == INADDR_ANY ||
             lease->server_address == INADDR_ANY ||
-            lease->subnet_mask == INADDR_ANY ||
-            lease->lifetime == 0)
+            lease->lifetime == 0) {
+                log_dhcp_client(client, "receieved lease lacks address, server "
+                                "address or lease lifetime, ignoring");
                 return -ENOMSG;
+        }
+
+        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 "
+                                        "mask, and a fallback one can not be "
+                                        "generated, ignoring");
+                        return -ENOMSG;
+                }
+        }
 
         client->lease = lease;
         lease = NULL;
@@ -709,8 +723,10 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
                 return DHCP_EVENT_NO_LEASE;
         }
 
-        if (r != DHCP_ACK)
+        if (r != DHCP_ACK) {
+                log_dhcp_client(client, "receieved message was not an ACK, ignoring");
                 return -ENOMSG;
+        }
 
         lease->next_server = ack->siaddr;
 
@@ -718,8 +734,21 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
 
         if (lease->address == INADDR_ANY ||
             lease->server_address == INADDR_ANY ||
-            lease->subnet_mask == INADDR_ANY || lease->lifetime == 0)
+            lease->lifetime == 0) {
+                log_dhcp_client(client, "receieved lease lacks address, server "
+                                "address or lease lifetime, ignoring");
                 return -ENOMSG;
+        }
+
+        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 "
+                                        "mask, and a fallback one can not be "
+                                        "generated, ignoring");
+                        return -ENOMSG;
+                }
+        }
 
         r = DHCP_EVENT_IP_ACQUIRE;
         if (client->lease) {
@@ -940,7 +969,9 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
                                                          client->event_priority);
                         if (r < 0)
                                 goto error;
-                }
+                } else if (r == -ENOMSG)
+                        /* invalid message, let's ignore it */
+                        return 0;
 
                 break;
 
@@ -950,7 +981,6 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
         case DHCP_STATE_REBINDING:
 
                 r = client_handle_ack(client, message, len);
-
                 if (r == DHCP_EVENT_NO_LEASE) {
 
                         client->timeout_resend =
@@ -967,9 +997,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
                         }
 
                         goto error;
-                }
-
-                if (r >= 0) {
+                } else if (r >= 0) {
                         client->timeout_resend =
                                 sd_event_source_unref(client->timeout_resend);
 
@@ -994,9 +1022,9 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
                         client->receive_message =
                                 sd_event_source_unref(client->receive_message);
                         client->fd = safe_close(client->fd);
-                }
-
-                r = 0;
+                } else if (r == -ENOMSG)
+                        /* invalid message, let's ignore it */
+                        return 0;
 
                 break;
 
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index e6d80d4..159bb50 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -481,3 +481,29 @@ int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret) {
 
         return 0;
 }
+
+int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
+        uint32_t address;
+
+        assert(lease);
+        assert(lease->address != INADDR_ANY);
+
+        address = be32toh(lease->address);
+
+        /* fall back to the default subnet masks based on address class */
+
+        if ((address >> 31) == 0x0)
+                /* class A, leading bits: 0 */
+                lease->subnet_mask = htobe32(0xff000000);
+        else if ((address >> 30) == 0x2)
+                /* class B, leading bits 10 */
+                lease->subnet_mask = htobe32(0xffff0000);
+        else if ((address >> 29) == 0x6)
+                /* class C, leading bits 110 */
+                lease->subnet_mask = htobe32(0xffffff00);
+        else
+                /* class D or E, no default mask. give up */
+                return -ERANGE;
+
+        return 0;
+}

commit 022446adf99b84c59a88c2e614033ccde13c395c
Author: Tom Gundersen <teg at jklm.no>
Date:   Wed Mar 19 17:19:22 2014 +0100

    sd-dhcp-client: make timeout handling a bit more robust
    
    Accept any lease lifetime greater than one second. Server should not
    hand out extremely short leases, but let's not be the ones to fail.
    
    Do not fail when arming a timer in the past, but also only arm one such
    timer.
    
    Avoid rounding errors when computing the default timeouts, this may be
    an issue if we are handed a very short lease.
    
    Also, don't pass 'time_now' around, as that can be found in the event
    object when needed.

diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index 6e35ef4..aff4c21 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -740,68 +740,111 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
         return r;
 }
 
-static uint64_t client_compute_timeout(uint64_t request_sent,
-                                       uint32_t lifetime) {
-        return request_sent + (lifetime - 3) * USEC_PER_SEC +
+static uint64_t client_compute_timeout(sd_dhcp_client *client,
+                                       uint32_t lifetime, double factor) {
+        assert(client);
+        assert(client->request_sent);
+        assert(lifetime);
+
+        return client->request_sent + ((lifetime - 3) * USEC_PER_SEC * factor) +
                 + (random_u32() & 0x1fffff);
 }
 
-static int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec) {
-        uint64_t next_timeout;
+static int client_set_lease_timeouts(sd_dhcp_client *client) {
+        usec_t time_now;
+        uint64_t lifetime_timeout;
+        uint64_t t2_timeout;
+        uint64_t t1_timeout;
+        char time_string[FORMAT_TIMESPAN_MAX];
         int r;
 
         assert(client);
         assert(client->event);
-
-        /* don't set timers for infinite leases */
-        if (client->lease->lifetime == 0xffffffff)
-                return 0;
-
-        if (client->lease->lifetime < 10)
-                return -EINVAL;
+        assert(client->lease);
+        assert(client->lease->lifetime);
 
         client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
         client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
         client->timeout_expire = sd_event_source_unref(client->timeout_expire);
 
-        if (!client->lease->t1)
-                client->lease->t1 = client->lease->lifetime / 2;
+        /* don't set timers for infinite leases */
+        if (client->lease->lifetime == 0xffffffff)
+                return 0;
 
-        next_timeout = client_compute_timeout(client->request_sent,
-                                              client->lease->t1);
-        if (next_timeout < usec)
-                return -EINVAL;
+        r = sd_event_get_now_monotonic(client->event, &time_now);
+        if (r < 0)
+                return r;
+        assert(client->request_sent <= time_now);
+
+        /* convert the various timeouts from relative (secs) to absolute (usecs) */
+        lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1);
+        if (client->lease->t1 && client->lease->t2) {
+                /* both T1 and T2 are given */
+                if (client->lease->t1 < client->lease->t2 &&
+                    client->lease->t2 < client->lease->lifetime) {
+                        /* they are both valid */
+                        t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
+                        t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
+                } else {
+                        /* discard both */
+                        t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
+                        client->lease->t2 = (client->lease->lifetime * 7) / 8;
+                        t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
+                        client->lease->t1 = client->lease->lifetime / 2;
+                }
+        } else if (client->lease->t2 && client->lease->t2 < client->lease->lifetime) {
+                /* only T2 is given, and it is valid */
+                t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
+                t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
+                client->lease->t1 = client->lease->lifetime / 2;
+                if (t2_timeout <= t1_timeout) {
+                        /* the computed T1 would be invalid, so discard T2 */
+                        t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
+                        client->lease->t2 = (client->lease->lifetime * 7) / 8;
+                }
+        } else if (client->lease->t1 && client->lease->t1 < client->lease->lifetime) {
+                /* only T1 is given, and it is valid */
+                t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
+                t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
+                client->lease->t2 = (client->lease->lifetime * 7) / 8;
+                if (t2_timeout <= t1_timeout) {
+                        /* the computed T2 would be invalid, so discard T1 */
+                        t2_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
+                        client->lease->t2 = client->lease->lifetime / 2;
+                }
+        } else {
+                /* fall back to the default timeouts */
+                t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
+                client->lease->t1 = client->lease->lifetime / 2;
+                t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
+                client->lease->t2 = (client->lease->lifetime * 7) / 8;
+        }
 
+        /* arm lifetime timeout */
         r = sd_event_add_monotonic(client->event,
-                                   &client->timeout_t1,
-                                   next_timeout,
+                                   &client->timeout_expire, lifetime_timeout,
                                    10 * USEC_PER_MSEC,
-                                   client_timeout_t1, client);
+                                   client_timeout_expire, client);
         if (r < 0)
                 return r;
 
-        r = sd_event_source_set_priority(client->timeout_t1,
+        r = sd_event_source_set_priority(client->timeout_expire,
                                          client->event_priority);
         if (r < 0)
                 return r;
 
-        if (!client->lease->t2)
-                client->lease->t2 = client->lease->lifetime * 7 / 8;
-
-        if (client->lease->t2 < client->lease->t1)
-                return -EINVAL;
+        log_dhcp_client(client, "lease expires in %s",
+                        format_timespan(time_string, FORMAT_TIMESPAN_MAX,
+                        lifetime_timeout - time_now, 0));
 
-        if (client->lease->lifetime < client->lease->t2)
-                return -EINVAL;
-
-        next_timeout = client_compute_timeout(client->request_sent,
-                                              client->lease->t2);
-        if (next_timeout < usec)
-                return -EINVAL;
+        /* don't arm earlier timeouts if this has already expired */
+        if (lifetime_timeout <= time_now)
+                return 0;
 
+        /* arm T2 timeout */
         r = sd_event_add_monotonic(client->event,
                                    &client->timeout_t2,
-                                   next_timeout,
+                                   t2_timeout,
                                    10 * USEC_PER_MSEC,
                                    client_timeout_t2, client);
         if (r < 0)
@@ -812,28 +855,37 @@ static int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec) {
         if (r < 0)
                 return r;
 
-        next_timeout = client_compute_timeout(client->request_sent,
-                                              client->lease->lifetime);
-        if (next_timeout < usec)
-                return -EINVAL;
+        log_dhcp_client(client, "T2 expires in %s",
+                        format_timespan(time_string, FORMAT_TIMESPAN_MAX,
+                        t2_timeout - time_now, 0));
+
+        /* don't arm earlier timeout if this has already expired */
+        if (t2_timeout <= time_now)
+                return 0;
 
+        /* arm T1 timeout */
         r = sd_event_add_monotonic(client->event,
-                                   &client->timeout_expire, next_timeout,
+                                   &client->timeout_t1,
+                                   t1_timeout,
                                    10 * USEC_PER_MSEC,
-                                   client_timeout_expire, client);
+                                   client_timeout_t1, client);
         if (r < 0)
                 return r;
 
-        r = sd_event_source_set_priority(client->timeout_expire,
+        r = sd_event_source_set_priority(client->timeout_t1,
                                          client->event_priority);
         if (r < 0)
                 return r;
 
+        log_dhcp_client(client, "T1 expires in %s",
+                        format_timespan(time_string, FORMAT_TIMESPAN_MAX,
+                        t1_timeout - time_now, 0));
+
         return 0;
 }
 
 static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
-                                 int len, usec_t time_now) {
+                                 int len) {
         int r = 0, notify_event = 0;
 
         assert(client);
@@ -932,7 +984,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
 
                         client->last_addr = client->lease->address;
 
-                        r = client_set_lease_timeouts(client, time_now);
+                        r = client_set_lease_timeouts(client);
                         if (r < 0)
                                 goto error;
 
@@ -967,11 +1019,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;
-        usec_t time_now;
 
         assert(s);
         assert(client);
-        assert(client->event);
 
         r = ioctl(fd, FIONREAD, &buflen);
         if (r < 0 || buflen <= 0)
@@ -985,19 +1035,13 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
         if (len < 0)
                 return 0;
 
-        r = sd_event_get_now_monotonic(client->event, &time_now);
-        if (r < 0)
-                return client_stop(client, r);
-
-        return client_handle_message(client, message, len,
-                                     time_now);
+        return client_handle_message(client, message, len);
 }
 
 static int client_receive_message_raw(sd_event_source *s, int fd,
                                       uint32_t revents, void *userdata) {
         sd_dhcp_client *client = userdata;
         _cleanup_free_ DHCPPacket *packet = NULL;
-        usec_t time_now;
         uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
         struct iovec iov = {};
         struct msghdr msg = {
@@ -1012,7 +1056,6 @@ static int client_receive_message_raw(sd_event_source *s, int fd,
 
         assert(s);
         assert(client);
-        assert(client->event);
 
         r = ioctl(fd, FIONREAD, &buflen);
         if (r < 0 || buflen <= 0)
@@ -1047,11 +1090,7 @@ static int client_receive_message_raw(sd_event_source *s, int fd,
 
         len -= DHCP_IP_UDP_SIZE;
 
-        r = sd_event_get_now_monotonic(client->event, &time_now);
-        if (r < 0)
-                return client_stop(client, r);
-
-        return client_handle_message(client, &packet->dhcp, len, time_now);
+        return client_handle_message(client, &packet->dhcp, len);
 }
 
 int sd_dhcp_client_start(sd_dhcp_client *client) {



More information about the systemd-commits mailing list