diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-11-11 15:40:37 -0700 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-11-11 19:30:14 -0700 |
commit | 03c22d47ae495cde6a2e4d068646550f2a2ccfb9 (patch) | |
tree | 30715c3bcfa43c8d9f67d026bb30741e63bca704 /libdhcp | |
parent | 7987bdf22009a6578f91440a0cb5c83a5afd340c (diff) |
tidy
Diffstat (limited to 'libdhcp')
-rw-r--r-- | libdhcp/dhcp_client.c | 177 |
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"); } } |