Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package ell for openSUSE:Factory checked in at 2021-09-09 23:07:19 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ell (Old) and /work/SRC/openSUSE:Factory/.ell.new.1899 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ell" Thu Sep 9 23:07:19 2021 rev:22 rq:917264 version:0.43 Changes: -------- --- /work/SRC/openSUSE:Factory/ell/ell.changes 2021-08-24 10:54:22.972359885 +0200 +++ /work/SRC/openSUSE:Factory/.ell.new.1899/ell.changes 2021-09-09 23:07:22.904819645 +0200 @@ -1,0 +2,7 @@ +Tue Sep 7 10:11:09 UTC 2021 - Malte Ohmstede <malte.ohmst...@gmail.com> + +- Update to release 0.43 + * Add support for DHCP Rapid Commit feature. + * Add support for DHCP authorative mode feature. + +------------------------------------------------------------------- Old: ---- ell-0.42.tar.sign ell-0.42.tar.xz New: ---- ell-0.43.tar.sign ell-0.43.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ell.spec ++++++ --- /var/tmp/diff_new_pack.lVOCwt/_old 2021-09-09 23:07:23.360820175 +0200 +++ /var/tmp/diff_new_pack.lVOCwt/_new 2021-09-09 23:07:23.364820180 +0200 @@ -18,7 +18,7 @@ %define lname libell0 Name: ell -Version: 0.42 +Version: 0.43 Release: 0 Summary: Wireless setup and cryptography library License: LGPL-2.1-or-later ++++++ ell-0.42.tar.xz -> ell-0.43.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ell-0.42/ChangeLog new/ell-0.43/ChangeLog --- old/ell-0.42/ChangeLog 2021-08-01 21:49:37.000000000 +0200 +++ new/ell-0.43/ChangeLog 2021-08-22 06:08:02.000000000 +0200 @@ -1,3 +1,7 @@ +ver 0.43: + Add support for DHCP Rapid Commit feature. + Add support for DHCP authoritative mode feature. + ver 0.42: Add support for constant time security functions. Add support for manipulating DHCP leases. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ell-0.42/configure new/ell-0.43/configure --- old/ell-0.42/configure 2021-08-01 21:51:21.000000000 +0200 +++ new/ell-0.43/configure 2021-08-22 06:09:28.000000000 +0200 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for ell 0.42. +# Generated by GNU Autoconf 2.69 for ell 0.43. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -587,8 +587,8 @@ # Identity of this package. PACKAGE_NAME='ell' PACKAGE_TARNAME='ell' -PACKAGE_VERSION='0.42' -PACKAGE_STRING='ell 0.42' +PACKAGE_VERSION='0.43' +PACKAGE_STRING='ell 0.43' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1350,7 +1350,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures ell 0.42 to adapt to many kinds of systems. +\`configure' configures ell 0.43 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1421,7 +1421,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of ell 0.42:";; + short | recursive ) echo "Configuration of ell 0.43:";; esac cat <<\_ACEOF @@ -1548,7 +1548,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -ell configure 0.42 +ell configure 0.43 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1913,7 +1913,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by ell $as_me 0.42, which was +It was created by ell $as_me 0.43, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2776,7 +2776,7 @@ # Define the identity of the package. PACKAGE='ell' - VERSION='0.42' + VERSION='0.43' cat >>confdefs.h <<_ACEOF @@ -13646,7 +13646,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by ell $as_me 0.42, which was +This file was extended by ell $as_me 0.43, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -13712,7 +13712,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -ell config.status 0.42 +ell config.status 0.43 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ell-0.42/configure.ac new/ell-0.43/configure.ac --- old/ell-0.42/configure.ac 2021-08-01 21:49:37.000000000 +0200 +++ new/ell-0.43/configure.ac 2021-08-22 06:08:02.000000000 +0200 @@ -1,5 +1,5 @@ AC_PREREQ(2.60) -AC_INIT(ell, 0.42) +AC_INIT(ell, 0.43) AC_CONFIG_HEADERS(config.h) AC_CONFIG_AUX_DIR(build-aux) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ell-0.42/ell/dhcp-lease.c new/ell-0.43/ell/dhcp-lease.c --- old/ell-0.42/ell/dhcp-lease.c 2021-08-01 21:49:37.000000000 +0200 +++ new/ell-0.43/ell/dhcp-lease.c 2021-08-22 06:08:02.000000000 +0200 @@ -49,6 +49,7 @@ l_free(lease->dns); l_free(lease->domain_name); + l_free(lease->client_id); l_free(lease); } @@ -138,6 +139,14 @@ goto error; break; + case DHCP_OPTION_CLIENT_IDENTIFIER: + if (l < 1 || l > 253 || lease->client_id) + goto error; + + lease->client_id = l_malloc(l + 1); + lease->client_id[0] = l; + memcpy(lease->client_id + 1, v, l); + break; default: break; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ell-0.42/ell/dhcp-private.h new/ell-0.43/ell/dhcp-private.h --- old/ell-0.42/ell/dhcp-private.h 2021-06-11 20:09:50.000000000 +0200 +++ new/ell-0.43/ell/dhcp-private.h 2021-08-22 06:08:02.000000000 +0200 @@ -65,6 +65,10 @@ #define DHCP_OPTION_PARAMETER_REQUEST_LIST 55 /* Section 9.8 */ #define DHCP_OPTION_MAXIMUM_MESSAGE_SIZE 57 /* Section 9.10 */ #define DHCP_OPTION_CLIENT_IDENTIFIER 61 /* Section 9.14 */ +#define DHCP_OPTION_RAPID_COMMIT 80 /* RFC 4039 Section 4 */ + +/* RFC 2131, Figure 2 */ +#define DHCP_FLAG_BROADCAST (1 << 15) /* RFC 2131, Figure 1 */ struct dhcp_message { @@ -103,7 +107,8 @@ uint16_t _dhcp_checksum(const void *buf, size_t len); uint16_t _dhcp_checksumv(const struct iovec *iov, size_t iov_cnt); -typedef void (*dhcp_transport_rx_cb_t)(const void *, size_t, void *); +typedef void (*dhcp_transport_rx_cb_t)(const void *, size_t, void *, + const uint8_t *); struct dhcp_transport { int (*open)(struct dhcp_transport *s, uint32_t xid); @@ -164,6 +169,7 @@ char *domain_name; /* for server */ uint8_t mac[6]; + uint8_t *client_id; /* set for an offered lease, but not ACK'ed */ bool offering : 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ell-0.42/ell/dhcp-server.c new/ell-0.43/ell/dhcp-server.c --- old/ell-0.42/ell/dhcp-server.c 2021-08-01 21:49:37.000000000 +0200 +++ new/ell-0.43/ell/dhcp-server.c 2021-08-22 06:08:02.000000000 +0200 @@ -87,6 +87,9 @@ struct dhcp_transport *transport; struct l_acd *acd; + + bool authoritative : 1; + bool rapid_commit : 1; }; #define MAC "%02x:%02x:%02x:%02x:%02x:%02x" @@ -112,6 +115,15 @@ return !l_time_after(get_lease_expiry_time(lease), l_time_now()); } +static bool match_lease_client_id(const void *data, const void *user_data) +{ + const struct l_dhcp_lease *lease = data; + const uint8_t *client_id = user_data; + + return lease->client_id && + !memcmp(lease->client_id, client_id, client_id[0] + 1); +} + static bool match_lease_mac(const void *data, const void *user_data) { const struct l_dhcp_lease *lease = data; @@ -120,16 +132,55 @@ return !memcmp(lease->mac, mac, 6); } -static struct l_dhcp_lease *find_lease_by_mac(struct l_dhcp_server *server, +static bool match_lease_ip(const void *data, const void *user_data) +{ + const struct l_dhcp_lease *lease = data; + + return lease->address == L_PTR_TO_UINT(user_data); +} + +static struct l_dhcp_lease *find_lease_by_ip(struct l_queue *lease_list, + uint32_t nip) +{ + return l_queue_find(lease_list, match_lease_ip, L_UINT_TO_PTR(nip)); +} + +static struct l_dhcp_lease *find_lease_by_id(struct l_queue *lease_list, + const uint8_t *client_id, const uint8_t *mac) { - return l_queue_find(server->lease_list, match_lease_mac, mac); + if (client_id) + return l_queue_find(lease_list, match_lease_client_id, + client_id); + + return l_queue_find(lease_list, match_lease_mac, mac); +} + +static struct l_dhcp_lease *find_lease_by_id_and_ip(struct l_queue *lease_list, + const uint8_t *client_id, + const uint8_t *mac, + uint32_t ip) +{ + struct l_dhcp_lease *lease = find_lease_by_ip(lease_list, ip); + + if (!lease) + return NULL; + + if (client_id) { + if (!match_lease_client_id(lease, client_id)) + return NULL; + } else { + if (!match_lease_mac(lease, mac)) + return NULL; + } + + return lease; } /* Clear the old lease and create the new one */ static int get_lease(struct l_dhcp_server *server, uint32_t yiaddr, - const uint8_t *mac, - struct l_dhcp_lease **lease_out) + const uint8_t *client_id, const uint8_t *mac, + struct l_dhcp_lease **lease_out) { struct l_dhcp_lease *lease; @@ -148,13 +199,17 @@ if (l_memeqzero(mac, ETH_ALEN)) return -ENXIO; - lease = find_lease_by_mac(server, mac); - + lease = find_lease_by_ip(server->lease_list, yiaddr); if (lease) { l_queue_remove(server->lease_list, lease); - *lease_out = lease; + return 0; + } + lease = find_lease_by_ip(server->expired_list, yiaddr); + if (lease && lease->address == yiaddr) { + l_queue_remove(server->expired_list, lease); + *lease_out = lease; return 0; } @@ -243,16 +298,17 @@ } static struct l_dhcp_lease *add_lease(struct l_dhcp_server *server, - bool offering, const uint8_t *chaddr, - uint32_t yiaddr) + bool offering, const uint8_t *client_id, + const uint8_t *chaddr, uint32_t yiaddr) { struct l_dhcp_lease *lease = NULL; int ret; - ret = get_lease(server, yiaddr, chaddr, &lease); + ret = get_lease(server, yiaddr, client_id, chaddr, &lease); if (ret != 0) return NULL; + l_free(lease->client_id); memset(lease, 0, sizeof(*lease)); memcpy(lease->mac, chaddr, ETH_ALEN); @@ -260,6 +316,9 @@ lease->subnet_mask = server->netmask; lease->router = server->gateway; + if (client_id) + lease->client_id = l_memdup(client_id, client_id[0] + 1); + lease->offering = offering; lease->bound_time = l_time_now(); @@ -312,19 +371,6 @@ set_next_expire_timer(server, lease); } -static bool match_lease_ip(const void *data, const void *user_data) -{ - const struct l_dhcp_lease *lease = data; - - return lease->address == L_PTR_TO_UINT(user_data); -} - -static struct l_dhcp_lease *find_lease_by_ip(struct l_queue *lease_list, - uint32_t nip) -{ - return l_queue_find(lease_list, match_lease_ip, L_UINT_TO_PTR(nip)); -} - static bool check_requested_ip(struct l_dhcp_server *server, uint32_t requested_nip) { @@ -391,7 +437,7 @@ continue; lease = find_lease_by_ip(server->expired_list, ip_nl); - if (lease) + if (lease && memcmp(lease->mac, safe_mac, ETH_ALEN)) continue; if (arp_check(ip_nl, safe_mac)) @@ -418,6 +464,71 @@ reply->ciaddr = client_msg->ciaddr; } +static bool server_message_send(struct l_dhcp_server *server, + struct dhcp_message *reply, size_t len, + uint8_t type) +{ + uint32_t daddr; + uint16_t dport; + const uint8_t *dest_mac; + + /* + * RFC2131 Section 4.1: "If the 'giaddr' field in a DHCP message from + * a client is non-zero, the server sends any return messages to the + * 'DHCP server' port on the BOOTP relay agent whose address appears + * in 'giaddr'. If the 'giaddr' field is zero and the 'ciaddr' field + * is nonzero, then the server unicasts DHCPOFFER and DHCPACK messages + * to the address in 'ciaddr'. If 'giaddr' is zero and 'ciaddr' is + * zero, and the broadcast bit is set, then the server broadcasts + * DHCPOFFER and DHCPACK messages to 0xffffffff. If the broadcast bit + * is not set and 'giaddr' is zero and 'ciaddr' is zero, then the + * server unicasts DHCPOFFER and DHCPACK messages to the client's + * hardware address and 'yiaddr' address. In all cases, when 'giaddr' + * is zero, the server broadcasts any DHCPNAK messages to 0xffffffff." + * + * 4.3.2: "If 'giaddr' is set in the DHCPREQUEST message, the client + * is on a different subnet. The server MUST set the broadcast bit in + * the DHCPNAK, so that the relay agent will broadcast the DHCPNAK to + * the client, because the client may not have a correct network + * address or subnet mask, and the client may not be answering ARP + * requests." + */ + if (reply->giaddr) { + dport = DHCP_PORT_SERVER; + daddr = reply->giaddr; + dest_mac = reply->chaddr; + + if (type == DHCP_MESSAGE_TYPE_NAK) + reply->flags |= L_CPU_TO_BE16(DHCP_FLAG_BROADCAST); + } else { + dport = DHCP_PORT_CLIENT; + + if (type == DHCP_MESSAGE_TYPE_NAK) { + daddr = 0xffffffff; + dest_mac = MAC_BCAST_ADDR; + } else if (reply->ciaddr) { + daddr = reply->ciaddr; + dest_mac = reply->chaddr; + } else if (L_BE16_TO_CPU(reply->flags) & DHCP_FLAG_BROADCAST) { + daddr = 0xffffffff; + dest_mac = MAC_BCAST_ADDR; + } else { + daddr = reply->yiaddr; + dest_mac = reply->chaddr; + } + } + + if (server->transport->l2_send(server->transport, server->address, + DHCP_PORT_SERVER, daddr, dport, + dest_mac, reply, len) < 0) { + SERVER_DEBUG("Failed to send %s", + _dhcp_message_type_to_string(type)); + return false; + } + + return true; +} + static void add_server_options(struct l_dhcp_server *server, struct dhcp_message_builder *builder) { @@ -440,9 +551,20 @@ } } +/* Copy the client identifier option from the client message per RFC6842 */ +static void copy_client_id(struct dhcp_message_builder *builder, + const uint8_t *client_id) +{ + if (client_id) + _dhcp_message_builder_append(builder, + DHCP_OPTION_CLIENT_IDENTIFIER, + client_id[0], client_id + 1); +} + static void send_offer(struct l_dhcp_server *server, const struct dhcp_message *client_msg, - struct l_dhcp_lease *lease, uint32_t requested_ip) + struct l_dhcp_lease *lease, uint32_t requested_ip, + const uint8_t *client_id) { struct dhcp_message_builder builder; size_t len = sizeof(struct dhcp_message) + DHCP_MIN_OPTIONS_SIZE; @@ -464,7 +586,8 @@ return; } - lease = add_lease(server, true, client_msg->chaddr, reply->yiaddr); + lease = add_lease(server, true, client_id, client_msg->chaddr, + reply->yiaddr); if (!lease) { SERVER_DEBUG("add_lease() failed"); return; @@ -483,21 +606,19 @@ 4, &server->address); add_server_options(server, &builder); + copy_client_id(&builder, client_id); _dhcp_message_builder_finalize(&builder, &len); SERVER_DEBUG("Sending OFFER of "NIPQUAD_FMT " to "MAC, NIPQUAD(reply->yiaddr), MAC_STR(reply->chaddr)); - if (server->transport->l2_send(server->transport, server->address, - DHCP_PORT_SERVER, - reply->ciaddr, DHCP_PORT_CLIENT, - reply->chaddr, reply, len) < 0) - SERVER_DEBUG("Failed to send OFFER"); + server_message_send(server, reply, len, DHCP_MESSAGE_TYPE_OFFER); } static void send_inform(struct l_dhcp_server *server, - const struct dhcp_message *client_msg) + const struct dhcp_message *client_msg, + const uint8_t *client_id) { struct dhcp_message_builder builder; size_t len = sizeof(struct dhcp_message) + DHCP_MIN_OPTIONS_SIZE; @@ -510,18 +631,16 @@ _dhcp_message_builder_init(&builder, reply, len, DHCP_MESSAGE_TYPE_ACK); add_server_options(server, &builder); + copy_client_id(&builder, client_id); _dhcp_message_builder_finalize(&builder, &len); - if (server->transport->l2_send(server->transport, server->address, - DHCP_PORT_SERVER, reply->ciaddr, - DHCP_PORT_CLIENT, reply->chaddr, - reply, len) < 0) - SERVER_DEBUG("Failed to send INFORM"); + server_message_send(server, reply, len, DHCP_MESSAGE_TYPE_INFORM); } static void send_nak(struct l_dhcp_server *server, - const struct dhcp_message *client_msg) + const struct dhcp_message *client_msg, + const uint8_t *client_id) { struct dhcp_message_builder builder; size_t len = sizeof(struct dhcp_message) + DHCP_MIN_OPTIONS_SIZE; @@ -532,24 +651,22 @@ server_message_init(server, client_msg, reply); _dhcp_message_builder_init(&builder, reply, len, DHCP_MESSAGE_TYPE_NAK); - + copy_client_id(&builder, client_id); _dhcp_message_builder_finalize(&builder, &len); - if (server->transport->l2_send(server->transport, server->address, - DHCP_PORT_SERVER, reply->ciaddr, - DHCP_PORT_CLIENT, MAC_BCAST_ADDR, - reply, len) < 0) - SERVER_DEBUG("Failed to send NACK"); + server_message_send(server, reply, len, DHCP_MESSAGE_TYPE_NAK); } static void send_ack(struct l_dhcp_server *server, - const struct dhcp_message *client_msg, uint32_t dest) + const struct dhcp_message *client_msg, + struct l_dhcp_lease *lease, + bool rapid_commit) { struct dhcp_message_builder builder; size_t len = sizeof(struct dhcp_message) + DHCP_MIN_OPTIONS_SIZE; L_AUTO_FREE_VAR(struct dhcp_message *, reply); uint32_t lease_time = L_CPU_TO_BE32(server->lease_seconds); - struct l_dhcp_lease *lease; + L_AUTO_FREE_VAR(uint8_t *, client_id) = l_steal_ptr(lease->client_id); reply = (struct dhcp_message *) l_new(uint8_t, len); @@ -557,37 +674,39 @@ _dhcp_message_builder_init(&builder, reply, len, DHCP_MESSAGE_TYPE_ACK); - reply->yiaddr = dest; + reply->yiaddr = lease->address; _dhcp_message_builder_append(&builder, L_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time); add_server_options(server, &builder); + copy_client_id(&builder, client_id); _dhcp_message_builder_append(&builder, L_DHCP_OPTION_SERVER_IDENTIFIER, 4, &server->address); + if (rapid_commit) + _dhcp_message_builder_append(&builder, DHCP_OPTION_RAPID_COMMIT, + 0, ""); + _dhcp_message_builder_finalize(&builder, &len); SERVER_DEBUG("Sending ACK to "NIPQUAD_FMT, NIPQUAD(reply->yiaddr)); - if (server->transport->l2_send(server->transport, server->address, - DHCP_PORT_SERVER, reply->ciaddr, - DHCP_PORT_CLIENT, - reply->chaddr, reply, len) < 0) { - SERVER_DEBUG("Failed to send ACK"); + if (!server_message_send(server, reply, len, DHCP_MESSAGE_TYPE_ACK)) return; - } - lease = add_lease(server, false, reply->chaddr, reply->yiaddr); + lease = add_lease(server, false, client_id, reply->chaddr, + reply->yiaddr); if (server->event_handler) server->event_handler(server, L_DHCP_SERVER_EVENT_NEW_LEASE, server->user_data, lease); } -static void listener_event(const void *data, size_t len, void *user_data) +static void listener_event(const void *data, size_t len, void *user_data, + const uint8_t *saddr) { struct l_dhcp_server *server = user_data; const struct dhcp_message *message = data; @@ -596,11 +715,17 @@ const void *v; struct l_dhcp_lease *lease; uint8_t type = 0; - uint32_t server_id_opt = 0; + bool server_id_opt = false; + bool server_id_match = true; uint32_t requested_ip_opt = 0; + L_AUTO_FREE_VAR(uint8_t *, client_id_opt) = NULL; + bool rapid_commit_opt = false; SERVER_DEBUG(""); + if (saddr && memcmp(saddr, message->chaddr, ETH_ALEN)) + return; + if (!_dhcp_message_iter_init(&iter, message, len)) return; @@ -612,25 +737,43 @@ break; case L_DHCP_OPTION_SERVER_IDENTIFIER: - if (l == 4) - server_id_opt = l_get_u32(v); - - if (server->address != server_id_opt) - return; + if (l != 4) + break; + server_id_opt = true; + server_id_match = (l_get_u32(v) == server->address); break; case L_DHCP_OPTION_REQUESTED_IP_ADDRESS: if (l == 4) requested_ip_opt = l_get_u32(v); break; + case DHCP_OPTION_CLIENT_IDENTIFIER: + if (l < 1 || l > 253 || client_id_opt) + break; + + client_id_opt = l_malloc(l + 1); + client_id_opt[0] = l; + memcpy(client_id_opt + 1, v, l); + break; + case DHCP_OPTION_RAPID_COMMIT: + rapid_commit_opt = true; + break; } } if (type == 0) return; - lease = find_lease_by_mac(server, message->chaddr); + if (requested_ip_opt) + lease = find_lease_by_id_and_ip(server->lease_list, + client_id_opt, message->chaddr, + requested_ip_opt); + + if (!requested_ip_opt || !lease) + lease = find_lease_by_id(server->lease_list, client_id_opt, + message->chaddr); + if (!lease) SERVER_DEBUG("No lease found for "MAC, MAC_STR(message->chaddr)); @@ -640,34 +783,125 @@ SERVER_DEBUG("Received DISCOVER, requested IP "NIPQUAD_FMT, NIPQUAD(requested_ip_opt)); - send_offer(server, message, lease, requested_ip_opt); + if (!server_id_match) + break; + + if (rapid_commit_opt && server->rapid_commit) { + lease = l_dhcp_server_discover(server, requested_ip_opt, + client_id_opt, + message->chaddr); + if (!lease) { + send_nak(server, message, client_id_opt); + break; + } + send_ack(server, message, lease, rapid_commit_opt); + break; + } + + send_offer(server, message, lease, requested_ip_opt, + client_id_opt); break; case DHCP_MESSAGE_TYPE_REQUEST: 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_match) { + if (server->authoritative) { + send_nak(server, message, client_id_opt); + break; + } + + if (!lease || !lease->offering) break; - } - if (lease && requested_ip_opt == lease->address) { - send_ack(server, message, lease->address); + remove_lease(server, lease); break; } - if (server_id_opt || !lease) { - send_nak(server, message); + /* + * As an extension, check if we have an expired lease matching + * the requested IP and the client ID/mac and if so, allow the + * lease to be re-activated. + */ + if (!lease && requested_ip_opt) + lease = find_lease_by_id_and_ip(server->expired_list, + client_id_opt, + message->chaddr, + requested_ip_opt); + + /* + * 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->authoritative) + send_nak(server, message, client_id_opt); + 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_id_match) { + /* + * 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, client_id_opt); + 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, client_id_opt); + break; + } + } + + send_ack(server, message, lease, false); 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_id_match || !requested_ip_opt || + !lease) break; if (requested_ip_opt == lease->address) @@ -677,7 +911,8 @@ case DHCP_MESSAGE_TYPE_RELEASE: SERVER_DEBUG("Received RELEASE"); - if (!server_id_opt || !lease || lease->offering) + if (!server_id_opt || !server_id_match || !lease || + lease->offering) break; if (message->ciaddr == lease->address) @@ -687,7 +922,10 @@ case DHCP_MESSAGE_TYPE_INFORM: SERVER_DEBUG("Received INFORM"); - send_inform(server, message); + if (!server_id_match) + break; + + send_inform(server, message, client_id_opt); break; } } @@ -732,6 +970,8 @@ server->expired_list = l_queue_new(); server->started = false; + server->authoritative = true; + server->rapid_commit = true; server->lease_seconds = DEFAULT_DHCP_LEASE_SEC; server->max_expired = MAX_EXPIRED_LEASES; @@ -803,8 +1043,8 @@ */ if (!server->start_ip) { server->start_ip = ntohl(server->address) + 1; - server->end_ip = ntohl(server->address) | - (~ntohl(server->netmask)); + server->end_ip = (ntohl(server->address) | + (~ntohl(server->netmask))) - 1; } else { if ((server->start_ip ^ ntohl(server->address)) & ntohl(server->netmask)) @@ -813,17 +1053,29 @@ if ((server->end_ip ^ ntohl(server->address)) & ntohl(server->netmask)) return false; - } - /* - * We skip over IPs ending in 0 or 255 when selecting a free address - * later on but make sure end_ip is not 0xffffffff so we can use - * "<= server->end_ip" safely in the loop condition. - */ - if ((server->end_ip & 0xff) == 255) - server->end_ip--; + /* + * Directly ensure the [start_ip, end_ip] range doesn't + * include the subnet address or the broadcast address so that + * we have fewer checks to make when selecting a free address + * from that range. Additionally this ensures end_ip is not + * 0xffffffff so we can use the condition "<= server->end_ip" + * safely on uint32_t values. + * In find_free_or_expired_ip we skip over IPs ending in .0 or + * .255 even for netmasks other than 24-bit just to avoid + * generating addresses that could look suspicious even if + * they're legal. We don't exclude these addresses when + * explicitly requested by the client, i.e. in + * check_requested_ip. + */ + if ((server->start_ip & (~ntohl(server->netmask))) == 0) + server->start_ip++; + + if ((server->end_ip | ntohl(server->netmask)) == 0xffffffff) + server->end_ip--; + } - if (server->start_ip > server->end_ip) + if (server->start_ip >= server->end_ip) return false; if (!server->ifname) { @@ -1053,9 +1305,29 @@ 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 void l_dhcp_server_set_enable_rapid_commit( + struct l_dhcp_server *server, + bool enable) +{ + if (unlikely(!server)) + return; + + server->rapid_commit = enable; +} + LIB_EXPORT struct l_dhcp_lease *l_dhcp_server_discover( struct l_dhcp_server *server, uint32_t requested_ip_opt, + const uint8_t *client_id, const uint8_t *mac) { struct l_dhcp_lease *lease; @@ -1063,7 +1335,7 @@ SERVER_DEBUG("Requested IP " NIPQUAD_FMT " for " MAC, NIPQUAD(requested_ip_opt), MAC_STR(mac)); - if ((lease = find_lease_by_mac(server, mac))) + if ((lease = find_lease_by_id(server->lease_list, client_id, 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); @@ -1074,7 +1346,7 @@ } } - lease = add_lease(server, true, mac, requested_ip_opt); + lease = add_lease(server, true, client_id, mac, requested_ip_opt); if (unlikely(!lease)) { SERVER_DEBUG("add_lease() failed"); return NULL; @@ -1088,13 +1360,16 @@ LIB_EXPORT bool l_dhcp_server_request(struct l_dhcp_server *server, struct l_dhcp_lease *lease) { + uint8_t mac[ETH_ALEN]; + 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); + memcpy(mac, lease->mac, ETH_ALEN); + lease = add_lease(server, false, NULL, mac, lease->address); if (server->event_handler) server->event_handler(server, L_DHCP_SERVER_EVENT_NEW_LEASE, @@ -1144,11 +1419,45 @@ return true; } +struct dhcp_expire_by_mac_data { + struct l_dhcp_server *server; + const uint8_t *mac; + unsigned int expired_cnt; +}; + +static bool dhcp_expire_by_mac(void *data, void *user_data) +{ + struct l_dhcp_lease *lease = data; + struct dhcp_expire_by_mac_data *expire_data = user_data; + struct l_dhcp_server *server = expire_data->server; + + if (!match_lease_mac(lease, expire_data->mac)) + return false; + + if (server->event_handler) + server->event_handler(server, L_DHCP_SERVER_EVENT_LEASE_EXPIRED, + server->user_data, lease); + + if (!lease->offering) { + if (l_queue_length(server->expired_list) > server->max_expired) + _dhcp_lease_free(l_queue_pop_head(server->expired_list)); + + l_queue_push_tail(server->expired_list, lease); + } else + _dhcp_lease_free(lease); + + expire_data->expired_cnt++; + 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); + struct dhcp_expire_by_mac_data expire_data = { server, mac, 0 }; + + l_queue_foreach_remove(server->lease_list, dhcp_expire_by_mac, + &expire_data); - if (likely(lease)) - lease_release(server, lease); + if (expire_data.expired_cnt) + set_next_expire_timer(server, NULL); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ell-0.42/ell/dhcp-transport.c new/ell-0.43/ell/dhcp-transport.c --- old/ell-0.42/ell/dhcp-transport.c 2021-06-11 20:09:50.000000000 +0200 +++ new/ell-0.43/ell/dhcp-transport.c 2021-08-22 06:08:02.000000000 +0200 @@ -111,8 +111,11 @@ ssize_t len; struct dhcp_packet *p; uint16_t c; + struct sockaddr_ll saddr; + socklen_t saddr_len = sizeof(saddr); - len = read(fd, buf, sizeof(buf)); + len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) &saddr, + &saddr_len); if (len < 0) return false; @@ -148,8 +151,15 @@ len -= sizeof(struct udphdr) - sizeof(struct iphdr); - if (transport->super.rx_cb) - transport->super.rx_cb(&p->dhcp, len, transport->super.rx_data); + if (transport->super.rx_cb) { + const uint8_t *src_mac = NULL; + + if (saddr_len >= sizeof(saddr) && saddr.sll_halen == ETH_ALEN) + src_mac = saddr.sll_addr; + + transport->super.rx_cb(&p->dhcp, len, transport->super.rx_data, + src_mac); + } return true; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ell-0.42/ell/dhcp-util.c new/ell-0.43/ell/dhcp-util.c --- old/ell-0.42/ell/dhcp-util.c 2021-03-29 14:19:13.000000000 +0200 +++ new/ell-0.43/ell/dhcp-util.c 2021-08-22 06:08:02.000000000 +0200 @@ -73,7 +73,9 @@ builder->pos[0] = code; builder->pos[1] = optlen; - memcpy(builder->pos + 2, optval, optlen); + + if (optlen) + memcpy(builder->pos + 2, optval, optlen); builder->pos += optlen + 2; break; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ell-0.42/ell/dhcp.c new/ell-0.43/ell/dhcp.c --- old/ell-0.42/ell/dhcp.c 2021-08-01 21:49:37.000000000 +0200 +++ new/ell-0.43/ell/dhcp.c 2021-08-22 06:08:02.000000000 +0200 @@ -361,6 +361,8 @@ client->hostname)) return -EINVAL; + _dhcp_message_builder_append(&builder, DHCP_OPTION_RAPID_COMMIT, + 0, ""); _dhcp_message_builder_finalize(&builder, &len); return client->transport->l2_send(client->transport, @@ -773,7 +775,8 @@ return 0; } -static void dhcp_client_rx_message(const void *data, size_t len, void *userdata) +static void dhcp_client_rx_message(const void *data, size_t len, void *userdata, + const uint8_t *saddr) { struct l_dhcp_client *client = userdata; const struct dhcp_message *message = data; @@ -815,6 +818,17 @@ case DHCP_STATE_INIT: return; case DHCP_STATE_SELECTING: + if (msg_type == DHCP_MESSAGE_TYPE_ACK) { + _dhcp_message_iter_init(&iter, message, len); + + while (_dhcp_message_iter_next(&iter, &t, &l, &v)) + if (t == DHCP_OPTION_RAPID_COMMIT) { + CLIENT_ENTER_STATE( + DHCP_STATE_REQUESTING); + goto receive_rapid_commit; + } + } + if (msg_type != DHCP_MESSAGE_TYPE_OFFER) return; @@ -835,6 +849,7 @@ case DHCP_STATE_REQUESTING: case DHCP_STATE_RENEWING: case DHCP_STATE_REBINDING: + receive_rapid_commit: if (msg_type == DHCP_MESSAGE_TYPE_NAK) { CLIENT_DEBUG("Received NAK, Stopping..."); l_dhcp_client_stop(client); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ell-0.42/ell/dhcp.h new/ell-0.43/ell/dhcp.h --- old/ell-0.42/ell/dhcp.h 2021-08-01 21:49:37.000000000 +0200 +++ new/ell-0.43/ell/dhcp.h 2021-08-22 06:08:02.000000000 +0200 @@ -141,9 +141,14 @@ 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); +void l_dhcp_server_set_enable_rapid_commit(struct l_dhcp_server *server, + bool enable); struct l_dhcp_lease *l_dhcp_server_discover(struct l_dhcp_server *server, uint32_t requested_ip_opt, + const uint8_t *client_id, const uint8_t *mac); bool l_dhcp_server_request(struct l_dhcp_server *server, struct l_dhcp_lease *lease); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ell-0.42/ell/ell.sym new/ell-0.43/ell/ell.sym --- old/ell-0.42/ell/ell.sym 2021-08-01 21:49:37.000000000 +0200 +++ new/ell-0.43/ell/ell.sym 2021-08-22 06:08:02.000000000 +0200 @@ -259,6 +259,8 @@ l_dhcp_server_set_netmask; l_dhcp_server_set_gateway; l_dhcp_server_set_dns; + l_dhcp_server_set_authoritative; + l_dhcp_server_set_enable_rapid_commit; l_dhcp_server_discover; l_dhcp_server_request; l_dhcp_server_decline; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ell-0.42/unit/test-dhcp.c new/ell-0.43/unit/test-dhcp.c --- old/ell-0.42/unit/test-dhcp.c 2021-06-11 20:09:50.000000000 +0200 +++ new/ell-0.43/unit/test-dhcp.c 2021-08-22 06:08:02.000000000 +0200 @@ -655,13 +655,17 @@ return r; } +static bool client_send_called = false; + static int fake_transport_send(struct dhcp_transport *transport, const struct sockaddr_in *dest, const void *data, size_t len) { assert(len <= sizeof(client_packet)); + assert(!client_send_called); memcpy(client_packet, data, len); client_packet_len = len; + client_send_called = true; return len; } @@ -673,8 +677,10 @@ const void *data, size_t len) { assert(len <= sizeof(client_packet)); + assert(!client_send_called); memcpy(client_packet, data, len); client_packet_len = len; + client_send_called = true; return len; } @@ -710,16 +716,21 @@ assert(l_dhcp_client_start(client)); + assert(client_send_called); + client_send_called = false; assert(dhcp_message_compare(discover_data_1, sizeof(discover_data_1), client_packet, client_packet_len)); - transport->rx_cb(offer_data_1, sizeof(offer_data_1), client); + transport->rx_cb(offer_data_1, sizeof(offer_data_1), client, NULL); + assert(client_send_called); + client_send_called = false; assert(dhcp_message_compare(request_data_1, sizeof(request_data_1), client_packet, client_packet_len)); event_handler_called = false; - transport->rx_cb(ack_data_1, sizeof(ack_data_1), client); + transport->rx_cb(ack_data_1, sizeof(ack_data_1), client, NULL); + assert(!client_send_called); assert(event_handler_called); lease = l_dhcp_client_get_lease(client); @@ -736,6 +747,8 @@ assert(lease->t2 == 0x00012750); l_dhcp_client_destroy(client); + assert(client_send_called); + client_send_called = false; } static bool l2_send_called = false; @@ -749,6 +762,7 @@ const void *data, size_t len) { assert(len <= sizeof(server_packet)); + assert(!l2_send_called); memcpy(server_packet, data, len); server_packet_len = len; @@ -774,9 +788,11 @@ { switch (event) { case L_DHCP_SERVER_EVENT_NEW_LEASE: + assert(!new_client); new_client = l_dhcp_lease_get_address(lease); break; case L_DHCP_SERVER_EVENT_LEASE_EXPIRED: + assert(!expired_client); expired_client = l_dhcp_lease_get_address(lease); break; } @@ -806,35 +822,46 @@ return client; } -static bool client_connect(struct l_dhcp_client *client, - struct l_dhcp_server *server) +static void client_connect(struct l_dhcp_client *client, + struct l_dhcp_server *server, bool rapid_commit) { struct dhcp_transport *srv_transport = _dhcp_server_get_transport(server); struct dhcp_transport *cli_transport = _dhcp_client_get_transport(client); - if (!l_dhcp_client_start(client)) - return false; + uint8_t cli_addr[ETH_ALEN]; + const struct dhcp_message *msg = (struct dhcp_message *) client_packet; + + assert(l_dhcp_client_start(client)); + assert(client_send_called); + client_send_called = false; + memcpy(cli_addr, msg->chaddr, ETH_ALEN); /* RX DISCOVER */ - srv_transport->rx_cb(client_packet, client_packet_len, server); + srv_transport->rx_cb(client_packet, client_packet_len, server, + cli_addr); assert(l2_send_called); l2_send_called = false; - /* RX OFFER */ - cli_transport->rx_cb(server_packet, server_packet_len, client); - - /* RX REQUEST */ - srv_transport->rx_cb(client_packet, client_packet_len, server); - assert(l2_send_called); - l2_send_called = false; + if (!rapid_commit) { + /* RX OFFER */ + cli_transport->rx_cb(server_packet, server_packet_len, client, + NULL); + assert(client_send_called); + client_send_called = false; + + /* RX REQUEST */ + srv_transport->rx_cb(client_packet, client_packet_len, server, + cli_addr); + assert(l2_send_called); + l2_send_called = false; + } /* RX ACK */ - cli_transport->rx_cb(server_packet, server_packet_len, client); + cli_transport->rx_cb(server_packet, server_packet_len, client, NULL); + assert(!client_send_called); assert(event_handler_called); - - return true; } static struct l_dhcp_server *server_init() @@ -866,6 +893,7 @@ static void test_complete_run(const void *data) { + bool rapid_commit = L_PTR_TO_UINT(data); static const uint8_t addr1[6] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; static const uint8_t addr2[6] = { 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c }; struct dhcp_transport *srv_transport; @@ -883,6 +911,7 @@ char **dns_list; server = server_init(); + l_dhcp_server_set_enable_rapid_commit(server, rapid_commit); assert(l_dhcp_server_set_ip_range(server, "192.168.1.2", "192.168.1.100")); @@ -891,7 +920,7 @@ client1 = client_init(addr1); - client_connect(client1, server); + client_connect(client1, server, rapid_commit); cli_lease = l_dhcp_client_get_lease(client1); assert(cli_lease); @@ -924,7 +953,7 @@ client2 = client_init(addr2); - client_connect(client2, server); + client_connect(client2, server, rapid_commit); cli_lease = l_dhcp_client_get_lease(client2); assert(cli_lease); @@ -941,22 +970,27 @@ l_free(cli_addr); l_dhcp_client_stop(client1); - - srv_transport->rx_cb(client_packet, client_packet_len, server); + assert(client_send_called); + client_send_called = false; + srv_transport->rx_cb(client_packet, client_packet_len, server, addr1); assert(expired_client); assert(!strcmp(expired_client, "192.168.1.2")); l_free(expired_client); expired_client = NULL; l_dhcp_client_stop(client2); - srv_transport->rx_cb(client_packet, client_packet_len, server); + assert(client_send_called); + client_send_called = false; + srv_transport->rx_cb(client_packet, client_packet_len, server, addr2); assert(expired_client); assert(!strcmp(expired_client, "192.168.1.3")); l_free(expired_client); expired_client = NULL; l_dhcp_client_destroy(client1); + assert(!client_send_called); l_dhcp_client_destroy(client2); + assert(!client_send_called); l_dhcp_server_stop(server); l_dhcp_server_destroy(server); @@ -975,6 +1009,7 @@ l_dhcp_server_set_ip_range(server, "192.168.1.2", "192.168.1.11"); _dhcp_server_set_max_expired_clients(server, 10); + l_dhcp_server_set_enable_rapid_commit(server, false); /* * Connect and release 10 clients, this should max out the expired @@ -987,19 +1022,22 @@ addr[5] = i; client = client_init(addr); - client_connect(client, server); + client_connect(client, server, false); l_free(new_client); new_client = NULL; l_dhcp_client_destroy(client); - srv_transport->rx_cb(client_packet, client_packet_len, server); + assert(client_send_called); + client_send_called = false; + srv_transport->rx_cb(client_packet, client_packet_len, server, + addr); l_free(expired_client); expired_client = NULL; } addr[5] = i + 1; client_new = client_init(addr); - client_connect(client_new, server); + client_connect(client_new, server, false); l_free(new_client); new_client = NULL; @@ -1010,6 +1048,8 @@ l_free(cli_addr); l_dhcp_client_destroy(client_new); + assert(client_send_called); + client_send_called = false; l_dhcp_server_destroy(server); } @@ -1036,7 +1076,8 @@ l_test_add("discover", test_discover, NULL); - l_test_add("complete run", test_complete_run, NULL); + l_test_add("complete run", test_complete_run, L_UINT_TO_PTR(false)); + l_test_add("rapid commit", test_complete_run, L_UINT_TO_PTR(true)); l_test_add("expired IP reuse", test_expired_ip_reuse, NULL); return l_test_run();