summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Gundersen <teg@jklm.no>2014-03-19 16:05:44 +0100
committerTom Gundersen <teg@jklm.no>2014-03-21 17:52:42 +0100
commit9e64dd72765ddc2583554ebed24eb2c824564838 (patch)
tree85f7e9394a093269adfa6ca917b203221b38bba8
parent022446adf99b84c59a88c2e614033ccde13c395c (diff)
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.
-rw-r--r--src/libsystemd-network/dhcp-lease-internal.h2
-rw-r--r--src/libsystemd-network/sd-dhcp-client.c54
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c26
3 files changed, 69 insertions, 13 deletions
diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h
index 70f1aa15e4..e478902e0e 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 aff4c21e95..59cd30c606 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 e6d80d4c66..159bb502c8 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;
+}