[PATCH 1/8] dhcp-server: Add "authoritative" mode
by Andrew Zaborowski
An authoritative DHCP server basically assumes that there are no other
DHCP servers on the network and replies to DHCPREQUEST messages
requesting IPs from outside of its subnet or with a wrong server ID
value. This speeds up connections from clients who try to use IP
leases they have cached from other networks and who start their IP
configuration by trying DHCPREQUESTs for those leases.
The term is not defined in the DHCP standard but is used by various
implementations. Default to authoritative being true. When set to
false, handle a DHCPREQUEST message directed at other servers by taking
it as meaning the client is declining our own lease offer as mandated
by RFC 2131 on page 16.
Refactor the checks in DHCPREQUEST handling quoting parts of the RFC.
The new checks are stricter and I'm not preserving some of the checks
that didn't seem justified, for example in the
"if (server_id_opt || !lease) ... send_nak()" block it didn't seem to
make sense to only reply to clients who have no lease (offered or
active) and ignore those who do have a lease with a different IP
address, the opposite would make more sense.
---
ell/dhcp-server.c | 109 ++++++++++++++++++++++++++++++++++++++++------
ell/dhcp.h | 2 +
ell/ell.sym | 1 +
3 files changed, 98 insertions(+), 14 deletions(-)
diff --git a/ell/dhcp-server.c b/ell/dhcp-server.c
index bae0f10..6415872 100644
--- a/ell/dhcp-server.c
+++ b/ell/dhcp-server.c
@@ -87,6 +87,8 @@ struct l_dhcp_server {
struct dhcp_transport *transport;
struct l_acd *acd;
+
+ bool authoritative : 1;
};
#define MAC "%02x:%02x:%02x:%02x:%02x:%02x"
@@ -615,9 +617,6 @@ static void listener_event(const void *data, size_t len, void *user_data)
if (l == 4)
server_id_opt = l_get_u32(v);
- if (server->address != server_id_opt)
- return;
-
break;
case L_DHCP_OPTION_REQUESTED_IP_ADDRESS:
if (l == 4)
@@ -640,6 +639,9 @@ static void listener_event(const void *data, size_t len, void *user_data)
SERVER_DEBUG("Received DISCOVER, requested IP "NIPQUAD_FMT,
NIPQUAD(requested_ip_opt));
+ if (server_id_opt && server_id_opt != server->address)
+ break;
+
send_offer(server, message, lease, requested_ip_opt);
break;
@@ -647,27 +649,92 @@ static void listener_event(const void *data, size_t len, void *user_data)
SERVER_DEBUG("Received REQUEST, requested IP "NIPQUAD_FMT,
NIPQUAD(requested_ip_opt));
- if (requested_ip_opt == 0) {
- requested_ip_opt = message->ciaddr;
- if (requested_ip_opt == 0)
+ /*
+ * RFC2131 Section 3.5: "Those servers not selected by the
+ * DHCPREQUEST message use the message as notification that
+ * the client has declined that server's offer."
+ */
+ if (server_id_opt && server_id_opt != server->address) {
+ if (server->authoritative) {
+ send_nak(server, message);
break;
- }
+ }
- if (lease && requested_ip_opt == lease->address) {
- send_ack(server, message, lease->address);
+ if (!lease || !lease->offering)
+ break;
+
+ remove_lease(server, lease);
break;
}
- if (server_id_opt || !lease) {
- send_nak(server, message);
+ /*
+ * RFC2131 Section 3.5: "If the selected server is unable to
+ * satisfy the DHCPREQUEST message (...), the server SHOULD
+ * respond with a DHCPNAK message."
+ *
+ * But:
+ * 4.3.2: "If the DHCP server has no record of this client,
+ * then it MUST remain silent (...)"
+ */
+ if (!lease) {
+ if (server_id_opt == server->address ||
+ server->authoritative)
+ send_nak(server, message);
+
break;
}
+
+ /*
+ * 4.3.2: "If the DHCPREQUEST message contains a 'server
+ * identifier' option, the message is in response to a
+ * DHCPOFFER message. Otherwise, the message is a request
+ * to verify or extend an existing lease."
+ */
+ if (server_id_opt == server->address) {
+ /*
+ * Allow either no 'requested IP address' option or
+ * a value identical with the one we offered because
+ * the spec is unclear on whether it is to be
+ * included:
+ *
+ * Section 4.3.2: "DHCPREQUEST generated during
+ * SELECTING state: (...) 'requested IP address' MUST
+ * be filled in with the yiaddr value from the chosen
+ * DHCPOFFER."
+ *
+ * but section 3.5 suggests only in the INIT-REBOOT
+ * state: "The 'requested IP address' option is to be
+ * filled in only in a DHCPREQUEST message when the
+ * client is verifying network parameters obtained
+ * previously."
+ */
+ if (!lease->offering ||
+ (requested_ip_opt &&
+ requested_ip_opt != lease->address)) {
+ send_nak(server, message);
+ break;
+ }
+ } else {
+ /*
+ * 3.5: "If a server receives a DHCPREQUEST message
+ * with an invalid 'requested IP address', the server
+ * SHOULD respond to the client with a DHCPNAK message"
+ */
+ if (lease->offering ||
+ (requested_ip_opt &&
+ requested_ip_opt != lease->address)) {
+ send_nak(server, message);
+ break;
+ }
+ }
+
+ send_ack(server, message, lease->address);
break;
case DHCP_MESSAGE_TYPE_DECLINE:
SERVER_DEBUG("Received DECLINE");
- if (!server_id_opt || !requested_ip_opt || !lease ||
- !lease->offering)
+ if (server_id_opt != server->address || !requested_ip_opt ||
+ !lease || !lease->offering)
break;
if (requested_ip_opt == lease->address)
@@ -677,7 +744,8 @@ static void listener_event(const void *data, size_t len, void *user_data)
case DHCP_MESSAGE_TYPE_RELEASE:
SERVER_DEBUG("Received RELEASE");
- if (!server_id_opt || !lease || lease->offering)
+ if (server_id_opt != server->address || !lease ||
+ lease->offering)
break;
if (message->ciaddr == lease->address)
@@ -687,6 +755,9 @@ static void listener_event(const void *data, size_t len, void *user_data)
case DHCP_MESSAGE_TYPE_INFORM:
SERVER_DEBUG("Received INFORM");
+ if (server_id_opt && server_id_opt != server->address)
+ break;
+
send_inform(server, message);
break;
}
@@ -732,6 +803,7 @@ LIB_EXPORT struct l_dhcp_server *l_dhcp_server_new(int ifindex)
server->expired_list = l_queue_new();
server->started = false;
+ server->authoritative = true;
server->lease_seconds = DEFAULT_DHCP_LEASE_SEC;
server->max_expired = MAX_EXPIRED_LEASES;
@@ -1053,6 +1125,15 @@ failed:
return false;
}
+LIB_EXPORT void l_dhcp_server_set_authoritative(struct l_dhcp_server *server,
+ bool authoritative)
+{
+ if (unlikely(!server))
+ return;
+
+ server->authoritative = authoritative;
+}
+
LIB_EXPORT struct l_dhcp_lease *l_dhcp_server_discover(
struct l_dhcp_server *server,
uint32_t requested_ip_opt,
diff --git a/ell/dhcp.h b/ell/dhcp.h
index 2804c78..830cbb1 100644
--- a/ell/dhcp.h
+++ b/ell/dhcp.h
@@ -141,6 +141,8 @@ bool l_dhcp_server_set_ip_address(struct l_dhcp_server *server,
bool l_dhcp_server_set_netmask(struct l_dhcp_server *server, const char *mask);
bool l_dhcp_server_set_gateway(struct l_dhcp_server *server, const char *ip);
bool l_dhcp_server_set_dns(struct l_dhcp_server *server, char **dns);
+void l_dhcp_server_set_authoritative(struct l_dhcp_server *server,
+ bool authoritative);
struct l_dhcp_lease *l_dhcp_server_discover(struct l_dhcp_server *server,
uint32_t requested_ip_opt,
diff --git a/ell/ell.sym b/ell/ell.sym
index 4d41448..3be10dc 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -259,6 +259,7 @@ global:
l_dhcp_server_set_netmask;
l_dhcp_server_set_gateway;
l_dhcp_server_set_dns;
+ l_dhcp_server_set_authoritative;
l_dhcp_server_discover;
l_dhcp_server_request;
l_dhcp_server_decline;
--
2.30.2
11 months, 1 week
[PATCH 1/2] dhcp-server: Add API for manipulating leases
by Andrew Zaborowski
Add methods for obtaining and releasing DHCP leases. There are four
methods that correspond to the DISCOVER, REQUEST, DECLINE and RELEASE
messages and two shortcut methods for releasing the leases. This API is
useful for implementing mechanisms like the optional P2P IP Allocation
in the 802.11 4-Way Handshake and FILS IP Address Allocation in 802.11
Associate frames (both reduces the number of roundtrips in connection
setup). Since there are multiple such mechanisms I decided to include
discover and request as separate methods to allow more flexibility.
---
ell/dhcp-server.c | 106 +++++++++++++++++++++++++++++++++++++++++++++-
ell/dhcp.h | 15 +++++++
ell/ell.sym | 6 +++
3 files changed, 125 insertions(+), 2 deletions(-)
diff --git a/ell/dhcp-server.c b/ell/dhcp-server.c
index ecdd612..fa5a7db 100644
--- a/ell/dhcp-server.c
+++ b/ell/dhcp-server.c
@@ -126,12 +126,14 @@ static struct l_dhcp_lease *find_lease_by_mac(struct l_dhcp_server *server,
return l_queue_find(server->lease_list, match_lease_mac, mac);
}
-static void remove_lease(struct l_dhcp_server *server,
+static bool remove_lease(struct l_dhcp_server *server,
struct l_dhcp_lease *lease)
{
- l_queue_remove(server->lease_list, lease);
+ if (!l_queue_remove(server->lease_list, lease))
+ return false;
_dhcp_lease_free(lease);
+ return true;
}
/* Clear the old lease and create the new one */
@@ -1042,3 +1044,103 @@ failed:
l_free(dns_list);
return false;
}
+
+LIB_EXPORT struct l_dhcp_lease *l_dhcp_server_discover(
+ struct l_dhcp_server *server,
+ uint32_t requested_ip_opt,
+ const uint8_t *mac)
+{
+ struct l_dhcp_lease *lease;
+
+ SERVER_DEBUG("Requested IP " NIPQUAD_FMT " for " MAC,
+ NIPQUAD(requested_ip_opt), MAC_STR(mac));
+
+ if ((lease = find_lease_by_mac(server, mac)))
+ requested_ip_opt = lease->address;
+ else if (!check_requested_ip(server, requested_ip_opt)) {
+ requested_ip_opt = find_free_or_expired_ip(server, mac);
+
+ if (unlikely(!requested_ip_opt)) {
+ SERVER_DEBUG("Could not find any free addresses");
+ return NULL;
+ }
+ }
+
+ lease = add_lease(server, true, mac, requested_ip_opt);
+ if (unlikely(!lease)) {
+ SERVER_DEBUG("add_lease() failed");
+ return NULL;
+ }
+
+ SERVER_DEBUG("Offering " NIPQUAD_FMT " to " MAC,
+ NIPQUAD(requested_ip_opt), MAC_STR(mac));
+ return lease;
+}
+
+LIB_EXPORT bool l_dhcp_server_request(struct l_dhcp_server *server,
+ struct l_dhcp_lease *lease)
+{
+ if (unlikely(!lease))
+ return false;
+
+ SERVER_DEBUG("Requested IP " NIPQUAD_FMT " for " MAC,
+ NIPQUAD(lease->address), MAC_STR(lease->mac));
+
+ lease = add_lease(server, false, lease->mac, lease->address);
+
+ if (server->event_handler)
+ server->event_handler(server, L_DHCP_SERVER_EVENT_NEW_LEASE,
+ server->user_data, lease);
+
+ return true;
+}
+
+LIB_EXPORT bool l_dhcp_server_decline(struct l_dhcp_server *server,
+ struct l_dhcp_lease *lease)
+{
+ if (unlikely(!lease || !lease->offering))
+ return false;
+
+ SERVER_DEBUG("Declined IP " NIPQUAD_FMT " for " MAC,
+ NIPQUAD(lease->address), MAC_STR(lease->mac));
+
+ return remove_lease(server, lease);
+}
+
+LIB_EXPORT bool l_dhcp_server_release(struct l_dhcp_server *server,
+ struct l_dhcp_lease *lease)
+{
+ if (unlikely(!lease || lease->offering))
+ return false;
+
+ SERVER_DEBUG("Released IP " NIPQUAD_FMT " for " MAC,
+ NIPQUAD(lease->address), MAC_STR(lease->mac));
+
+ lease_release(server, lease);
+ return true;
+}
+
+/* Drop an offered, active or expired lease without moving it to expired_list */
+LIB_EXPORT bool l_dhcp_server_lease_remove(struct l_dhcp_server *server,
+ struct l_dhcp_lease *lease)
+{
+ if (unlikely(!lease))
+ return false;
+
+ if (unlikely(!l_queue_remove(server->lease_list, lease) &&
+ !l_queue_remove(server->expired_list, lease)))
+ return false;
+
+ _dhcp_lease_free(lease);
+ set_next_expire_timer(server, NULL);
+ return true;
+}
+
+LIB_EXPORT void l_dhcp_server_expire_by_mac(struct l_dhcp_server *server,
+ const uint8_t *mac)
+{
+ struct l_dhcp_lease *lease = find_lease_by_mac(server, mac);
+
+ if (likely(lease))
+ lease_release(server, lease);
+}
diff --git a/ell/dhcp.h b/ell/dhcp.h
index 2af75cc..2804c78 100644
--- a/ell/dhcp.h
+++ b/ell/dhcp.h
@@ -141,6 +141,21 @@ bool l_dhcp_server_set_ip_address(struct l_dhcp_server *server,
bool l_dhcp_server_set_netmask(struct l_dhcp_server *server, const char *mask);
bool l_dhcp_server_set_gateway(struct l_dhcp_server *server, const char *ip);
bool l_dhcp_server_set_dns(struct l_dhcp_server *server, char **dns);
+
+struct l_dhcp_lease *l_dhcp_server_discover(struct l_dhcp_server *server,
+ uint32_t requested_ip_opt,
+ const uint8_t *mac);
+bool l_dhcp_server_request(struct l_dhcp_server *server,
+ struct l_dhcp_lease *lease);
+bool l_dhcp_server_decline(struct l_dhcp_server *server,
+ struct l_dhcp_lease *lease);
+bool l_dhcp_server_release(struct l_dhcp_server *server,
+ struct l_dhcp_lease *lease);
+
+bool l_dhcp_server_lease_remove(struct l_dhcp_server *server,
+ struct l_dhcp_lease *lease);
+void l_dhcp_server_expire_by_mac(struct l_dhcp_server *server,
+ const uint8_t *mac);
#ifdef __cplusplus
}
#endif
diff --git a/ell/ell.sym b/ell/ell.sym
index e5eeefb..4d41448 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -259,6 +259,12 @@ global:
l_dhcp_server_set_netmask;
l_dhcp_server_set_gateway;
l_dhcp_server_set_dns;
+ l_dhcp_server_discover;
+ l_dhcp_server_request;
+ l_dhcp_server_decline;
+ l_dhcp_server_release;
+ l_dhcp_server_lease_remove;
+ l_dhcp_server_lease_expire_by_mac;
/* dhcp6 */
l_dhcp6_client_new;
l_dhcp6_client_destroy;
--
2.30.2
11 months, 2 weeks
[PATCH 1/3] dhcp-server: Fix debug messages in send_offer
by Andrew Zaborowski
find_free_or_expired_ip() or check_requested_ip() should fail if no
addresses are left in the pool, rather than lease_add() so emit that
error message there. The only thing lease_add() validates that has not
been validated before might be the origin MAC, treat the lease_add()
failure more as an internal error.
---
ell/dhcp-server.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ell/dhcp-server.c b/ell/dhcp-server.c
index 34512ae..b761833 100644
--- a/ell/dhcp-server.c
+++ b/ell/dhcp-server.c
@@ -451,13 +451,13 @@ static void send_offer(struct l_dhcp_server *server,
client_msg->chaddr);
if (!reply->yiaddr) {
- SERVER_DEBUG("Could not find lease or send offer");
+ SERVER_DEBUG("No free IP addresses, OFFER abandoned");
return;
}
lease = add_lease(server, true, client_msg->chaddr, reply->yiaddr);
if (!lease) {
- SERVER_DEBUG("No free IP addresses, OFFER abandoned");
+ SERVER_DEBUG("add_lease() failed");
return;
}
--
2.30.2
11 months, 4 weeks
[PATCH 1/3] icmp6: Add l_icmp6_router_get_address
by Andrew Zaborowski
Add a getter for the router's address and make a small formatting change
while there.
---
ell/ell.sym | 1 +
ell/icmp6.c | 15 ++++++++++++++-
ell/icmp6.h | 1 +
3 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/ell/ell.sym b/ell/ell.sym
index 1fbd98c..d7027e3 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -657,6 +657,7 @@ global:
l_icmp6_client_set_nodelay;
l_icmp6_client_set_rtnl;
l_icmp6_client_set_route_priority;
+ l_icmp6_router_get_address;
l_icmp6_router_get_managed;
l_icmp6_router_get_other;
/* acd */
diff --git a/ell/icmp6.c b/ell/icmp6.c
index fe8a506..ebdcabd 100644
--- a/ell/icmp6.c
+++ b/ell/icmp6.c
@@ -728,12 +728,25 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra,
}
opts += l;
- opts_len -= l ;
+ opts_len -= l;
}
return r;
}
+LIB_EXPORT char *l_icmp6_router_get_address(const struct l_icmp6_router *r)
+{
+ char buf[INET6_ADDRSTRLEN];
+
+ if (unlikely(!r))
+ return NULL;
+
+ if (!inet_ntop(AF_INET6, r->address, buf, sizeof(buf)))
+ return NULL;
+
+ return l_strdup(buf);
+}
+
LIB_EXPORT bool l_icmp6_router_get_managed(const struct l_icmp6_router *r)
{
if (unlikely(!r))
diff --git a/ell/icmp6.h b/ell/icmp6.h
index a6b1265..b2231c9 100644
--- a/ell/icmp6.h
+++ b/ell/icmp6.h
@@ -66,6 +66,7 @@ bool l_icmp6_client_set_rtnl(struct l_icmp6_client *client,
bool l_icmp6_client_set_route_priority(struct l_icmp6_client *client,
uint32_t priority);
+char *l_icmp6_router_get_address(const struct l_icmp6_router *r);
bool l_icmp6_router_get_managed(const struct l_icmp6_router *r);
bool l_icmp6_router_get_other(const struct l_icmp6_router *r);
--
2.30.2
1 year
[PATCH] Add logging if bind function fails
by Michael Johnson
The bind function returns any errors it encounters but they were
silently being discarded in this method. This at least logs this if
debug messages are enabled.
In the future it would be nice to have this log even if the debug output
isn't enabled as DHCP will not be working correctly.
---
ell/dhcp.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/ell/dhcp.c b/ell/dhcp.c
index bd346cc..115325e 100644
--- a/ell/dhcp.c
+++ b/ell/dhcp.c
@@ -782,7 +782,7 @@ static void dhcp_client_rx_message(const void *data, size_t len, void *userdata)
uint8_t msg_type = 0;
uint8_t t, l;
const void *v;
- int r;
+ int r, e;
struct in_addr ia;
CLIENT_DEBUG("");
@@ -855,9 +855,14 @@ static void dhcp_client_rx_message(const void *data, size_t len, void *userdata)
l_timeout_remove(client->timeout_resend);
client->timeout_resend = NULL;
- if (client->transport->bind)
- client->transport->bind(client->transport,
+ if (client->transport->bind) {
+ e = client->transport->bind(client->transport,
client->lease->address);
+ if (e < 0) {
+ CLIENT_DEBUG("Failed to bind dhcp socket. "
+ "Error %d: %s", e, strerror(-e));
+ }
+ }
dhcp_client_event_notify(client, r);
--
2.25.1
1 year