diff options
Diffstat (limited to 'libdhcp')
-rw-r--r-- | libdhcp/CMakeLists.txt | 6 | ||||
-rw-r--r-- | libdhcp/dhcp_client.c | 100 | ||||
-rw-r--r-- | libdhcp/dhcp_common.h | 15 | ||||
-rw-r--r-- | libdhcp/include/libdhcp/client.h | 6 |
4 files changed, 80 insertions, 47 deletions
diff --git a/libdhcp/CMakeLists.txt b/libdhcp/CMakeLists.txt index 2ded1f4..dee7cb6 100644 --- a/libdhcp/CMakeLists.txt +++ b/libdhcp/CMakeLists.txt @@ -1,10 +1,10 @@ -# libdhcp/CMakeLists.txt - TODO +# libdhcp/CMakeLists.txt - A DHCP client # -# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> # SPDX-License-Identifier: AGPL-3.0-or-later add_library(libdhcp INTERFACE) -target_include_directories(libdhcp SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_include_directories(libdhcp PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) target_sources(libdhcp INTERFACE dhcp_client.c ) diff --git a/libdhcp/dhcp_client.c b/libdhcp/dhcp_client.c index a7b1d49..8ec3647 100644 --- a/libdhcp/dhcp_client.c +++ b/libdhcp/dhcp_client.c @@ -1,6 +1,6 @@ /* libdhcp/dhcp_client.c - A DHCP client * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later * * ----------------------------------------------------------------------------- @@ -87,9 +87,11 @@ #include <string.h> /* for strlen(), memcpy(), memset() */ #include <libmisc/rand.h> -#include <libmisc/vcall.h> #include <libhw/generic/alarmclock.h> +#define LOG_NAME DHCP +#include <libmisc/log.h> + #include <libdhcp/client.h> #include "dhcp_common.h" @@ -110,13 +112,6 @@ /* Implementation *************************************************************/ -#if CONFIG_DHCP_DEBUG - #include <stdio.h> - #define debugf(fmt, ...) printf(fmt "\n" __VA_OPT__(,) __VA_ARGS__) -#else - #define debugf(fmt, ...) ((void)0) -#endif - enum requirement { MUST, MUST_NOT, @@ -129,8 +124,8 @@ enum requirement { struct dhcp_client { /* Static. */ - implements_net_iface *iface; - implements_net_packet_conn *sock; + lo_interface net_iface iface; + lo_interface net_packet_conn sock; struct net_eth_addr self_eth_addr; char *self_hostname; size_t self_id_len; @@ -171,6 +166,18 @@ struct dhcp_client { }; +static const char *state_strs[] = { + [STATE_INIT] = "INIT", + [STATE_SELECTING] = "SELECTING", + [STATE_REQUESTING] = "REQUESTING", + [STATE_BOUND] = "BOUND", + [STATE_RENEWING] = "RENEWING", + [STATE_REBINDING] = "REBINDING", + + [STATE_INIT_REBOOT] = "INIT_REBOOT", + [STATE_REBOOTING] = "REBOOTING", +}; + /** * For convenience in switch blocks, a list of the states that, when * msgtyp==DHCP_MSGTYP_REQUEST, dhcp_client_send() has assert()ed the state is @@ -305,7 +312,7 @@ static bool dhcp_client_send(struct dhcp_client *client, uint8_t msgtyp, const c switch (msgtyp) { case DHCP_MSGTYP_DISCOVER: case DHCP_MSGTYP_INFORM: case DHCP_MSGTYP_REQUEST: - secs = (VCALL(bootclock, get_time_ns) - client->time_ns_init)/NS_PER_S; + secs = (LO_CALL(bootclock, get_time_ns) - client->time_ns_init)/NS_PER_S; if (!secs) /* systemd's sd-dhcp-client.c asserts that some * servers are broken and require .secs to be @@ -453,7 +460,8 @@ static bool dhcp_client_send(struct dhcp_client *client, uint8_t msgtyp, const c /**********************************************************************\ * Send * \**********************************************************************/ - ssize_t r = VCALL(client->sock, sendto, scratch_msg, DHCP_MSG_BASE_SIZE + optlen, + debugf("state %s: sending DHCP %s", state_strs[client->state], dhcp_msgtyp_str(msgtyp)); + ssize_t r = LO_CALL(client->sock, sendto, scratch_msg, DHCP_MSG_BASE_SIZE + optlen, client_broadcasts ? net_ip4_addr_broadcast : client->lease_server_id, DHCP_PORT_SERVER); if (r < 0) { debugf("error: sendto: %zd", r); @@ -570,7 +578,7 @@ static ssize_t dhcp_client_recv(struct dhcp_client *client, struct dhcp_recv_msg assert(client); ignore: - msg_len = VCALL(client->sock, recvfrom, &ret->raw, sizeof(ret->raw), &srv_addr, &srv_port); + msg_len = LO_CALL(client->sock, recvfrom, &ret->raw, sizeof(ret->raw), &srv_addr, &srv_port); if (msg_len < 0) /* msg_len is -errno */ return msg_len; @@ -693,9 +701,11 @@ static ssize_t dhcp_client_recv(struct dhcp_client *client, struct dhcp_recv_msg } /** @return true if there's a conflict, false if the addr appears to be unused */ -static bool dhcp_check_conflict(implements_net_packet_conn *sock, struct net_ip4_addr addr) { - assert(sock); - return VCALL(sock, sendto, "CHECK_IP_CONFLICT", 17, addr, 5000) != NET_ETIMEDOUT; +static bool dhcp_check_conflict(lo_interface net_packet_conn sock, struct net_ip4_addr addr) { + assert(!LO_IS_NULL(sock)); + ssize_t v = LO_CALL(sock, sendto, "CHECK_IP_CONFLICT", 17, addr, 5000); + debugf("check_ip_conflict => %zd", v); + return v != -NET_EARP_TIMEOUT; } static void dhcp_client_take_lease(struct dhcp_client *client, struct dhcp_recv_msg *msg, bool ifup) { @@ -729,12 +739,17 @@ static void dhcp_client_take_lease(struct dhcp_client *client, struct dhcp_recv_ client->lease_time_ns_t2 = (dur_ns_t2 == DHCP_INFINITY * NS_PER_S) ? 0 : client->time_ns_init + dur_ns_t2; client->lease_time_ns_end = (dur_ns_end == DHCP_INFINITY * NS_PER_S) ? 0 : client->time_ns_init + dur_ns_end; - if (ifup) - VCALL(client->iface, ifup, (struct net_iface_config){ + if (ifup) { + infof("applying configuration to "PRI_net_eth_addr, ARG_net_eth_addr(client->self_eth_addr)); + infof(":: addr = "PRI_net_ip4_addr, ARG_net_ip4_addr(client->lease_client_addr)); + infof(":: gateway_addr = "PRI_net_ip4_addr, ARG_net_ip4_addr(client->lease_auxdata.gateway_addr)); + infof(":: subnet_mask = "PRI_net_ip4_addr, ARG_net_ip4_addr(client->lease_auxdata.subnet_mask)); + LO_CALL(client->iface, ifup, (struct net_iface_config){ .addr = client->lease_client_addr, .gateway_addr = client->lease_auxdata.gateway_addr, .subnet_mask = client->lease_auxdata.subnet_mask, }); + } } static void dhcp_client_setstate(struct dhcp_client *client, @@ -745,21 +760,22 @@ static void dhcp_client_setstate(struct dhcp_client *client, client->state = newstate; } -static __attribute__((noreturn)) void dhcp_client_run(struct dhcp_client *client, struct dhcp_recv_msg *scratch_msg) { +[[noreturn]] static void dhcp_client_run(struct dhcp_client *client, struct dhcp_recv_msg *scratch_msg) { assert(client); ssize_t r; /* State transition diagram: https://datatracker.ietf.org/doc/html/rfc2131#page-35 */ for (;;) { + debugf("loop: state=%s", state_strs[client->state]); switch (client->state) { case STATE_INIT: client->xid = rand_uint63n(UINT32_MAX); - client->time_ns_init = VCALL(bootclock, get_time_ns); + client->time_ns_init = LO_CALL(bootclock, get_time_ns); dhcp_client_setstate(client, STATE_SELECTING, DHCP_MSGTYP_DISCOVER, NULL, scratch_msg); break; case STATE_SELECTING: - VCALL(client->sock, set_read_deadline, client->time_ns_init+CONFIG_DHCP_SELECTING_NS); + LO_CALL(client->sock, set_recv_deadline, client->time_ns_init+CONFIG_DHCP_SELECTING_NS); switch ((r = dhcp_client_recv(client, scratch_msg))) { case 0: switch (scratch_msg->option_dat[scratch_msg->options[DHCP_OPT_DHCP_MSG_TYPE].off]) { @@ -772,7 +788,7 @@ static __attribute__((noreturn)) void dhcp_client_run(struct dhcp_client *client /* ignore */ } break; - case -NET_ETIMEDOUT: + case -NET_ERECV_TIMEOUT: dhcp_client_setstate(client, STATE_INIT, 0, NULL, scratch_msg); break; default: @@ -781,7 +797,7 @@ static __attribute__((noreturn)) void dhcp_client_run(struct dhcp_client *client } break; case STATE_REQUESTING: - VCALL(client->sock, set_read_deadline, 0); + LO_CALL(client->sock, set_recv_deadline, 0); switch ((r = dhcp_client_recv(client, scratch_msg))) { case 0: switch (scratch_msg->option_dat[scratch_msg->options[DHCP_OPT_DHCP_MSG_TYPE].off]) { @@ -790,6 +806,8 @@ static __attribute__((noreturn)) void dhcp_client_run(struct dhcp_client *client break; case DHCP_MSGTYP_ACK: if (dhcp_check_conflict(client->sock, client->lease_client_addr)) { + debugf("IP "PRI_net_ip4_addr" is already in use", + ARG_net_ip4_addr(client->lease_client_addr)); dhcp_client_setstate(client, STATE_INIT, DHCP_MSGTYP_DECLINE, "IP is already in use", scratch_msg); } else { dhcp_client_take_lease(client, scratch_msg, true); @@ -806,12 +824,12 @@ static __attribute__((noreturn)) void dhcp_client_run(struct dhcp_client *client } break; case STATE_BOUND: - VCALL(client->sock, set_read_deadline, client->lease_time_ns_t1); + LO_CALL(client->sock, set_recv_deadline, client->lease_time_ns_t1); switch ((r = dhcp_client_recv(client, scratch_msg))) { case 0: /* discard */ break; - case -NET_ETIMEDOUT: + case -NET_ERECV_TIMEOUT: dhcp_client_setstate(client, STATE_RENEWING, DHCP_MSGTYP_REQUEST, NULL, scratch_msg); break; default: @@ -821,14 +839,14 @@ static __attribute__((noreturn)) void dhcp_client_run(struct dhcp_client *client break; case STATE_RENEWING: client->xid = rand_uint63n(UINT32_MAX); - client->time_ns_init = VCALL(bootclock, get_time_ns); + client->time_ns_init = LO_CALL(bootclock, get_time_ns); - VCALL(client->sock, set_read_deadline, client->lease_time_ns_t2); + LO_CALL(client->sock, set_recv_deadline, client->lease_time_ns_t2); switch ((r = dhcp_client_recv(client, scratch_msg))) { case 0: switch (scratch_msg->option_dat[scratch_msg->options[DHCP_OPT_DHCP_MSG_TYPE].off]) { case DHCP_MSGTYP_NAK: - VCALL(client->iface, ifdown); + LO_CALL(client->iface, ifdown); dhcp_client_setstate(client, STATE_INIT, 0, NULL, scratch_msg); break; case DHCP_MSGTYP_ACK: @@ -839,7 +857,7 @@ static __attribute__((noreturn)) void dhcp_client_run(struct dhcp_client *client /* ignore */ } break; - case -NET_ETIMEDOUT: + case -NET_ERECV_TIMEOUT: client->lease_server_id = net_ip4_addr_zero; dhcp_client_setstate(client, STATE_REBINDING, DHCP_MSGTYP_REQUEST, NULL, scratch_msg); break; @@ -849,12 +867,12 @@ static __attribute__((noreturn)) void dhcp_client_run(struct dhcp_client *client } break; case STATE_REBINDING: - VCALL(client->sock, set_read_deadline, client->lease_time_ns_end); + LO_CALL(client->sock, set_recv_deadline, client->lease_time_ns_end); switch ((r = dhcp_client_recv(client, scratch_msg))) { case 0: switch (scratch_msg->option_dat[scratch_msg->options[DHCP_OPT_DHCP_MSG_TYPE].off]) { case DHCP_MSGTYP_NAK: - VCALL(client->iface, ifdown); + LO_CALL(client->iface, ifdown); dhcp_client_setstate(client, STATE_BOUND, 0, NULL, scratch_msg); break; case DHCP_MSGTYP_ACK: @@ -865,8 +883,8 @@ static __attribute__((noreturn)) void dhcp_client_run(struct dhcp_client *client /* ignore */ } break; - case -NET_ETIMEDOUT: - VCALL(client->iface, ifdown); + case -NET_ERECV_TIMEOUT: + LO_CALL(client->iface, ifdown); dhcp_client_setstate(client, STATE_BOUND, 0, NULL, scratch_msg); break; default: @@ -883,9 +901,9 @@ static __attribute__((noreturn)) void dhcp_client_run(struct dhcp_client *client } } -__attribute__((noreturn)) void dhcp_client_main(implements_net_iface *iface, - char *self_hostname) { - assert(iface); +[[noreturn]] void dhcp_client_main(lo_interface net_iface iface, + char *self_hostname) { + assert(!LO_IS_NULL(iface)); /* Even though a client ID is optional and not meaningful for * us (the best we can do is to duplicate .chaddr), systemd's @@ -893,14 +911,14 @@ __attribute__((noreturn)) void dhcp_client_main(implements_net_iface *iface, * require it to be set. */ struct {uint8_t typ; struct net_eth_addr dat;} client_id = { DHCP_HTYPE_ETHERNET, - VCALL(iface, hwaddr), + LO_CALL(iface, hwaddr), }; struct dhcp_client client = { /* Static. */ .iface = iface, - .sock = VCALL(iface, udp_conn, DHCP_PORT_CLIENT), - .self_eth_addr = VCALL(iface, hwaddr), + .sock = LO_CALL(iface, udp_conn, DHCP_PORT_CLIENT), + .self_eth_addr = LO_CALL(iface, hwaddr), .self_hostname = self_hostname, .self_id_len = sizeof(client_id), .self_id_dat = &client_id, @@ -908,7 +926,7 @@ __attribute__((noreturn)) void dhcp_client_main(implements_net_iface *iface, /* Mutable. */ .state = STATE_INIT, }; - assert(client.sock); + assert(!LO_IS_NULL(client.sock)); struct dhcp_recv_msg scratch; diff --git a/libdhcp/dhcp_common.h b/libdhcp/dhcp_common.h index b265df3..5b51ce2 100644 --- a/libdhcp/dhcp_common.h +++ b/libdhcp/dhcp_common.h @@ -68,6 +68,7 @@ #define _LIBDHCP_DHCP_COMMON_H_ #include <libmisc/endian.h> +#include <libmisc/log.h> /* for const_byte_str() */ /* Config *********************************************************************/ @@ -302,4 +303,18 @@ static inline bool dhcp_opt_length_is_valid(uint8_t opt, uint16_t len) { #define DHCP_MSGTYP_RELEASE ((uint8_t) 7) /* RFC2132, client->server */ #define DHCP_MSGTYP_INFORM ((uint8_t) 8) /* RFC2132, client->server */ +static const char *dhcp_msgtyp_str(uint8_t typ) { + switch (typ) { + case DHCP_MSGTYP_DISCOVER: return "DHCP_MSGTYP_DISCOVER"; + case DHCP_MSGTYP_OFFER: return "DHCP_MSGTYP_OFFER"; + case DHCP_MSGTYP_REQUEST: return "DHCP_MSGTYP_REQUEST"; + case DHCP_MSGTYP_DECLINE: return "DHCP_MSGTYP_DECLINE"; + case DHCP_MSGTYP_ACK: return "DHCP_MSGTYP_ACK"; + case DHCP_MSGTYP_NAK: return "DHCP_MSGTYP_NAK"; + case DHCP_MSGTYP_RELEASE: return "DHCP_MSGTYP_RELEASE"; + case DHCP_MSGTYP_INFORM: return "DHCP_MSGTYP_INFORM"; + default: return const_byte_str(typ); + } +} + #endif /* _LIBDHCP_DHCP_COMMON_H_ */ diff --git a/libdhcp/include/libdhcp/client.h b/libdhcp/include/libdhcp/client.h index 1aa23d0..f81e9b1 100644 --- a/libdhcp/include/libdhcp/client.h +++ b/libdhcp/include/libdhcp/client.h @@ -1,6 +1,6 @@ /* libdhcp/client.h - A DHCP client * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later * * ----------------------------------------------------------------------------- @@ -69,7 +69,7 @@ #include <libhw/generic/net.h> -__attribute__((noreturn)) void dhcp_client_main(implements_net_iface *iface, - char *self_hostname); +[[noreturn]] void dhcp_client_main(lo_interface net_iface iface, + char *self_hostname); #endif /* _LIBDHCP_CLIENT_H_ */ |