summaryrefslogtreecommitdiff
path: root/libdhcp
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-11-11 15:40:37 -0700
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-11-11 19:30:14 -0700
commit03c22d47ae495cde6a2e4d068646550f2a2ccfb9 (patch)
tree30715c3bcfa43c8d9f67d026bb30741e63bca704 /libdhcp
parent7987bdf22009a6578f91440a0cb5c83a5afd340c (diff)
tidy
Diffstat (limited to 'libdhcp')
-rw-r--r--libdhcp/dhcp_client.c177
1 files changed, 70 insertions, 107 deletions
diff --git a/libdhcp/dhcp_client.c b/libdhcp/dhcp_client.c
index ff9a0e9..d4c849c 100644
--- a/libdhcp/dhcp_client.c
+++ b/libdhcp/dhcp_client.c
@@ -116,7 +116,6 @@
#define CONFIG_DHCP_CAN_RECV_UNICAST_IP_WITHOUT_IP false
#endif
-
/* Implementation *************************************************************/
#if CONFIG_DHCP_DEBUG
@@ -126,18 +125,6 @@
#define debugf(fmt, ...) ((void)0)
#endif
-#define mem_encode(dst, obj) ({ \
- typeof(obj) _obj = obj; \
- memcpy(dst, &_obj, sizeof(_obj)); \
- sizeof(_obj); \
-})
-
-static inline size_t strn_encode(void *dst, char *str, size_t n) {
- size_t len = strnlen(str, n);
- memcpy(dst, str, len);
- return len;
-}
-
enum requirement {
MUST,
MUST_NOT,
@@ -145,7 +132,7 @@ enum requirement {
SHOULD_NOT,
MAY,
- IT_DEPENDS,
+ _SHOULD_NOT_HAPPEN,
};
struct dhcp_client {
@@ -155,7 +142,7 @@ struct dhcp_client {
struct net_eth_addr self_eth_addr;
char self_hostname[63];
- /* Lease. */
+ /* Mutable. */
enum {
STATE_INIT,
STATE_SELECTING,
@@ -166,7 +153,8 @@ struct dhcp_client {
STATE_INIT_REBOOT,
STATE_REBOOTING,
- } state;
+ } state;
+ uint8_t last_sent_msgtyp;
/* Lifetime of xid:
* 1. initial : [INIT]->DHCPDISCOVER->DHCPOFFER->DHCPREQUEST-+->DHCPNAK-------------->[INIT]
* |->DHCPACK->DHCPDECLINE->[INIT]
@@ -197,13 +185,10 @@ struct dhcp_client {
* @param client->lease_server_id (sometimes)
* @param client->sock
*
+ * @return client->last_sent_msgtyp
* @return whether there was an error
*/
static bool dhcp_client_send(struct dhcp_client *client, uint8_t msgtyp, char *errstr) {
- struct dhcp_msg msg = {0};
- size_t optlen = 0;
- bool server_broadcasts, client_broadcasts;
-
/**********************************************************************\
* Preconditions *
\**********************************************************************/
@@ -219,20 +204,18 @@ static bool dhcp_client_send(struct dhcp_client *client, uint8_t msgtyp, char *e
client->state == STATE_INIT_REBOOT || /* variant initial selection */
client->state == STATE_BOUND || /* T1 expired, start renew */
client->state == STATE_RENEWING ); /* T2 expired, start rebind */
-#define IMPOSSIBLE_REQUEST_CASES \
- case STATE_INIT: \
- case STATE_REQUESTING: \
- case STATE_REBINDING: \
- case STATE_REBOOTING: \
- assert(msgtyp == DHCP_MSGTYP_REQUEST); \
- assert_notreached("invalid client state for sending DHCPREQUEST"); \
- default: \
- assert_notreached("invalid client state");
+#define IMPOSSIBLE_REQUEST_STATES \
+ STATE_INIT: \
+ case STATE_REQUESTING: \
+ case STATE_REBINDING: \
+ case STATE_REBOOTING
/**********************************************************************\
* Setup *
\**********************************************************************/
+ bool server_broadcasts, client_broadcasts;
+
server_broadcasts = !CONFIG_DHCP_CAN_RECV_UNICAST_IP_WITHOUT_IP;
if (msgtyp == DHCP_MSGTYP_REQUEST &&
(client->state == STATE_BOUND || client->state == STATE_RENEWING))
@@ -249,9 +232,14 @@ static bool dhcp_client_send(struct dhcp_client *client, uint8_t msgtyp, char *e
}
/**********************************************************************\
- * Table at https://datatracker.ietf.org/doc/html/rfc2131#page-37 *
+ * Build the message *
\**********************************************************************/
+ struct dhcp_msg msg = {0};
+ size_t optlen = 0;
+
+ /* Base structure.
+ * https://datatracker.ietf.org/doc/html/rfc2131#page-37 */
msg.op = DHCP_OP_BOOTREQUEST;
msg.htype = DHCP_HTYPE_ETHERNET;
msg.hlen = sizeof(client->self_eth_addr);
@@ -280,7 +268,10 @@ static bool dhcp_client_send(struct dhcp_client *client, uint8_t msgtyp, char *e
* DHCPREQUEST is never sent in the REBINDING
* state. */
msg.ciaddr = client->lease_client_addr; break;
- IMPOSSIBLE_REQUEST_CASES
+ case IMPOSSIBLE_REQUEST_STATES:
+ assert_notreached("invalid client state for sending DHCPREQUEST");
+ default:
+ assert_notreached("invalid client state");
} break;
case DHCP_MSGTYP_DECLINE: msg.ciaddr = net_ip4_addr_zero; break;
case DHCP_MSGTYP_RELEASE: msg.ciaddr = client->lease_client_addr; break;
@@ -288,49 +279,61 @@ static bool dhcp_client_send(struct dhcp_client *client, uint8_t msgtyp, char *e
msg.yiaddr = net_ip4_addr_zero; /* only set by servers */
msg.siaddr = net_ip4_addr_zero; /* only set by servers */
msg.giaddr = net_ip4_addr_zero; /* only set by relays */
- mem_encode(msg.chaddr, client->self_eth_addr);
+ memcpy(msg.chaddr, client->self_eth_addr.octets, sizeof(client->self_eth_addr));
/* msg.sname = "options, if indicated in 'sname/file' option'; otherwise unused"; */
/* msg.file = "options, if indicated in 'sname/file' option'; otherwise unused"; */
- /**********************************************************************\
- * https://datatracker.ietf.org/doc/html/rfc2131#section-4.1 *
- \**********************************************************************/
-
+ /* Magic cookie.
+ * https://datatracker.ietf.org/doc/html/rfc2131#section-4.1 */
msg.options[optlen++] = dhcp_magic_cookie[0];
msg.options[optlen++] = dhcp_magic_cookie[1];
msg.options[optlen++] = dhcp_magic_cookie[2];
msg.options[optlen++] = dhcp_magic_cookie[3];
- /**********************************************************************\
- * Table at https://datatracker.ietf.org/doc/html/rfc2131#page-38 *
- \**********************************************************************/
-
-#define V(x) sizeof(x), &(x)
-#define NONE 0, NULL
-
+ /* Options. */
static uint8_t parameter_request_list[] = {
DHCP_OPT_SUBNET_MASK,
DHCP_OPT_ROUTER,
DHCP_OPT_RENEWAL_TIME,
DHCP_OPT_REBINDING_TIME,
};
-
const struct {
bool have_cols;
enum requirement cols[5];
size_t val_len;
void *val_ptr;
} options[0x100] = {
- /* https://datatracker.ietf.org/doc/html/rfc2131#page-38 */
+#define V(x) sizeof(x), &(x)
+#define NONE 0, NULL
+ /* Encode the table from
+ * https://datatracker.ietf.org/doc/html/rfc2131#page-38 */
+#define INC_ADDR ({ \
+ enum requirement req; \
+ switch (client->state) { \
+ case STATE_SELECTING: case STATE_INIT_REBOOT: req = MUST; break; \
+ case STATE_BOUND: case STATE_RENEWING: req = MUST_NOT; break; \
+ case IMPOSSIBLE_REQUEST_STATES: default: req = _SHOULD_NOT_HAPPEN; \
+ } \
+ req; \
+ })
+#define INC_SERVER ({ \
+ enum requirement req; \
+ switch (client->state) { \
+ case STATE_SELECTING: req = MUST; break; \
+ case STATE_INIT_REBOOT: case STATE_BOUND: case STATE_RENEWING: req = MUST_NOT; break; \
+ case IMPOSSIBLE_REQUEST_STATES: default: req = _SHOULD_NOT_HAPPEN; \
+ } \
+ req; \
+ })
/* Option DISCOVER INFORM REQUEST DECLINE RELEASE */
/* ------ ---------- ---------- ---------- -------- -------- */
- [DHCP_OPT_ADDRESS_REQUEST] = { 1, { MAY, MUST_NOT, IT_DEPENDS, MUST, MUST_NOT }, V(client->lease_client_addr) },
+ [DHCP_OPT_ADDRESS_REQUEST] = { 1, { MAY, MUST_NOT, INC_ADDR, MUST, MUST_NOT }, V(client->lease_client_addr) },
[DHCP_OPT_ADDRESS_TIME] = { 1, { MAY, MUST_NOT, MAY, MUST_NOT, MUST_NOT }, NONE },
[DHCP_OPT_OVERLOAD] = { 1, { MAY, MAY, MAY, MAY, MAY }, NONE },
[DHCP_OPT_DHCP_MSG_TYPE] = { 1, { MUST, MUST, MUST, MUST, MUST }, V(msgtyp) },
[DHCP_OPT_CLIENT_ID] = { 1, { MAY, MAY, MAY, MAY, MAY }, NONE },
[DHCP_OPT_CLASS_ID] = { 1, { MAY, MAY, MAY, MUST_NOT, MUST_NOT }, NONE },
- [DHCP_OPT_DHCP_SERVER_ID] = { 1, { MUST_NOT, MUST_NOT, IT_DEPENDS, MUST, MUST }, V(client->lease_server_id) },
+ [DHCP_OPT_DHCP_SERVER_ID] = { 1, { MUST_NOT, MUST_NOT, INC_SERVER, MUST, MUST }, V(client->lease_server_id) },
[DHCP_OPT_PARAMETER_LIST] = { 1, { MAY, MAY, MAY, MUST_NOT, MUST_NOT }, V(parameter_request_list) },
[DHCP_OPT_DHCP_MAX_MSG_SIZE] = { 1, { MAY, MAY, MAY, MUST_NOT, MUST_NOT },
CONFIG_DHCP_OPT_SIZE > DHCP_MSG_MIN_MAX_OPT_SIZE ? 2 : 0,
@@ -355,37 +358,6 @@ static bool dhcp_client_send(struct dhcp_client *client, uint8_t msgtyp, char *e
enum requirement req = options[opt].have_cols
? options[opt].cols[col]
: options[0].cols[col];
- if (req == IT_DEPENDS) {
- switch (opt) {
- case DHCP_OPT_ADDRESS_REQUEST:
- assert(msgtyp == DHCP_MSGTYP_REQUEST);
- switch (client->state) {
- case STATE_SELECTING: case STATE_INIT_REBOOT:
- req = MUST;
- break;
- case STATE_BOUND: case STATE_RENEWING:
- req = MUST_NOT;
- break;
- IMPOSSIBLE_REQUEST_CASES
- }
- break;
- case DHCP_OPT_DHCP_SERVER_ID:
- assert(msgtyp == DHCP_MSGTYP_REQUEST);
- switch (client->state) {
- case STATE_SELECTING:
- req = MUST;
- break;
- case STATE_INIT_REBOOT: case STATE_BOUND: case STATE_RENEWING:
- req = MUST_NOT;
- break;
- IMPOSSIBLE_REQUEST_CASES
- }
- break;
- default:
- assert_notreached("unexpected IT_DEPENDS");
- }
- assert(req != IT_DEPENDS);
- }
switch (req) {
case MUST_NOT:
/* Do nothing. */
@@ -404,37 +376,35 @@ static bool dhcp_client_send(struct dhcp_client *client, uint8_t msgtyp, char *e
optlen += options[opt].val_len;
}
break;
- case IT_DEPENDS:
- assert_notreached("IT_DEPENDS should have been translated already");
+ case _SHOULD_NOT_HAPPEN:
+ assert_notreached("bad table");
}
}
-
- /**********************************************************************\
- * https://datatracker.ietf.org/doc/html/rfc2131#section-4.1 *
- \**********************************************************************/
-
msg.options[optlen++] = DHCP_OPT_END;
assert(optlen <= CONFIG_DHCP_OPT_SIZE);
- /* Send. */
+ /**********************************************************************\
+ * Send *
+ \**********************************************************************/
ssize_t r = VCALL(client->sock, sendto, &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);
return true;
}
+ client->last_sent_msgtyp = msgtyp;
return false;
}
struct dhcp_recv_msg {
- struct dhcp_msg raw;
+ struct dhcp_msg raw;
struct {
- uint16_t len;
- uint8_t *dat;
- } options[0x100];
- uint8_t _option_data[sizeof((struct dhcp_msg){}.options)+
- sizeof((struct dhcp_msg){}.file)+
- sizeof((struct dhcp_msg){}.sname)];
+ uint16_t len;
+ uint8_t *dat;
+ } options[0x100];
+ uint8_t _option_data[sizeof((struct dhcp_msg){}.options)+
+ sizeof((struct dhcp_msg){}.file)+
+ sizeof((struct dhcp_msg){}.sname)];
};
/** @return whether there is an error */
@@ -587,15 +557,17 @@ static ssize_t dhcp_client_recv(struct dhcp_client *client, struct dhcp_recv_msg
if (optoverload & 2u)
_dhcp_client_recv_consolidate_opts(ret, ret->raw.sname, sizeof(ret->raw.sname));
/* Validate presence of options. */
- static const struct {
+ const struct {
bool have_cols;
enum requirement cols[3];
} option_req[0x100] = {
- /* https://datatracker.ietf.org/doc/html/rfc2131#page-29 */
+ /* Enocde the table from
+ * https://datatracker.ietf.org/doc/html/rfc2131#page-29 */
+#define REQ_TIME client->last_sent_msgtyp == DHCP_MSGTYP_REQUEST ? MUST : MUST_NOT
/* Option DHCPOFFER DHCPACK DHCPNAK */
/* ------ -------- ---------- -------- */
[DHCP_OPT_ADDRESS_REQUEST] = { 1, { MUST_NOT, MUST_NOT, MUST_NOT } },
- [DHCP_OPT_ADDRESS_TIME] = { 1, { MUST, IT_DEPENDS, MUST_NOT } },
+ [DHCP_OPT_ADDRESS_TIME] = { 1, { MUST, REQ_TIME, MUST_NOT } },
[DHCP_OPT_OVERLOAD] = { 1, { MAY, MAY, MUST_NOT } },
[DHCP_OPT_DHCP_MSG_TYPE] = { 1, { MUST, MUST, MUST } },
[DHCP_OPT_PARAMETER_LIST] = { 1, { MUST_NOT, MUST_NOT, MUST_NOT } },
@@ -619,15 +591,6 @@ static ssize_t dhcp_client_recv(struct dhcp_client *client, struct dhcp_recv_msg
enum requirement req = option_req[opt].have_cols
? option_req[opt].cols[col]
: option_req[0].cols[col];
- if (req == IT_DEPENDS) {
- /* "MUST" when this is a response to a DHCPREQUEST,
- * "MUST_NOT" when this is a response to a DHCPINFORM.
- * Because this client implementation never sends
- * DHCPINFORM, this must be a response to
- * DHCPREQUEST. */
- req = MUST;
- assert(req != IT_DEPENDS);
- }
switch (req) {
case MUST:
if (!ret->options[opt].len)
@@ -642,8 +605,8 @@ static ssize_t dhcp_client_recv(struct dhcp_client *client, struct dhcp_recv_msg
case MAY:
/* Do nothing. */
break;
- case IT_DEPENDS:
- assert_notreached("IT_DEPENDS should have been translated already");
+ case _SHOULD_NOT_HAPPEN:
+ assert_notreached("bad table");
}
}