Skip to content

Commit 69746df

Browse files
committed
- handle dhcpnak and to force the client to restart the dhcp process
- added dhcp_msg_type to parse dhcp's message type
1 parent 0e5d1c1 commit 69746df

3 files changed

Lines changed: 109 additions & 12 deletions

File tree

src/test/unit/unit.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,7 @@ Suite *wolf_suite(void)
673673
tcase_add_test(tc_proto, test_regression_syn_on_last_ack_not_silently_processed);
674674
tcase_add_test(tc_proto, test_regression_fast_recovery_cwnd_ssthresh_rfc5681);
675675
tcase_add_test(tc_proto, test_regression_paws_rejects_stale_timestamp);
676+
tcase_add_test(tc_proto, test_regression_dhcp_nak_restarts_configuration);
676677

677678
tcase_add_test(tc_utils, test_transport_checksum);
678679
tcase_add_test(tc_utils, test_iphdr_set_checksum);

src/test/unit/unit_tests_proto.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4215,4 +4215,54 @@ START_TEST(test_regression_paws_rejects_stale_timestamp)
42154215
END_TEST
42164216

42174217

4218+
/* RFC 2131 s4.4.1: if the client receives a DHCPNAK, it must restart
4219+
* the configuration process. The current code silently ignores NAKs
4220+
* during RENEWING/REBINDING because dhcp_parse_ack returns -1 and
4221+
* dhcp_poll treats that as a no-op. */
4222+
START_TEST(test_regression_dhcp_nak_restarts_configuration)
4223+
{
4224+
struct wolfIP s;
4225+
struct dhcp_msg msg;
4226+
struct dhcp_option *opt;
4227+
struct tsocket *ts;
4228+
struct ipconf *primary;
4229+
4230+
wolfIP_init(&s);
4231+
mock_link_init(&s);
4232+
wolfIP_ipconfig_set(&s, 0x0A000064U, 0xFFFFFF00U, 0);
4233+
4234+
s.dhcp_udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP);
4235+
ck_assert_int_gt(s.dhcp_udp_sd, 0);
4236+
ts = &s.udpsockets[SOCKET_UNMARK(s.dhcp_udp_sd)];
4237+
4238+
/* Simulate a RENEWING client that receives a DHCPNAK */
4239+
s.dhcp_state = DHCP_RENEWING;
4240+
s.dhcp_xid = 0x12345678U;
4241+
primary = wolfIP_primary_ipconf(&s);
4242+
ck_assert_ptr_nonnull(primary);
4243+
4244+
/* Build a minimal DHCPNAK message (type 6) */
4245+
memset(&msg, 0, sizeof(msg));
4246+
msg.op = 2; /* BOOT_REPLY */
4247+
msg.magic = ee32(DHCP_MAGIC);
4248+
msg.xid = ee32(0x12345678U);
4249+
opt = (struct dhcp_option *)msg.options;
4250+
opt->code = DHCP_OPTION_MSG_TYPE;
4251+
opt->len = 1;
4252+
opt->data[0] = 6; /* DHCPNAK */
4253+
opt = (struct dhcp_option *)((uint8_t *)opt + 3);
4254+
opt->code = DHCP_OPTION_END;
4255+
4256+
enqueue_udp_rx(ts, &msg, sizeof(msg), DHCP_SERVER_PORT);
4257+
(void)dhcp_poll(&s);
4258+
4259+
/* After receiving NAK, the client must not remain in RENEWING.
4260+
* It should restart discovery (transition to DHCP_OFF or
4261+
* DHCP_DISCOVER_SENT). */
4262+
ck_assert_int_ne(s.dhcp_state, DHCP_RENEWING);
4263+
ck_assert_int_ne(s.dhcp_state, DHCP_BOUND);
4264+
}
4265+
END_TEST
4266+
4267+
42184268
/* ----------------------------------------------------------------------- */

