about summary refs log tree commit diff
path: root/shared/n-dhcp4
diff options
context:
space:
mode:
authorSebastien Bacher <seb128@ubuntu.com>2020-01-13 16:10:30 +0100
committerSebastien Bacher <seb128@ubuntu.com>2020-01-13 16:31:40 +0100
commit5a09f7759860f4f2a9bb01471ca8099cd705bc10 (patch)
tree3a9cffc9622d53c9196771a1c7924bceb754f118 /shared/n-dhcp4
parent25691220fd27093630cf244e219b2f4f1f4e749e (diff)
parentca847639aab94434daed120c874e1100c3d75dcf (diff)
Merge remote-tracking branch 'salsa/debian/master'
Diffstat (limited to 'shared/n-dhcp4')
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-c-connection.c92
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-c-lease.c26
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-c-probe.c126
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-client.c49
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-incoming.c5
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-private.h26
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-socket.c4
-rw-r--r--shared/n-dhcp4/src/n-dhcp4.h6
8 files changed, 303 insertions, 31 deletions
diff --git a/shared/n-dhcp4/src/n-dhcp4-c-connection.c b/shared/n-dhcp4/src/n-dhcp4-c-connection.c
index 5c50dacf..e51a3e32 100644
--- a/shared/n-dhcp4/src/n-dhcp4-c-connection.c
+++ b/shared/n-dhcp4/src/n-dhcp4-c-connection.c
@@ -139,7 +139,19 @@ int n_dhcp4_c_connection_listen(NDhcp4CConnection *connection) {
         _c_cleanup_(c_closep) int fd_packet = -1;
         int r;
 
-        c_assert(connection->state == N_DHCP4_C_CONNECTION_STATE_INIT);
+        c_assert(connection->state == N_DHCP4_C_CONNECTION_STATE_INIT ||
+                 connection->state == N_DHCP4_C_CONNECTION_STATE_DRAINING ||
+                 connection->state == N_DHCP4_C_CONNECTION_STATE_UDP);
+
+        if (connection->fd_packet >= 0) {
+                epoll_ctl(connection->fd_epoll, EPOLL_CTL_DEL, connection->fd_packet, NULL);
+                connection->fd_packet = c_close(connection->fd_packet);
+        }
+
+        if (connection->fd_udp >= 0) {
+                epoll_ctl(connection->fd_epoll, EPOLL_CTL_DEL, connection->fd_udp, NULL);
+                connection->fd_udp = c_close(connection->fd_udp);
+        }
 
         r = n_dhcp4_c_socket_packet_new(&fd_packet, connection->client_config->ifindex);
         if (r)
@@ -319,7 +331,6 @@ void n_dhcp4_c_connection_get_timeout(NDhcp4CConnection *connection,
         switch (connection->request->userdata.type) {
         case N_DHCP4_C_MESSAGE_DISCOVER:
         case N_DHCP4_C_MESSAGE_SELECT:
-        case N_DHCP4_C_MESSAGE_REBOOT:
         case N_DHCP4_C_MESSAGE_INFORM:
                 /*
                  * Resend with an exponential backoff and a one second random
@@ -338,6 +349,7 @@ void n_dhcp4_c_connection_get_timeout(NDhcp4CConnection *connection,
                 break;
         case N_DHCP4_C_MESSAGE_REBIND:
         case N_DHCP4_C_MESSAGE_RENEW:
+        case N_DHCP4_C_MESSAGE_REBOOT:
                 /*
                  * Resend every sixty seconds with a one second random slack.
                  *
@@ -513,6 +525,7 @@ static int n_dhcp4_c_connection_new_message(NDhcp4CConnection *connection,
         n_dhcp4_c_connection_init_header(connection, header);
 
         message->userdata.type = type;
+        message->userdata.message_type = message_type;
 
         /*
          * Note that some implementations expect the MESSAGE_TYPE option to be
@@ -690,6 +703,7 @@ int n_dhcp4_c_connection_select_new(NDhcp4CConnection *connection,
          */
         message->userdata.start_time = offer->userdata.start_time;
         message->userdata.base_time = offer->userdata.base_time;
+        message->userdata.client_addr = client.s_addr;
         n_dhcp4_incoming_get_xid(offer, &xid);
         n_dhcp4_outgoing_set_xid(message, xid);
 
@@ -763,6 +777,7 @@ int n_dhcp4_c_connection_renew_new(NDhcp4CConnection *connection,
         if (r)
                 return r;
 
+        message->userdata.client_addr = connection->client_ip;
         *requestp = message;
         message = NULL;
         return 0;
@@ -796,6 +811,7 @@ int n_dhcp4_c_connection_rebind_new(NDhcp4CConnection *connection,
         if (r)
                 return r;
 
+        message->userdata.client_addr = connection->client_ip;
         *requestp = message;
         message = NULL;
         return 0;
@@ -847,6 +863,7 @@ int n_dhcp4_c_connection_decline_new(NDhcp4CConnection *connection,
                         return r;
         }
 
+        message->userdata.client_addr = client.s_addr;
         *requestp = message;
         message = NULL;
         return 0;
@@ -889,6 +906,7 @@ int n_dhcp4_c_connection_inform_new(NDhcp4CConnection *connection,
         if (r)
                 return r;
 
+        message->userdata.client_addr = connection->client_ip;
         *requestp = message;
         message = NULL;
         return 0;
@@ -955,10 +973,38 @@ int n_dhcp4_c_connection_release_new(NDhcp4CConnection *connection,
         return 0;
 }
 
+static const char *message_type_to_str(uint8_t type) {
+        switch (type) {
+        case N_DHCP4_MESSAGE_DISCOVER:
+                return "DISCOVER";
+        case N_DHCP4_MESSAGE_OFFER:
+                return "OFFER";
+        case N_DHCP4_MESSAGE_REQUEST:
+                return "REQUEST";
+        case N_DHCP4_MESSAGE_DECLINE:
+                return "DECLINE";
+        case N_DHCP4_MESSAGE_ACK:
+                return "ACK";
+        case N_DHCP4_MESSAGE_NAK:
+                return "NACK";
+        case N_DHCP4_MESSAGE_RELEASE:
+                return "RELEASE";
+        case N_DHCP4_MESSAGE_INFORM:
+                return "INFORM";
+        case N_DHCP4_MESSAGE_FORCERENEW:
+                return "FORCERENEW";
+        default:
+                return "UNKNOWN";
+        }
+}
+
 static int n_dhcp4_c_connection_send_request(NDhcp4CConnection *connection,
                                              NDhcp4Outgoing *request,
                                              uint64_t timestamp) {
+        char server_addr[INET_ADDRSTRLEN];
+        char client_addr[INET_ADDRSTRLEN];
         int r;
+        bool broadcast = false;
 
         /*
          * Increment the base time and reset the xid field,
@@ -993,12 +1039,14 @@ static int n_dhcp4_c_connection_send_request(NDhcp4CConnection *connection,
         case N_DHCP4_C_MESSAGE_SELECT:
         case N_DHCP4_C_MESSAGE_REBOOT:
         case N_DHCP4_C_MESSAGE_DECLINE:
+        case N_DHCP4_C_MESSAGE_REBIND:
+                broadcast = true;
                 r = n_dhcp4_c_connection_packet_broadcast(connection, request);
                 if (r)
                         return r;
                 break;
         case N_DHCP4_C_MESSAGE_INFORM:
-        case N_DHCP4_C_MESSAGE_REBIND:
+                broadcast = true;
                 r = n_dhcp4_c_connection_udp_broadcast(connection, request);
                 if (r)
                         return r;
@@ -1015,6 +1063,26 @@ static int n_dhcp4_c_connection_send_request(NDhcp4CConnection *connection,
                 c_assert(0);
         }
 
+        if (request->userdata.client_addr == INADDR_ANY) {
+                n_dhcp4_c_log(connection->client_config, LOG_INFO,
+                              "sent %s to %s",
+                              message_type_to_str(request->userdata.message_type),
+                              broadcast ?
+                              "255.255.255.255" :
+                              inet_ntop(AF_INET, &connection->server_ip,
+                                        server_addr, sizeof(server_addr)));
+        } else {
+                n_dhcp4_c_log(connection->client_config, LOG_INFO,
+                              "sent %s of %s to %s",
+                              message_type_to_str(request->userdata.message_type),
+                              inet_ntop(AF_INET, &request->userdata.client_addr,
+                                        client_addr, sizeof(client_addr)),
+                              broadcast ?
+                              "255.255.255.255" :
+                              inet_ntop(AF_INET, &connection->server_ip,
+                                        server_addr, sizeof(server_addr)));
+        }
+
         ++request->userdata.n_send;
         return 0;
 }
@@ -1066,6 +1134,8 @@ int n_dhcp4_c_connection_dispatch_timer(NDhcp4CConnection *connection,
 int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection,
                                      NDhcp4Incoming **messagep) {
         _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL;
+        char serv_addr[INET_ADDRSTRLEN];
+        char client_addr[INET_ADDRSTRLEN];
         uint8_t type;
         int r;
 
@@ -1118,6 +1188,22 @@ int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection,
         if (r)
                 return r;
 
+        if (type == N_DHCP4_MESSAGE_OFFER || type == N_DHCP4_MESSAGE_ACK) {
+                n_dhcp4_c_log(connection->client_config, LOG_INFO,
+                              "received %s of %s from %s",
+                              message_type_to_str(type),
+                              inet_ntop(AF_INET, &message->message.header.yiaddr,
+                                        client_addr, sizeof(client_addr)),
+                              inet_ntop(AF_INET, &message->message.header.siaddr,
+                                        serv_addr, sizeof(serv_addr)));
+        } else {
+                n_dhcp4_c_log(connection->client_config, LOG_INFO,
+                              "received %s from %s",
+                              message_type_to_str(type),
+                              inet_ntop(AF_INET, &message->message.header.siaddr,
+                                        serv_addr, sizeof(serv_addr)));
+        }
+
         switch (type) {
         case N_DHCP4_MESSAGE_OFFER:
         case N_DHCP4_MESSAGE_ACK:
diff --git a/shared/n-dhcp4/src/n-dhcp4-c-lease.c b/shared/n-dhcp4/src/n-dhcp4-c-lease.c
index 208ad92a..695a112a 100644
--- a/shared/n-dhcp4/src/n-dhcp4-c-lease.c
+++ b/shared/n-dhcp4/src/n-dhcp4-c-lease.c
@@ -204,6 +204,32 @@ _c_public_ void n_dhcp4_client_lease_get_yiaddr(NDhcp4ClientLease *lease, struct
 }
 
 /**
+ * n_dhcp4_client_lease_get_siaddr() - get the server IP address
+ * @lease:                      the lease to operate on
+ * @siaddr:                     return argument for the IP address
+ *
+ * Gets the server IP address cotained in the lease. Or INADDR_ANY if the
+ * lease does not contain an IP address.
+ */
+_c_public_ void n_dhcp4_client_lease_get_siaddr(NDhcp4ClientLease *lease, struct in_addr *siaddr) {
+        NDhcp4Header *header = n_dhcp4_incoming_get_header(lease->message);
+
+        siaddr->s_addr = header->siaddr;
+}
+
+/**
+ * n_dhcp4_client_lease_get_basetime() - get the timestamp when the lease was received.
+ * @lease:                      the lease to operate on
+ * @ns_basetimep:               return argument for the base time in nano seconds
+ *
+ * Gets the timestamp when the lease was received in CLOCK_BOOTTIME. This
+ * is also the base timestamp for the expiration of the lifetime and t1/t2.
+ */
+_c_public_ void n_dhcp4_client_lease_get_basetime(NDhcp4ClientLease *lease, uint64_t *ns_basetimep) {
+        *ns_basetimep = lease->message->userdata.base_time;
+}
+
+/**
  * n_dhcp4_client_lease_get_lifetime() - get the lifetime
  * @lease:                      the lease to operate on
  * @ns_lifetimep:               return argument for the lifetime in nano seconds
diff --git a/shared/n-dhcp4/src/n-dhcp4-c-probe.c b/shared/n-dhcp4/src/n-dhcp4-c-probe.c
index 107c18bb..4fb7d389 100644
--- a/shared/n-dhcp4/src/n-dhcp4-c-probe.c
+++ b/shared/n-dhcp4/src/n-dhcp4-c-probe.c
@@ -170,8 +170,6 @@ _c_public_ void n_dhcp4_client_probe_config_set_inform_only(NDhcp4ClientProbeCon
  * INIT-REBOOT path, as described by the DHCP specification. In most cases, you
  * do not want this.
  *
- * XXX: This is currently not implemented, and setting the property has no effect.
- *
  * Background: The INIT-REBOOT path allows a DHCP client to skip
  *             server-discovery when rebooting/resuming their machine. The DHCP
  *             client simply re-requests the lease it had acquired before. This
@@ -438,11 +436,17 @@ int n_dhcp4_client_probe_new(NDhcp4ClientProbe **probep,
         if (r)
                 return r;
 
+        if (probe->config->init_reboot && probe->config->requested_ip.s_addr != INADDR_ANY)
+                probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT;
+        else
+                probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT;
+
         if (active) {
                 /*
                  * Defer the sending of DISCOVER by a random amount (by default up to 9 seconds).
                  */
-                probe->ns_deferred = ns_now + (n_dhcp4_client_probe_config_get_random(probe->config) % (probe->config->ms_start_delay * 1000000ULL));
+                if (probe->state == N_DHCP4_CLIENT_PROBE_STATE_INIT)
+                        probe->ns_deferred = ns_now + (n_dhcp4_client_probe_config_get_random(probe->config) % (probe->config->ms_start_delay * 1000000ULL));
                 probe->client->current_probe = probe;
         } else {
                 r = n_dhcp4_client_probe_raise(probe,
@@ -575,6 +579,14 @@ void n_dhcp4_client_probe_get_timeout(NDhcp4ClientProbe *probe, uint64_t *timeou
         n_dhcp4_c_connection_get_timeout(&probe->connection, &timeout);
 
         switch (probe->state) {
+        case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
+                /* send DHCP request immediately */
+                timeout = 1;
+                break;
+        case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
+                if (probe->ns_reinit && (!timeout || probe->ns_reinit < timeout))
+                        timeout = probe->ns_reinit;
+                break;
         case N_DHCP4_CLIENT_PROBE_STATE_INIT:
                 if (probe->ns_deferred && (!timeout || probe->ns_deferred < timeout))
                         timeout = probe->ns_deferred;
@@ -626,6 +638,52 @@ static int n_dhcp4_client_probe_outgoing_append_options(NDhcp4ClientProbe *probe
         return 0;
 }
 
+static int n_dhcp4_client_probe_transition_reboot(NDhcp4ClientProbe *probe, uint64_t ns_now) {
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL;
+        int r;
+
+        switch (probe->state) {
+        case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
+                r = n_dhcp4_c_connection_listen(&probe->connection);
+                if (r)
+                        return r;
+
+                r = n_dhcp4_c_connection_reboot_new(&probe->connection, &request, &probe->config->requested_ip);
+                if (r)
+                        return r;
+
+                r = n_dhcp4_client_probe_outgoing_append_options(probe, request);
+                if (r)
+                        return r;
+
+                r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now);
+                if (r)
+                        return r;
+                else
+                        request = NULL; /* consumed */
+
+                probe->state = N_DHCP4_CLIENT_PROBE_STATE_REBOOTING;
+                probe->ns_reinit = ns_now + 2000000000ULL;
+
+                break;
+
+        case N_DHCP4_CLIENT_PROBE_STATE_SELECTING:
+        case N_DHCP4_CLIENT_PROBE_STATE_INIT:
+        case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
+        case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING:
+        case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
+        case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
+        case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
+        case N_DHCP4_CLIENT_PROBE_STATE_REBINDING:
+        case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED:
+        default:
+                abort();
+                break;
+        }
+
+        return 0;
+}
+
 static int n_dhcp4_client_probe_transition_deferred(NDhcp4ClientProbe *probe, uint64_t ns_now) {
         _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL;
         int r;
@@ -635,12 +693,14 @@ static int n_dhcp4_client_probe_transition_deferred(NDhcp4ClientProbe *probe, ui
                 r = n_dhcp4_c_connection_listen(&probe->connection);
                 if (r)
                         return r;
+                /* fall-through */
 
+        case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
                 r = n_dhcp4_c_connection_discover_new(&probe->connection, &request);
                 if (r)
                         return r;
 
-                if (probe->config->requested_ip.s_addr != INADDR_ANY) {
+                if (!probe->config->init_reboot && probe->config->requested_ip.s_addr != INADDR_ANY) {
                         r = n_dhcp4_outgoing_append_requested_ip(request, probe->config->requested_ip);
                         if (r)
                                 return r;
@@ -663,7 +723,6 @@ static int n_dhcp4_client_probe_transition_deferred(NDhcp4ClientProbe *probe, ui
 
         case N_DHCP4_CLIENT_PROBE_STATE_SELECTING:
         case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
-        case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
         case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING:
         case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
         case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
@@ -726,6 +785,10 @@ static int n_dhcp4_client_probe_transition_t2(NDhcp4ClientProbe *probe, uint64_t
         switch (probe->state) {
         case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
         case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
+                r = n_dhcp4_c_connection_listen(&probe->connection);
+                if (r)
+                        return r;
+
                 r = n_dhcp4_c_connection_rebind_new(&probe->connection, &request);
                 if (r)
                         return r;
@@ -800,7 +863,8 @@ static int n_dhcp4_client_probe_transition_lifetime(NDhcp4ClientProbe *probe) {
         return 0;
 }
 
-static int n_dhcp4_client_probe_transition_offer(NDhcp4ClientProbe *probe, NDhcp4Incoming *message) {
+static int n_dhcp4_client_probe_transition_offer(NDhcp4ClientProbe *probe, NDhcp4Incoming *message_take) {
+        _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = message_take;
         _c_cleanup_(n_dhcp4_client_lease_unrefp) NDhcp4ClientLease *lease = NULL;
         NDhcp4CEventNode *node;
         int r;
@@ -818,7 +882,7 @@ static int n_dhcp4_client_probe_transition_offer(NDhcp4ClientProbe *probe, NDhcp
                 if (r)
                         return r;
 
-                /* message consumed, do not fail */
+                message = NULL; /* consumed */
 
                 n_dhcp4_client_lease_link(lease, probe);
 
@@ -843,14 +907,26 @@ static int n_dhcp4_client_probe_transition_offer(NDhcp4ClientProbe *probe, NDhcp
         return 0;
 }
 
-static int n_dhcp4_client_probe_transition_ack(NDhcp4ClientProbe *probe, NDhcp4Incoming *message) {
+static int n_dhcp4_client_probe_transition_ack(NDhcp4ClientProbe *probe, NDhcp4Incoming *message_take) {
+        _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = message_take;
         _c_cleanup_(n_dhcp4_client_lease_unrefp) NDhcp4ClientLease *lease = NULL;
         NDhcp4CEventNode *node;
+        struct in_addr client = {};
+        struct in_addr server = {};
         int r;
 
         switch (probe->state) {
-        case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
         case N_DHCP4_CLIENT_PROBE_STATE_REBINDING:
+                n_dhcp4_incoming_get_yiaddr(message, &client);
+
+                r = n_dhcp4_incoming_query_server_identifier(message, &server);
+                if (r)
+                        return r;
+                r = n_dhcp4_c_connection_connect(&probe->connection, &client, &server);
+                if (r)
+                        return r;
+                /* fall-through */
+        case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
 
                 r = n_dhcp4_client_probe_raise(probe,
                                                &node,
@@ -862,7 +938,7 @@ static int n_dhcp4_client_probe_transition_ack(NDhcp4ClientProbe *probe, NDhcp4I
                 if (r)
                         return r;
 
-                /* message consumed, do not fail */
+                message = NULL; /* consumed */
 
                 n_dhcp4_client_lease_link(lease, probe);
 
@@ -874,6 +950,7 @@ static int n_dhcp4_client_probe_transition_ack(NDhcp4ClientProbe *probe, NDhcp4I
                 break;
 
         case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING:
+        case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
 
                 r = n_dhcp4_client_probe_raise(probe,
                                                &node,
@@ -885,7 +962,7 @@ static int n_dhcp4_client_probe_transition_ack(NDhcp4ClientProbe *probe, NDhcp4I
                 if (r)
                         return r;
 
-                /* message consumed, don to fail */
+                message = NULL; /* consumed */
 
                 n_dhcp4_client_lease_link(lease, probe);
 
@@ -898,7 +975,6 @@ static int n_dhcp4_client_probe_transition_ack(NDhcp4ClientProbe *probe, NDhcp4I
         case N_DHCP4_CLIENT_PROBE_STATE_INIT:
         case N_DHCP4_CLIENT_PROBE_STATE_SELECTING:
         case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
-        case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
         case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
         case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
         case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED:
@@ -1077,6 +1153,19 @@ int n_dhcp4_client_probe_dispatch_timer(NDhcp4ClientProbe *probe, uint64_t ns_no
         int r;
 
         switch (probe->state) {
+        case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
+                r = n_dhcp4_client_probe_transition_reboot(probe, ns_now);
+                if (r)
+                        return r;
+                break;
+        case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
+                if (ns_now >= probe->ns_reinit) {
+                        r = n_dhcp4_client_probe_transition_deferred(probe, ns_now);
+                        if (r)
+                                return r;
+                }
+
+                break;
         case N_DHCP4_CLIENT_PROBE_STATE_INIT:
                 if (ns_now >= probe->ns_deferred) {
                         r = n_dhcp4_client_probe_transition_deferred(probe, ns_now);
@@ -1100,16 +1189,17 @@ int n_dhcp4_client_probe_dispatch_timer(NDhcp4ClientProbe *probe, uint64_t ns_no
                         r = n_dhcp4_client_probe_transition_lifetime(probe);
                         if (r)
                                 return r;
-                } else if (ns_now >= probe->current_lease->t2) {
+                } else if (ns_now >= probe->current_lease->t2 &&
+                           probe->state != N_DHCP4_CLIENT_PROBE_STATE_REBINDING) {
                         r = n_dhcp4_client_probe_transition_t2(probe, ns_now);
                         if (r)
                                 return r;
-                } else if (ns_now >= probe->current_lease->t1) {
+                } else if (ns_now >= probe->current_lease->t1 &&
+                           probe->state == N_DHCP4_CLIENT_PROBE_STATE_BOUND) {
                         r = n_dhcp4_client_probe_transition_t1(probe, ns_now);
                         if (r)
                                 return r;
                 }
-
                 break;
         default:
                 /* ignore */
@@ -1167,17 +1257,15 @@ int n_dhcp4_client_probe_dispatch_io(NDhcp4ClientProbe *probe, uint32_t events)
         switch (type) {
         case N_DHCP4_MESSAGE_OFFER:
                 r = n_dhcp4_client_probe_transition_offer(probe, message);
+                message = NULL; /* consumed */
                 if (r)
                         return r;
-                else
-                        message = NULL; /* consumed */
                 break;
         case N_DHCP4_MESSAGE_ACK:
                 r = n_dhcp4_client_probe_transition_ack(probe, message);
+                message = NULL; /* consumed */
                 if (r)
                         return r;
-                else
-                        message = NULL; /* consumed */
                 break;
         case N_DHCP4_MESSAGE_NAK:
                 r = n_dhcp4_client_probe_transition_nak(probe);
diff --git a/shared/n-dhcp4/src/n-dhcp4-client.c b/shared/n-dhcp4/src/n-dhcp4-client.c
index 5f7794fb..4fa3d65d 100644
--- a/shared/n-dhcp4/src/n-dhcp4-client.c
+++ b/shared/n-dhcp4/src/n-dhcp4-client.c
@@ -94,6 +94,9 @@ int n_dhcp4_client_config_dup(NDhcp4ClientConfig *config, NDhcp4ClientConfig **d
         dup->n_mac = config->n_mac;
         memcpy(dup->broadcast_mac, config->broadcast_mac, sizeof(dup->broadcast_mac));
         dup->n_broadcast_mac = config->n_broadcast_mac;
+        dup->log.level = config->log.level;
+        dup->log.func = config->log.func;
+        dup->log.data = config->log.data;
 
         r = n_dhcp4_client_config_set_client_id(dup,
                                                 config->client_id,
@@ -248,6 +251,15 @@ _c_public_ int n_dhcp4_client_config_set_client_id(NDhcp4ClientConfig *config, c
         return 0;
 }
 
+_c_public_ void n_dhcp4_client_config_set_log_level(NDhcp4ClientConfig *config, int level) {
+        config->log.level = level;
+}
+
+_c_public_ void n_dhcp4_client_config_set_log_func(NDhcp4ClientConfig *config, NDhcp4LogFunc func, void *data) {
+        config->log.func = func;
+        config->log.data = data;
+}
+
 /**
  * n_dhcp4_c_event_node_new() - allocate new event
  * @nodep:                      output argument for new event
@@ -376,8 +388,14 @@ _c_public_ int n_dhcp4_client_new(NDhcp4Client **clientp, NDhcp4ClientConfig *co
                 return -errno;
 
         client->fd_timer = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK);
-        if (client->fd_timer < 0)
-                return -errno;
+        if (client->fd_timer < 0) {
+                if (errno != EINVAL)
+                        return -errno;
+                client->fd_timer = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
+                if (client->fd_timer < 0)
+                        return -errno;
+                client->timerfd_is_monotonic = true;
+        }
 
         ev.data.u32 = N_DHCP4_CLIENT_EPOLL_TIMER;
         r = epoll_ctl(client->fd_epoll, EPOLL_CTL_ADD, client->fd_timer, &ev);
@@ -487,8 +505,24 @@ void n_dhcp4_client_arm_timer(NDhcp4Client *client) {
                 n_dhcp4_client_probe_get_timeout(client->current_probe, &timeout);
 
         if (timeout != client->scheduled_timeout) {
+                uint64_t scheduled_timeout = timeout;
+                int flags = TFD_TIMER_ABSTIME;
+
+                if (   timeout != 0
+                    && client->timerfd_is_monotonic) {
+                        uint64_t now;
+
+                        /* the timerfd ticks with CLOCK_MONOTONIC. Calculate and set the relative
+                         * timeout. */
+                        now = n_dhcp4_gettime(CLOCK_BOOTTIME);
+                        if (timeout <= now)
+                                timeout = 1;
+                        else
+                                timeout = timeout - now;
+                        flags = 0;
+                }
                 r = timerfd_settime(client->fd_timer,
-                                    TFD_TIMER_ABSTIME,
+                                    flags,
                                     &(struct itimerspec){
                                         .it_value = {
                                                 .tv_sec = timeout / UINT64_C(1000000000),
@@ -498,7 +532,7 @@ void n_dhcp4_client_arm_timer(NDhcp4Client *client) {
                                     NULL);
                 c_assert(r >= 0);
 
-                client->scheduled_timeout = timeout;
+                client->scheduled_timeout = scheduled_timeout;
         }
 }
 
@@ -647,7 +681,12 @@ _c_public_ int n_dhcp4_client_dispatch(NDhcp4Client *client) {
 
                                 /* continue normally */
                         } else if (r) {
-                                c_assert(r < _N_DHCP4_E_INTERNAL);
+                                if (r >= _N_DHCP4_E_INTERNAL) {
+                                        n_dhcp4_c_log(client->config, LOG_ERR,
+                                                      "invalid internal error code %d after dispatch",
+                                                      r);
+                                        return N_DHCP4_E_INTERNAL;
+                                }
                                 return r;
                         }
                 }
diff --git a/shared/n-dhcp4/src/n-dhcp4-incoming.c b/shared/n-dhcp4/src/n-dhcp4-incoming.c
index 255da458..e7234c0a 100644
--- a/shared/n-dhcp4/src/n-dhcp4-incoming.c
+++ b/shared/n-dhcp4/src/n-dhcp4-incoming.c
@@ -365,10 +365,7 @@ static int n_dhcp4_incoming_query_u32(NDhcp4Incoming *message, uint8_t option, u
 
         memcpy(&be32, data, sizeof(be32));
 
-        if (be32 == (uint32_t)-1)
-                *u32p = 0;
-        else
-                *u32p = ntohl(be32);
+        *u32p = ntohl(be32);
         return 0;
 }
 
diff --git a/shared/n-dhcp4/src/n-dhcp4-private.h b/shared/n-dhcp4/src/n-dhcp4-private.h
index c38ddbfc..fcfb0f35 100644
--- a/shared/n-dhcp4/src/n-dhcp4-private.h
+++ b/shared/n-dhcp4/src/n-dhcp4-private.h
@@ -11,6 +11,7 @@
 #include <stdlib.h>
 #include <time.h>
 #include <unistd.h>
+#include <syslog.h>
 #include "n-dhcp4.h"
 
 typedef struct NDhcp4CConnection NDhcp4CConnection;
@@ -198,6 +199,8 @@ struct NDhcp4Outgoing {
 
         struct {
                 uint8_t type;
+                uint8_t message_type;
+                uint32_t client_addr;
                 uint64_t start_time;
                 uint64_t base_time;
                 uint64_t send_time;
@@ -239,6 +242,11 @@ struct NDhcp4ClientConfig {
         size_t n_broadcast_mac;
         uint8_t *client_id;
         size_t n_client_id;
+        struct {
+                int level;
+                NDhcp4LogFunc func;
+                void *data;
+        } log;
 };
 
 #define N_DHCP4_CLIENT_CONFIG_NULL(_x) {                                        \
@@ -324,6 +332,7 @@ struct NDhcp4Client {
         uint64_t scheduled_timeout;
 
         bool preempted : 1;
+        bool timerfd_is_monotonic : 1;
 };
 
 #define N_DHCP4_CLIENT_NULL(_x) {                                               \
@@ -342,6 +351,7 @@ struct NDhcp4ClientProbe {
 
         unsigned int state;                     /* current probe state */
         uint64_t ns_deferred;                   /* timeout for deferred action */
+        uint64_t ns_reinit;
         NDhcp4ClientLease *current_lease;       /* current lease */
 
         NDhcp4CConnection connection;           /* client connection wrapper */
@@ -686,3 +696,19 @@ static inline uint64_t n_dhcp4_gettime(clockid_t clock) {
 
         return ts.tv_sec * 1000ULL * 1000ULL * 1000ULL + ts.tv_nsec;
 }
+
+#define n_dhcp4_c_log(_config, _level, ...)                                    \
+        do {                                                                   \
+                const NDhcp4ClientConfig *__config = _config;                  \
+                                                                               \
+                if (_level <= __config->log.level && __config->log.func) {     \
+                        if (1) {                                               \
+                                _config->log.func(_level,                      \
+                                                  __config->log.data,          \
+                                                  __VA_ARGS__);                \
+                        } else {                                               \
+                                /* To have the compiler check arguments */     \
+                                printf(__VA_ARGS__);                           \
+                        }                                                      \
+                }                                                              \
+        } while (0)
diff --git a/shared/n-dhcp4/src/n-dhcp4-socket.c b/shared/n-dhcp4/src/n-dhcp4-socket.c
index b9ac176f..c7e89772 100644
--- a/shared/n-dhcp4/src/n-dhcp4-socket.c
+++ b/shared/n-dhcp4/src/n-dhcp4-socket.c
@@ -195,6 +195,10 @@ int n_dhcp4_c_socket_udp_new(int *sockfdp,
         if (sockfd < 0)
                 return -errno;
 
+        r = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+        if (r < 0)
+                return -errno;
+
         r = setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
         if (r < 0)
                 return -errno;
diff --git a/shared/n-dhcp4/src/n-dhcp4.h b/shared/n-dhcp4/src/n-dhcp4.h
index 58a3cb80..d3747662 100644
--- a/shared/n-dhcp4/src/n-dhcp4.h
+++ b/shared/n-dhcp4/src/n-dhcp4.h
@@ -29,6 +29,8 @@ typedef struct NDhcp4ServerEvent NDhcp4ServerEvent;
 typedef struct NDhcp4ServerIp NDhcp4ServerIp;
 typedef struct NDhcp4ServerLease NDhcp4ServerLease;
 
+typedef void (*NDhcp4LogFunc)(int level, void *data, const char *fmt, ...);
+
 #define N_DHCP4_CLIENT_START_DELAY_RFC2131 (UINT64_C(9000))
 
 enum {
@@ -111,6 +113,8 @@ void n_dhcp4_client_config_set_request_broadcast(NDhcp4ClientConfig *config, boo
 void n_dhcp4_client_config_set_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac);
 void n_dhcp4_client_config_set_broadcast_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac);
 int n_dhcp4_client_config_set_client_id(NDhcp4ClientConfig *config, const uint8_t *id, size_t n_id);
+void n_dhcp4_client_config_set_log_level(NDhcp4ClientConfig *config, int level);
+void n_dhcp4_client_config_set_log_func(NDhcp4ClientConfig *config, NDhcp4LogFunc func, void *data);
 
 /* client-probe configs */
 
@@ -156,6 +160,8 @@ NDhcp4ClientLease *n_dhcp4_client_lease_ref(NDhcp4ClientLease *lease);
 NDhcp4ClientLease *n_dhcp4_client_lease_unref(NDhcp4ClientLease *lease);
 
 void n_dhcp4_client_lease_get_yiaddr(NDhcp4ClientLease *lease, struct in_addr *yiaddr);
+void n_dhcp4_client_lease_get_siaddr(NDhcp4ClientLease *lease, struct in_addr *siaddr);
+void n_dhcp4_client_lease_get_basetime(NDhcp4ClientLease *lease, uint64_t *ns_basetimep);
 void n_dhcp4_client_lease_get_lifetime(NDhcp4ClientLease *lease, uint64_t *ns_lifetimep);
 int n_dhcp4_client_lease_query(NDhcp4ClientLease *lease, uint8_t option, uint8_t **datap, size_t *n_datap);