diff options
Diffstat (limited to 'shared/n-dhcp4/src')
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-c-connection.c | 92 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-c-lease.c | 26 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-c-probe.c | 126 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-client.c | 49 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-incoming.c | 5 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-private.h | 26 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-socket.c | 4 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4.h | 6 |
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); |