src/wolfip.c

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,7 @@ static int wolfIP_filter_notify_icmp(enum wolfIP_filter_reason reason,
976976
#define DHCP_OFFER 2
977977
#define DHCP_REQUEST 3
978978
#define DHCP_ACK 5
979+
#define DHCP_NAK 6
979980

980981
#define DHCP_MAGIC 0x63825363
981982
#define DHCP_SERVER_PORT 67
@@ -5387,6 +5388,42 @@ static int dhcp_parse_offer(struct wolfIP *s, struct dhcp_msg *msg, uint32_t msg
53875388
}
53885389

53895390

5391+
/* Return the DHCP message type from a validated message, or -1 on error. */
5392+
static int dhcp_msg_type(struct wolfIP *s, struct dhcp_msg *msg, uint32_t msg_len)
5393+
{
5394+
uint8_t *opt = (uint8_t *)msg->options;
5395+
uint8_t *opt_end;
5396+
if (msg_len < DHCP_HEADER_LEN)
5397+
return -1;
5398+
if (ee32(msg->magic) != DHCP_MAGIC)
5399+
return -1;
5400+
if (ee32(msg->xid) != s->dhcp_xid)
5401+
return -1;
5402+
if (msg_len - DHCP_HEADER_LEN > sizeof(msg->options))
5403+
opt_end = (uint8_t *)msg->options + sizeof(msg->options);
5404+
else
5405+
opt_end = (uint8_t *)msg->options + (msg_len - DHCP_HEADER_LEN);
5406+
while (opt < opt_end) {
5407+
uint8_t code = opt[0];
5408+
uint8_t len;
5409+
if (code == DHCP_OPTION_END)
5410+
break;
5411+
if (code == 0) {
5412+
opt++;
5413+
continue;
5414+
}
5415+
if (opt + 2 > opt_end)
5416+
break;
5417+
len = opt[1];
5418+
if (opt + 2 + len > opt_end)
5419+
break;
5420+
if (code == DHCP_OPTION_MSG_TYPE && len == 1)
5421+
return opt[2];
5422+
opt += 2 + len;
5423+
}
5424+
return -1;
5425+
}
5426+
53905427
static int dhcp_parse_ack(struct wolfIP *s, struct dhcp_msg *msg, uint32_t msg_len)
53915428
{
53925429
uint8_t *opt = (uint8_t *)msg->options;
@@ -5520,19 +5557,28 @@ static int dhcp_poll(struct wolfIP *s)
55205557
return -1;
55215558
if ((s->dhcp_state == DHCP_DISCOVER_SENT) && (dhcp_parse_offer(s, &msg, (uint32_t)len) == 0))
55225559
dhcp_send_request(s);
5523-
else if ((s->dhcp_state == DHCP_REQUEST_SENT ||
5560+
else if (s->dhcp_state == DHCP_REQUEST_SENT ||
55245561
s->dhcp_state == DHCP_RENEWING ||
5525-
s->dhcp_state == DHCP_REBINDING) &&
5526-
(dhcp_parse_ack(s, &msg, (uint32_t)len) == 0)) {
5527-
struct ipconf *primary = wolfIP_primary_ipconf(s);
5528-
LOG("DHCP configuration received.\n");
5529-
if (primary) {
5530-
LOG("IP Address: %u.%u.%u.%u\n", (unsigned int)((primary->ip >> 24) & 0xFF), (unsigned int)((primary->ip >> 16) & 0xFF), (unsigned int)((primary->ip >> 8) & 0xFF), (unsigned int)((primary->ip >> 0) & 0xFF));
5531-
LOG("Subnet Mask: %u.%u.%u.%u\n", (unsigned int)((primary->mask >> 24) & 0xFF), (unsigned int)((primary->mask >> 16) & 0xFF), (unsigned int)((primary->mask >> 8) & 0xFF), (unsigned int)((primary->mask >> 0) & 0xFF));
5532-
LOG("Gateway: %u.%u.%u.%u\n", (unsigned int)((primary->gw >> 24) & 0xFF), (unsigned int)((primary->gw >> 16) & 0xFF), (unsigned int)((primary->gw >> 8) & 0xFF), (unsigned int)((primary->gw >> 0) & 0xFF));
5533-
}
5534-
if (s->dns_server)
5535-
LOG("DNS Server: %u.%u.%u.%u\n", (unsigned int)((s->dns_server >> 24) & 0xFF), (unsigned int)((s->dns_server >> 16) & 0xFF), (unsigned int)((s->dns_server >> 8) & 0xFF), (unsigned int)((s->dns_server >> 0) & 0xFF));
5562+
s->dhcp_state == DHCP_REBINDING) {
5563+
/* RFC 2131 s4.4.1: if the client receives a DHCPNAK,
5564+
* it must restart the configuration process. */
5565+
if (dhcp_msg_type(s, &msg, (uint32_t)len) == DHCP_NAK) {
5566+
dhcp_cancel_timer(s);
5567+
s->dhcp_state = DHCP_OFF;
5568+
dhcp_send_discover(s);
5569+
return 0;
5570+
}
5571+
if (dhcp_parse_ack(s, &msg, (uint32_t)len) == 0) {
5572+
struct ipconf *primary = wolfIP_primary_ipconf(s);
5573+
LOG("DHCP configuration received.\n");
5574+
if (primary) {
5575+
LOG("IP Address: %u.%u.%u.%u\n", (unsigned int)((primary->ip >> 24) & 0xFF), (unsigned int)((primary->ip >> 16) & 0xFF), (unsigned int)((primary->ip >> 8) & 0xFF), (unsigned int)((primary->ip >> 0) & 0xFF));
5576+
LOG("Subnet Mask: %u.%u.%u.%u\n", (unsigned int)((primary->mask >> 24) & 0xFF), (unsigned int)((primary->mask >> 16) & 0xFF), (unsigned int)((primary->mask >> 8) & 0xFF), (unsigned int)((primary->mask >> 0) & 0xFF));
5577+
LOG("Gateway: %u.%u.%u.%u\n", (unsigned int)((primary->gw >> 24) & 0xFF), (unsigned int)((primary->gw >> 16) & 0xFF), (unsigned int)((primary->gw >> 8) & 0xFF), (unsigned int)((primary->gw >> 0) & 0xFF));
5578+
}
5579+
if (s->dns_server)
5580+
LOG("DNS Server: %u.%u.%u.%u\n", (unsigned int)((s->dns_server >> 24) & 0xFF), (unsigned int)((s->dns_server >> 16) & 0xFF), (unsigned int)((s->dns_server >> 8) & 0xFF), (unsigned int)((s->dns_server >> 0) & 0xFF));
5581+
}
55365582
}
55375583
return 0;
55385584
}

0 commit comments

Comments
 (0)