summaryrefslogtreecommitdiff
path: root/libdhcp
diff options
context:
space:
mode:
Diffstat (limited to 'libdhcp')
-rw-r--r--libdhcp/CMakeLists.txt6
-rw-r--r--libdhcp/dhcp_client.c100
-rw-r--r--libdhcp/dhcp_common.h15
-rw-r--r--libdhcp/include/libdhcp/client.h6
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_ */