chiark / gitweb /
sd-dhcp-client: add fallback subnet masks
authorTom Gundersen <teg@jklm.no>
Wed, 19 Mar 2014 15:05:44 +0000 (16:05 +0100)
committerTom Gundersen <teg@jklm.no>
Fri, 21 Mar 2014 16:52:42 +0000 (17:52 +0100)
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.

src/libsystemd-network/dhcp-lease-internal.h
src/libsystemd-network/sd-dhcp-client.c
src/libsystemd-network/sd-dhcp-lease.c

index 70f1aa1..e478902 100644 (file)
@@ -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)
index aff4c21..59cd30c 100644 (file)
@@ -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;
 
index e6d80d4..159bb50 100644 (file)
@@ -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;
+}