From 9e64dd72765ddc2583554ebed24eb2c824564838 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Wed, 19 Mar 2014 16:05:44 +0100 Subject: [PATCH] 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. --- src/libsystemd-network/dhcp-lease-internal.h | 2 + src/libsystemd-network/sd-dhcp-client.c | 54 +++++++++++++++----- src/libsystemd-network/sd-dhcp-lease.c | 26 ++++++++++ 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 70f1aa15e..e478902e0 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 aff4c21e9..59cd30c60 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 e6d80d4c6..159bb502c 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; +} -- 2.30.2