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();

Reply via email to