udhcpc: include client-id option in DECLINEs, even if it's a custom -x 61:HEX option
client_data.vendorclass, .hostname and .fqdn probably need the same treatment:
just insert them into the list of -x opts, get rid of
if (client_data.vendorclass)
udhcp_add_binary_option(packet, client_data.vendorclass);
if (client_data.hostname)
udhcp_add_binary_option(packet, client_data.hostname);
if (client_data.fqdn)
udhcp_add_binary_option(packet, client_data.fqdn);
function old new delta
udhcp_insert_new_option - 166 +166
perform_release 171 207 +36
perform_d6_release 227 259 +32
udhcpc6_main 2558 2580 +22
init_d6_packet 103 84 -19
udhcpc_main 2585 2564 -21
attach_option 397 253 -144
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 3/3 up/down: 256/-184) Total: 72 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c
index f2d6907..684d76b 100644
--- a/networking/udhcp/common.c
+++ b/networking/udhcp/common.c
@@ -420,6 +420,43 @@
return 1;
}
+void* FAST_FUNC udhcp_insert_new_option(
+ struct option_set **opt_list,
+ unsigned code,
+ const void *buffer,
+ unsigned length,
+ bool dhcpv6)
+{
+ IF_NOT_UDHCPC6(bool dhcpv6 = 0;)
+ struct option_set *new, **curr;
+
+ log2("attaching option %02x to list", code);
+ new = xmalloc(sizeof(*new));
+ if (!dhcpv6) {
+ new->data = xmalloc(length + OPT_DATA);
+ new->data[OPT_CODE] = code;
+ new->data[OPT_LEN] = length;
+ memcpy(new->data + OPT_DATA, buffer, length);
+ } else {
+ new->data = xmalloc(length + D6_OPT_DATA);
+ new->data[D6_OPT_CODE] = code >> 8;
+ new->data[D6_OPT_CODE + 1] = code & 0xff;
+ new->data[D6_OPT_LEN] = length >> 8;
+ new->data[D6_OPT_LEN + 1] = length & 0xff;
+ memcpy(new->data + D6_OPT_DATA, buffer, length);
+ }
+
+ curr = opt_list;
+//FIXME: DHCP6 codes > 255!!
+ while (*curr && (*curr)->data[OPT_CODE] < code)
+ curr = &(*curr)->next;
+
+ new->next = *curr;
+ *curr = new;
+
+ return new->data;
+}
+
/* udhcp_str2optset:
* Parse string option representation to binary form and add it to opt_list.
* Called to parse "udhcpc -x OPTNAME:OPTVAL"
@@ -459,32 +496,8 @@
existing = udhcp_find_option(*opt_list, optflag->code);
if (!existing) {
- struct option_set *new, **curr;
-
/* make a new option */
- log2("attaching option %02x to list", optflag->code);
- new = xmalloc(sizeof(*new));
- if (!dhcpv6) {
- new->data = xmalloc(length + OPT_DATA);
- new->data[OPT_CODE] = optflag->code;
- new->data[OPT_LEN] = length;
- memcpy(new->data + OPT_DATA, buffer, length);
- } else {
- new->data = xmalloc(length + D6_OPT_DATA);
- new->data[D6_OPT_CODE] = optflag->code >> 8;
- new->data[D6_OPT_CODE + 1] = optflag->code & 0xff;
- new->data[D6_OPT_LEN] = length >> 8;
- new->data[D6_OPT_LEN + 1] = length & 0xff;
- memcpy(new->data + D6_OPT_DATA, buffer,
- length);
- }
-
- curr = opt_list;
- while (*curr && (*curr)->data[OPT_CODE] < optflag->code)
- curr = &(*curr)->next;
-
- new->next = *curr;
- *curr = new;
+ udhcp_insert_new_option(opt_list, optflag->code, buffer, length, dhcpv6);
goto ret;
}
diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h
index cc0abd2..e5af628 100644
--- a/networking/udhcp/common.h
+++ b/networking/udhcp/common.h
@@ -319,6 +319,17 @@
/* 2nd param is "uint32_t*" */
int FAST_FUNC udhcp_str2nip(const char *str, void *arg);
+
+#if !ENABLE_UDHCPC6
+#define udhcp_insert_new_option(opt_list, code, buffer, length, dhcpv6) \
+ udhcp_insert_new_option(opt_list, code, buffer, length)
+#endif
+void* FAST_FUNC udhcp_insert_new_option(struct option_set **opt_list,
+ unsigned code,
+ const void *buffer,
+ unsigned length,
+ bool dhcpv6);
+
/* 2nd param is "struct option_set**" */
#if !ENABLE_UDHCPC6
#define udhcp_str2optset(str, arg, optflags, option_strings, dhcpv6) \
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
index 5bca4a8..c4bedb2 100644
--- a/networking/udhcp/d6_dhcpc.c
+++ b/networking/udhcp/d6_dhcpc.c
@@ -482,7 +482,6 @@
static uint8_t *init_d6_packet(struct d6_packet *packet, char type, uint32_t xid)
{
uint8_t *ptr;
- struct d6_option *clientid;
unsigned secs;
memset(packet, 0, sizeof(*packet));
@@ -503,9 +502,7 @@
*((uint16_t*)ptr) = (secs < 0xffff) ? htons(secs) : 0xffff;
ptr += 2;
- /* add CLIENTID option */
- clientid = (void*)client_data.clientid;
- return mempcpy(ptr, clientid, clientid->len + 2+2);
+ return ptr;
}
static uint8_t *add_d6_client_options(uint8_t *ptr)
@@ -593,10 +590,10 @@
struct d6_packet packet;
uint8_t *opt_ptr;
- /* Fill in: msg type, client id */
+ /* Fill in: msg type */
opt_ptr = init_d6_packet(&packet, D6_MSG_INFORMATION_REQUEST, xid);
- /* Add options:
+ /* Add options: client-id,
* "param req" option according to -O, options specified with -x
*/
opt_ptr = add_d6_client_options(opt_ptr);
@@ -693,7 +690,7 @@
uint8_t *opt_ptr;
unsigned len;
- /* Fill in: msg type, client id */
+ /* Fill in: msg type */
opt_ptr = init_d6_packet(&packet, D6_MSG_SOLICIT, xid);
/* Create new IA_NA, optionally with included IAADDR with requested IP */
@@ -726,7 +723,7 @@
opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, len);
}
- /* Add options:
+ /* Add options: client-id,
* "param req" option according to -O, options specified with -x
*/
opt_ptr = add_d6_client_options(opt_ptr);
@@ -771,7 +768,7 @@
struct d6_packet packet;
uint8_t *opt_ptr;
- /* Fill in: msg type, client id */
+ /* Fill in: msg type */
opt_ptr = init_d6_packet(&packet, D6_MSG_REQUEST, xid);
/* server id */
@@ -783,7 +780,7 @@
if (client6_data.ia_pd)
opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2);
- /* Add options:
+ /* Add options: client-id,
* "param req" option according to -O, options specified with -x
*/
opt_ptr = add_d6_client_options(opt_ptr);
@@ -844,7 +841,7 @@
struct d6_packet packet;
uint8_t *opt_ptr;
- /* Fill in: msg type, client id */
+ /* Fill in: msg type */
opt_ptr = init_d6_packet(&packet, DHCPREQUEST, xid);
/* server id */
@@ -856,7 +853,7 @@
if (client6_data.ia_pd)
opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2);
- /* Add options:
+ /* Add options: client-id,
* "param req" option according to -O, options specified with -x
*/
opt_ptr = add_d6_client_options(opt_ptr);
@@ -878,6 +875,7 @@
{
struct d6_packet packet;
uint8_t *opt_ptr;
+ struct option_set *ci;
/* Fill in: msg type, client id */
opt_ptr = init_d6_packet(&packet, D6_MSG_RELEASE, random_xid());
@@ -889,6 +887,10 @@
/* IA PD */
if (client6_data.ia_pd)
opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2);
+ /* Client-id */
+ ci = udhcp_find_option(client_data.options, D6_OPT_CLIENTID);
+ if (ci)
+ opt_ptr = mempcpy(opt_ptr, ci->data, D6_OPT_DATA + 2+2 + 6);
bb_info_msg("sending %s", "release");
return d6_send_kernel_packet_from_client_data_ifindex(
@@ -1184,7 +1186,7 @@
{
const char *str_r;
IF_FEATURE_UDHCP_PORT(char *str_P;)
- void *clientid_mac_ptr;
+ uint8_t *clientid_mac_ptr;
llist_t *list_O = NULL;
llist_t *list_x = NULL;
int tryagain_timeout = 20;
@@ -1284,22 +1286,19 @@
if (d6_read_interface(client_data.interface,
&client_data.ifindex,
&client6_data.ll_ip6,
- client_data.client_mac)
+ client_data_client_mac)
) {
return 1;
}
- /* Create client ID based on mac, set clientid_mac_ptr */
- {
- struct d6_option *clientid;
- clientid = xzalloc(2+2+2+2+6);
- clientid->code = D6_OPT_CLIENTID;
- clientid->len = 2+2+6;
- clientid->data[1] = 3; /* DUID-LL */
- clientid->data[3] = 1; /* ethernet */
- clientid_mac_ptr = clientid->data + 2+2;
- memcpy(clientid_mac_ptr, client_data.client_mac, 6);
- client_data.clientid = (void*)clientid;
+ clientid_mac_ptr = NULL;
+ if (!udhcp_find_option(client_data.options, D6_OPT_CLIENTID)) {
+ /* not set, set the default client ID */
+ client_data.clientid[1] = 3; /* DUID-LL */
+ client_data.clientid[3] = 1; /* ethernet */
+ clientid_mac_ptr = udhcp_insert_new_option(&client_data.options, D6_OPT_CLIENTID,
+ client_data.clientid, 2+2 + 6, /*dhcp6:*/ 1);
+ clientid_mac_ptr += 2+2 + 2+2; /* skip option code, len, DUID-LL, ethernet */
}
#if !BB_MMU
@@ -1386,12 +1385,13 @@
if (d6_read_interface(client_data.interface,
&client_data.ifindex,
&client6_data.ll_ip6,
- client_data.client_mac)
+ client_data_client_mac)
) {
goto ret0; /* iface is gone? */
}
- memcpy(clientid_mac_ptr, client_data.client_mac, 6);
+ if (clientid_mac_ptr)
+ memcpy(clientid_mac_ptr, client_data_client_mac, 6);
switch (client_data.state) {
case INIT_SELECTING:
@@ -1505,7 +1505,9 @@
continue;
/* case RELEASED: */
}
- /* yah, I know, *you* say it would never happen */
+ /* RELEASED state (when we got SIGUSR2) ends up here.
+ * (wait for SIGUSR1 to re-init, or for TERM, etc)
+ */
timeout = INT_MAX;
continue; /* back to main loop */
} /* if poll timed out */
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index ea06405..a06eeaa 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -612,9 +612,7 @@
secs = client_data.last_secs - client_data.first_secs;
packet->secs = (secs < 0xffff) ? htons(secs) : 0xffff;
- memcpy(packet->chaddr, client_data.client_mac, 6);
- if (client_data.clientid)
- udhcp_add_binary_option(packet, client_data.clientid);
+ memcpy(packet->chaddr, client_data_client_mac, 6);
}
static void add_client_options(struct dhcp_packet *packet)
@@ -715,7 +713,7 @@
/* Fill in: op, htype, hlen, cookie, chaddr fields,
* random xid field (we override it below),
- * client-id option (unless -C), message type option:
+ * message type option:
*/
init_packet(&packet, DHCPDISCOVER);
@@ -724,7 +722,7 @@
udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
/* Add options: maxsize,
- * optionally: hostname, fqdn, vendorclass,
+ * optionally: hostname, fqdn, vendorclass, client-id,
* "param req" option according to -O, options specified with -x
*/
add_client_options(&packet);
@@ -758,7 +756,7 @@
*/
/* Fill in: op, htype, hlen, cookie, chaddr fields,
* random xid field (we override it below),
- * client-id option (unless -C), message type option:
+ * message type option:
*/
init_packet(&packet, DHCPREQUEST);
@@ -768,7 +766,7 @@
udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
/* Add options: maxsize,
- * optionally: hostname, fqdn, vendorclass,
+ * optionally: hostname, fqdn, vendorclass, client-id,
* "param req" option according to -O, and options specified with -x
*/
add_client_options(&packet);
@@ -805,7 +803,7 @@
*/
/* Fill in: op, htype, hlen, cookie, chaddr fields,
* random xid field (we override it below),
- * client-id option (unless -C), message type option:
+ * message type option:
*/
init_packet(&packet, DHCPREQUEST);
@@ -813,7 +811,7 @@
packet.ciaddr = ciaddr;
/* Add options: maxsize,
- * optionally: hostname, fqdn, vendorclass,
+ * optionally: hostname, fqdn, vendorclass, client-id,
* "param req" option according to -O, and options specified with -x
*/
add_client_options(&packet);
@@ -837,7 +835,7 @@
struct dhcp_packet packet;
/* Fill in: op, htype, hlen, cookie, chaddr, random xid fields,
- * client-id option (unless -C), message type option:
+ * message type option:
*/
init_packet(&packet, DHCPDECLINE);
@@ -854,6 +852,8 @@
udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
+//TODO: add client-id opt?
+
bb_simple_info_msg("broadcasting decline");
return raw_bcast_from_client_data_ifindex(&packet, INADDR_ANY);
}
@@ -865,9 +865,10 @@
int send_release(uint32_t server, uint32_t ciaddr)
{
struct dhcp_packet packet;
+ struct option_set *ci;
/* Fill in: op, htype, hlen, cookie, chaddr, random xid fields,
- * client-id option (unless -C), message type option:
+ * message type option:
*/
init_packet(&packet, DHCPRELEASE);
@@ -876,6 +877,14 @@
udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
+ /* RFC 2131 section 3.1.6:
+ * If the client used a 'client identifier' when it obtained the lease,
+ * it MUST use the same 'client identifier' in the DHCPRELEASE message.
+ */
+ ci = udhcp_find_option(client_data.options, DHCP_CLIENT_ID);
+ if (ci)
+ udhcp_add_binary_option(&packet, ci->data);
+
bb_info_msg("sending %s", "release");
/* Note: normally we unicast here since "server" is not zero.
* However, there _are_ people who run "address-less" DHCP servers,
@@ -1230,7 +1239,7 @@
const char *str_V, *str_h, *str_F, *str_r;
IF_FEATURE_UDHCPC_ARPING(const char *str_a = "2000";)
IF_FEATURE_UDHCP_PORT(char *str_P;)
- void *clientid_mac_ptr;
+ uint8_t *clientid_mac_ptr;
llist_t *list_O = NULL;
llist_t *list_x = NULL;
int tryagain_timeout = 20;
@@ -1339,7 +1348,7 @@
if (udhcp_read_interface(client_data.interface,
&client_data.ifindex,
NULL,
- client_data.client_mac)
+ client_data_client_mac)
) {
return 1;
}
@@ -1347,10 +1356,11 @@
clientid_mac_ptr = NULL;
if (!(opt & OPT_C) && !udhcp_find_option(client_data.options, DHCP_CLIENT_ID)) {
/* not suppressed and not set, set the default client ID */
- client_data.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, "", 7);
- client_data.clientid[OPT_DATA] = 1; /* type: ethernet */
- clientid_mac_ptr = client_data.clientid + OPT_DATA+1;
- memcpy(clientid_mac_ptr, client_data.client_mac, 6);
+ client_data_client_mac[-1] = 1; /* type: ethernet */
+ clientid_mac_ptr = udhcp_insert_new_option(
+ &client_data.options, DHCP_CLIENT_ID,
+ client_data_client_mac - 1, 1 + 6, /*dhcp6:*/ 0);
+ clientid_mac_ptr += 3; /* skip option code, len, ethernet */
}
if (str_V[0] != '\0') {
// can drop -V, str_V, client_data.vendorclass,
@@ -1447,12 +1457,12 @@
if (udhcp_read_interface(client_data.interface,
&client_data.ifindex,
NULL,
- client_data.client_mac)
+ client_data_client_mac)
) {
goto ret0; /* iface is gone? */
}
if (clientid_mac_ptr)
- memcpy(clientid_mac_ptr, client_data.client_mac, 6);
+ memcpy(clientid_mac_ptr, client_data_client_mac, 6);
switch (client_data.state) {
case INIT_SELECTING:
@@ -1569,7 +1579,9 @@
continue;
/* case RELEASED: */
}
- /* yah, I know, *you* say it would never happen */
+ /* RELEASED state (when we got SIGUSR2) ends up here.
+ * (wait for SIGUSR1 to re-init, or for TERM, etc)
+ */
timeout = INT_MAX;
continue; /* back to main loop */
} /* if poll timed out */
@@ -1645,7 +1657,7 @@
/* Ignore packets that aren't for us */
if (packet.hlen != 6
- || memcmp(packet.chaddr, client_data.client_mac, 6) != 0
+ || memcmp(packet.chaddr, client_data_client_mac, 6) != 0
) {
//FIXME: need to also check that last 10 bytes are zero
log1("chaddr does not match%s", ", ignoring packet"); // log2?
@@ -1757,7 +1769,7 @@
if (!arpping(requested_ip,
NULL,
(uint32_t) 0,
- client_data.client_mac,
+ client_data_client_mac,
client_data.interface,
arpping_ms)
) {
diff --git a/networking/udhcp/dhcpc.h b/networking/udhcp/dhcpc.h
index 7ad01ea..a4cc188 100644
--- a/networking/udhcp/dhcpc.h
+++ b/networking/udhcp/dhcpc.h
@@ -8,7 +8,8 @@
PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
struct client_data_t {
- uint8_t client_mac[6]; /* Our mac address */
+ uint8_t clientid[2+2 + 6]; /* Our mac address (prefixed by padding used for client-id) */
+#define client_data_client_mac (client_data.clientid + 2+2)
IF_FEATURE_UDHCP_PORT(uint16_t port;)
int ifindex; /* Index number of the interface to use */
uint8_t opt_mask[256 / 8]; /* Bitmask of options to send (-O option) */
@@ -17,7 +18,6 @@
char *pidfile; /* Optionally store the process ID */
const char *script; /* User script to run at dhcp events */
struct option_set *options; /* list of DHCP options to send to server */
- uint8_t *clientid; /* Optional client id to use */
uint8_t *vendorclass; /* Optional vendor class-id to use */
uint8_t *hostname; /* Optional hostname to use */
uint8_t *fqdn; /* Optional fully qualified domain name to use */