summary refs log tree commit diff
path: root/shared/n-dhcp4/src
diff options
context:
space:
mode:
Diffstat (limited to 'shared/n-dhcp4/src')
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-c-connection.c67
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-c-lease.c12
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-c-probe.c25
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-client.c42
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-incoming.c5
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-private.h25
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-socket.c4
-rw-r--r--shared/n-dhcp4/src/n-dhcp4.h5
8 files changed, 165 insertions, 20 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..8a2ddb36 100644
--- a/shared/n-dhcp4/src/n-dhcp4-c-connection.c
+++ b/shared/n-dhcp4/src/n-dhcp4-c-connection.c
@@ -513,6 +513,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 +691,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 +765,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 +799,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 +851,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 +894,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,9 +961,36 @@ 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;
 
         /*
@@ -1015,6 +1048,22 @@ 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),
+                              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)),
+                              inet_ntop(AF_INET, &connection->server_ip,
+                                        server_addr, sizeof(server_addr)));
+        }
+
         ++request->userdata.n_send;
         return 0;
 }
@@ -1066,6 +1115,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 +1169,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 c14a9daf..695a112a 100644
--- a/shared/n-dhcp4/src/n-dhcp4-c-lease.c
+++ b/shared/n-dhcp4/src/n-dhcp4-c-lease.c
@@ -218,6 +218,18 @@ _c_public_ void n_dhcp4_client_lease_get_siaddr(NDhcp4ClientLease *lease, struct
 }
 
 /**
+ * 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..2bced4cf 100644
--- a/shared/n-dhcp4/src/n-dhcp4-c-probe.c
+++ b/shared/n-dhcp4/src/n-dhcp4-c-probe.c
@@ -800,7 +800,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 +819,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,7 +844,8 @@ 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;
         int r;
@@ -862,7 +864,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);
 
@@ -885,7 +887,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);
 
@@ -1100,16 +1102,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 +1170,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..0bfe48ee 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;
         }
 }
 
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..1cf5f25e 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) {                                               \
@@ -686,3 +695,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 ef75eca7..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 */
 
@@ -157,6 +161,7 @@ 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);