Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
462aceb
F-11432: compare TS.Recent freshness in one byte-order domain
danielinux Sep 2, 2026
bae3679
F-12410: scan DHCP overloaded option stream file before sname
danielinux Sep 2, 2026
e932462
F-11433: build the ethernet header before raw TX filter callbacks
danielinux Sep 2, 2026
fd96740
F-12385: clear the pre-accept flag when the control RTO takes over th…
danielinux Sep 2, 2026
7968c48
F-11425: document the PEAP success ack as the 2-byte form it builds
danielinux Sep 2, 2026
e622e20
F-11426: document the MSK half order and key directions as built
danielinux Sep 2, 2026
42e3797
F-12392: drop the redundant ACK-bit retest before processing
danielinux Sep 2, 2026
e78d3d7
F-12433: do not advance the receive ACK for a FIN that changes no state
danielinux Sep 2, 2026
7c94890
F-12432: don't answer ARP requests on an unconfigured interface
danielinux Sep 2, 2026
add22bf
F-12430: bind DAD conflict detection to the probing interface
danielinux Sep 2, 2026
a4a75e9
F-12431: detect DAD conflicts from ARP requests, not just replies
danielinux Sep 2, 2026
40e1a6c
F-12387: abort the DNS query when the timeout timer cannot be scheduled
danielinux Sep 2, 2026
e706175
F-12386: skip in-use ports in the auto source-port/ICMP-id allocators
danielinux Sep 2, 2026
c16646a
F-11445: do not mark the control RTO active when the timer insert fails
danielinux Sep 2, 2026
2437fac
tcp: clear the control-RTO active flag before the re-arm insert
danielinux Sep 3, 2026
08e1b8a
tcp: scan the full port range in the source-port allocator
danielinux Sep 3, 2026
10d2582
dhcp: treat a GARP announcement as a DAD conflict
danielinux Sep 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/supplicant/eap_peap.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ int eap_peap_build_mschapv2_response(uint8_t *out, size_t out_cap,
size_t username_len,
size_t *out_len);

/* Build the trivial inner EAP-Response/MSCHAPv2 Success ack: 6 bytes,
* [Code=Resp, id, length=6 BE, type=26, opcode=Success]
* sent in reply to the server's "S=..." Success Request.
/* Build the 2-byte compressed inner MSCHAPv2 Success acknowledgment:
* [type=26, opcode=Success], sent in reply to the server's Success
* Request. eap_id is unused because PEAPv0 omits the inner EAP header.
*/
int eap_peap_build_mschapv2_ack(uint8_t *out, size_t out_cap,
uint8_t eap_id,
Expand Down
12 changes: 6 additions & 6 deletions src/supplicant/mschapv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,13 @@ int mschapv2_verify_authenticator_response(

/* Derive the 64-byte EAP-MSCHAPv2 MSK per RFC 3079.
* MasterKey = SHA1(PasswordHashHash || NTResponse || MagicConstant1)
* SendKey16 = GetAsymmetricStartKey(MasterKey, 16, server-to-client)
* RecvKey16 = GetAsymmetricStartKey(MasterKey, 16, client-to-server)
* MSK = SendKey16 || RecvKey16 || 32 zero bytes (per RFC 3748)
* SendKey16 = GetAsymmetricStartKey(MasterKey, 16, client-to-server)
* RecvKey16 = GetAsymmetricStartKey(MasterKey, 16, server-to-client)
* MSK = RecvKey16 || SendKey16 || 32 zero bytes (per RFC 3748)
*
* Note RFC 3748 sec.7.10 specifies how the EAP MSK is built from
* MSCHAPv2 keys; we follow the "client" perspective: send = MS-MPPE-
* Recv-Key, recv = MS-MPPE-Send-Key, then 32 zero bytes.
* RFC 3748 sec.7.10 builds the EAP MSK from the MSCHAPv2 keys. From the
* client perspective, client send uses the client-to-server key and
* client receive uses the server-to-client key.
*/
int mschapv2_derive_msk(const char *password, size_t pw_len,
const uint8_t nt_response[MSCHAPV2_NT_RESPONSE_LEN],
Expand Down
15 changes: 15 additions & 0 deletions src/test/unit/unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_filter_notify_tcp_metadata);
tcase_add_test(tc_utils, test_filter_notify_udp_ihl_options_metadata);
tcase_add_test(tc_utils, test_filter_notify_udp_ihl_truncated_no_overread);
tcase_add_test(tc_utils, test_filter_notify_raw_tx_eth_header_built);
tcase_add_test(tc_utils, test_filter_dispatch_no_callback);
tcase_add_test(tc_utils, test_filter_dispatch_mask_not_set);
tcase_add_test(tc_utils, test_filter_fresh_callback_consulted_before_mask_configured);
Expand All @@ -210,6 +211,9 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_sock_bind_tcp_filter_blocks);
tcase_add_test(tc_utils, test_sock_bind_tcp_port_collision_rejected);
tcase_add_test(tc_utils, test_sock_bind_udp_src_port_nonzero);
tcase_add_test(tc_utils, test_udp_auto_port_skips_in_use);
tcase_add_test(tc_utils, test_port_alloc_walks_past_long_collision_run);
tcase_add_test(tc_utils, test_port_alloc_returns_zero_when_range_exhausted);
tcase_add_test(tc_utils, test_sock_bind_udp_filter_blocks);
tcase_add_test(tc_utils, test_sock_bind_icmp_success);
tcase_add_test(tc_utils, test_sock_connect_wrong_family);
Expand Down Expand Up @@ -279,6 +283,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_ip_recv_drops_zero_source);
tcase_add_test(tc_utils, test_arp_recv_rejects_broadcast_sender);
tcase_add_test(tc_utils, test_arp_recv_rejects_multicast_sender);
tcase_add_test(tc_utils, test_arp_recv_unconfigured_if_does_not_answer);
tcase_add_test(tc_utils, test_dhcp_ack_rejects_mismatched_server_id);
tcase_add_test(tc_utils, test_udp_no_icmp_unreachable_for_broadcast_src);
tcase_add_test(tc_utils, test_udp_no_icmp_unreachable_for_multicast_src);
Expand Down Expand Up @@ -535,6 +540,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_dns_schedule_timer_initial_jitter_and_cancel);
tcase_add_test(tc_utils, test_dns_schedule_timer_caps_large_retry_shift);
tcase_add_test(tc_utils, test_dns_send_query_schedules_timeout);
tcase_add_test(tc_utils, test_dns_send_query_timer_heap_full_aborts);
tcase_add_test(tc_utils, test_dns_send_query_send_failure_clears_outstanding_state);
tcase_add_test(tc_utils, test_dns_resend_query_uses_stored_query_buffer);
tcase_add_test(tc_utils, test_dns_resend_query_fails_without_valid_socket);
Expand Down Expand Up @@ -608,6 +614,8 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_tcp_listener_preaccept_accept_reverts_port);
tcase_add_test(tc_utils, test_tcp_listener_preaccept_timeout_reverts_port);
tcase_add_test(tc_utils, test_tcp_listener_preaccept_revert_drains_connection_state);
tcase_add_test(tc_utils, test_tcp_listener_preaccept_close_rto_retransmits_finack);
tcase_add_test(tc_utils, test_tcp_fin_in_close_wait_does_not_advance_ack);
tcase_add_test(tc_utils, test_tcp_listener_revert_restores_option_baseline);
tcase_add_test(tc_utils, test_tcp_parse_sack_wraparound_block_accepted);
tcase_add_test(tc_utils, test_tcp_parse_options_stops_on_truncated_or_invalid_option_length);
Expand Down Expand Up @@ -710,6 +718,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_tcp_process_ts_no_ecr);
tcase_add_test(tc_utils, test_tcp_process_ts_future_ecr_rejected);
tcase_add_test(tc_utils, test_tcp_process_ts_ooo_segment_keeps_recent);
tcase_add_test(tc_utils, test_tcp_process_ts_recent_compare_host_order);
tcase_add_test(tc_utils, test_tcp_input_paws_ooo_does_not_poison_hole_fill);
tcase_add_test(tc_utils, test_tcp_process_ts_updates_rtt_when_set);
tcase_add_test(tc_utils, test_tcp_send_syn_advertises_sack_permitted);
Expand Down Expand Up @@ -1471,8 +1480,12 @@ Suite *wolf_suite(void)
tcase_add_test(tc_core, test_dhcp_parse_ack_inner_pad_bytes_skipped);
tcase_add_test(tc_core, test_dhcp_dad_probe_wire_format);
tcase_add_test(tc_core, test_dhcp_dad_conflict_releases_and_rediscover);
tcase_add_test(tc_core, test_dhcp_dad_request_claiming_candidate_conflict);
tcase_add_test(tc_core, test_dhcp_dad_probe_for_candidate_conflict);
tcase_add_test(tc_core, test_dhcp_dad_garp_announcement_conflict);
tcase_add_test(tc_core, test_dhcp_dad_own_mac_reply_ignored);
tcase_add_test(tc_core, test_dhcp_dad_reply_for_other_ip_ignored);
tcase_add_test(tc_core, test_dhcp_dad_reply_on_unconfigured_secondary_ignored);
tcase_add_test(tc_core, test_dhcp_dad_single_dhcp_timer_in_heap);
tcase_add_test(tc_core, test_dhcp_dad_probe_count_len_returning_driver);
tcase_add_test(tc_core, test_dhcp_decline_wire_format);
Expand Down Expand Up @@ -1667,6 +1680,8 @@ Suite *wolf_suite(void)
tcase_add_test(tc_core, test_wolfip_packetsocket_from_fd_negative_fd);
#endif /* WOLFIP_PACKET_SOCKETS */
tcase_add_test(tc_core, test_bind_port_in_use_different_ips_no_collision);
tcase_add_test(tc_core, test_tcp_ctrl_rto_start_no_timer_does_not_set_active);
tcase_add_test(tc_core, test_tcp_ctrl_rto_start_rearm_failure_clears_active);

#if WOLFIP_VLAN
/* --- unit_tests_vlan.c (30 tests for 802.1Q support) --- */
Expand Down
201 changes: 201 additions & 0 deletions src/test/unit/unit_tests_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,53 @@ START_TEST(test_filter_notify_udp_ihl_truncated_no_overread)
}
END_TEST

START_TEST(test_filter_notify_raw_tx_eth_header_built)
{
struct wolfIP s;
struct wolfIP_sockaddr_in dst;
uint8_t payload[4] = {0xDE, 0xAD, 0xBE, 0xEF};
uint8_t bcast_mac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
int raw_sd;
int ret;

wolfIP_init(&s);
mock_link_init(&s);
wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0);

filter_cb_calls = 0;
memset(&filter_last_event, 0, sizeof(filter_last_event));
wolfIP_filter_set_callback(test_filter_cb, NULL);
wolfIP_filter_set_mask(WOLFIP_FILT_MASK(WOLFIP_FILT_SENDING));

raw_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_RAW, WI_IPPROTO_UDP);
ck_assert_int_ge(raw_sd, 0);

/* Limited broadcast: the nexthop MAC is all-ones, so the flush
* path needs no ARP entry. */
memset(&dst, 0, sizeof(dst));
dst.sin_family = AF_INET;
dst.sin_addr.s_addr = ee32(0xFFFFFFFFU);

ret = wolfIP_sock_sendto(&s, raw_sd, payload, sizeof(payload), 0,
(struct wolfIP_sockaddr *)&dst, sizeof(dst));
ck_assert_int_eq(ret, (int)sizeof(payload));

(void)wolfIP_poll(&s, 0);

wolfIP_filter_set_callback(NULL, NULL);
wolfIP_sock_close(&s, raw_sd);

/* The eth event must carry the header the frame was actually sent
* with: all-ones destination, the interface MAC as source. */
ck_assert_int_ge(filter_cb_calls, 1);
ck_assert_uint_eq(filter_last_event.meta.ip_proto,
WOLFIP_FILTER_PROTO_ETH);
ck_assert_int_eq(memcmp(filter_last_event.meta.dst_mac, bcast_mac, 6), 0);
ck_assert_int_eq(memcmp(filter_last_event.meta.src_mac,
wolfIP_ll_at(&s, TEST_PRIMARY_IF)->mac, 6), 0);
}
END_TEST


START_TEST(test_filter_dispatch_no_callback)
{
Expand Down Expand Up @@ -1460,6 +1507,95 @@ START_TEST(test_sock_bind_udp_src_port_nonzero)
}
END_TEST

/* An auto-assigned UDP source port must not collide with a port already
* bound by another socket: the allocator must skip in-use ports. With the
* RNG pinned to 5000 (the bound port), the fix walks forward to 5001. */
START_TEST(test_udp_auto_port_skips_in_use)
{
struct wolfIP s;
int udp_sd1, udp_sd2;
struct tsocket *ts2;
struct wolfIP_sockaddr_in sin;
const char payload[] = "test";

wolfIP_init(&s);
mock_link_init(&s);
wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0);

/* Bind the first UDP socket to port 5000. */
udp_sd1 = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP);
ck_assert_int_gt(udp_sd1, 0);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = ee16(5000);
sin.sin_addr.s_addr = ee32(0x0A000001U);
ck_assert_int_eq(wolfIP_sock_bind(&s, udp_sd1,
(struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0);

/* Pin the RNG to 5000 (the bound port); the auto allocator must walk
* forward to 5001 instead of colliding. */
test_rand_override_enabled = 1;
test_rand_override_value = 5000U;

/* Create a second UDP socket and sendto (auto-assigns a source port). */
udp_sd2 = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP);
ck_assert_int_gt(udp_sd2, 0);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = ee16(9999);
sin.sin_addr.s_addr = ee32(0x0A000002U);
ck_assert_int_ge(wolfIP_sock_sendto(&s, udp_sd2, payload, sizeof(payload), 0,
(const struct wolfIP_sockaddr *)&sin,
sizeof(sin)), 0);
test_rand_override_enabled = 0;

/* The auto port must be 5001 (not the bound 5000). */
ts2 = &s.udpsockets[SOCKET_UNMARK(udp_sd2)];
ck_assert_uint_eq(ts2->src_port, 5001U);
}
END_TEST

/* The allocator walks the whole min_port..65535 range: a collision run
* longer than the old 16-try limit must be skipped, not returned. With the
* RNG pinned to the run start, the old loop stopped after 16 tries and
* returned an in-use port. */
START_TEST(test_port_alloc_walks_past_long_collision_run)
{
static struct tsocket arr[18];
uint16_t port;
int i;

memset(arr, 0, sizeof(arr));
/* 17 consecutive ports in use, starting at the pinned RNG start. */
for (i = 0; i < 17; i++)
arr[i].src_port = (uint16_t)(1024 + i);
test_rand_override_enabled = 1;
test_rand_override_value = 1024U;
port = port_alloc_random(arr, 18, &arr[17], IPADDR_ANY, 1024);
test_rand_override_enabled = 0;
ck_assert_uint_eq(port, 1041U);
ck_assert_int_eq(bind_port_in_use(arr, 18, &arr[17], IPADDR_ANY, port), 0);
}
END_TEST

/* When the candidate range holds no free port the allocator returns 0
* instead of a collided value: callers treat 0 as allocation failure. */
START_TEST(test_port_alloc_returns_zero_when_range_exhausted)
{
static struct tsocket arr[2];
uint16_t port;

memset(arr, 0, sizeof(arr));
/* The only candidate (min_port == 65535) is already claimed. */
arr[0].src_port = 65535;
test_rand_override_enabled = 1;
test_rand_override_value = 65535U;
port = port_alloc_random(arr, 2, &arr[1], IPADDR_ANY, 65535);
test_rand_override_enabled = 0;
ck_assert_uint_eq(port, 0U);
}
END_TEST

START_TEST(test_sock_bind_udp_filter_blocks)
{
struct wolfIP s;
Expand Down Expand Up @@ -4729,6 +4865,71 @@ START_TEST(test_arp_recv_rejects_multicast_sender)
}
END_TEST

/* An unconfigured interface (no assigned address) must not answer ARP
* requests: matching the target against a zero conf->ip let a request
* for 0.0.0.0 be answered by advertising 0.0.0.0 as the sender protocol
* address. */
START_TEST(test_arp_recv_unconfigured_if_does_not_answer)
{
struct wolfIP s;
struct arp_packet arp;
struct wolfIP_ll_dev *ll;
struct ipconf *conf;
static const uint8_t fake_mac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x03};
uint32_t frames_before;

wolfIP_init(&s);
mock_link_init(&s);
mock_link_init_idx(&s, TEST_SECOND_IF, NULL);
wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0);

conf = wolfIP_ipconf_at(&s, TEST_SECOND_IF);
ck_assert_uint_eq(conf->ip, IPADDR_ANY);

/* A request for 0.0.0.0 on the unconfigured secondary: no reply. */
ll = wolfIP_getdev_ex(&s, TEST_SECOND_IF);
memset(&arp, 0, sizeof(arp));
memcpy(arp.eth.dst, ll->mac, 6);
memcpy(arp.eth.src, fake_mac, 6);
arp.eth.type = ee16(ETH_TYPE_ARP);
arp.htype = ee16(1);
arp.ptype = ee16(0x0800);
arp.hlen = 6;
arp.plen = 4;
arp.opcode = ee16(ARP_REQUEST);
memcpy(arp.sma, fake_mac, 6);
arp.sip = ee32(0x0A000002U);
memset(arp.tma, 0, 6);
arp.tip = ee32(IPADDR_ANY);

frames_before = last_frame_sent_count;
arp_recv(&s, TEST_SECOND_IF, &arp, sizeof(arp));
ck_assert_uint_eq(last_frame_sent_count, frames_before);

/* Control: a request for the primary's IP on the configured primary
* is still answered. */
ll = wolfIP_getdev_ex(&s, TEST_PRIMARY_IF);
conf = wolfIP_ipconf_at(&s, TEST_PRIMARY_IF);
memset(&arp, 0, sizeof(arp));
memcpy(arp.eth.dst, ll->mac, 6);
memcpy(arp.eth.src, fake_mac, 6);
arp.eth.type = ee16(ETH_TYPE_ARP);
arp.htype = ee16(1);
arp.ptype = ee16(0x0800);
arp.hlen = 6;
arp.plen = 4;
arp.opcode = ee16(ARP_REQUEST);
memcpy(arp.sma, fake_mac, 6);
arp.sip = ee32(0x0A000002U);
memset(arp.tma, 0, 6);
arp.tip = ee32(conf->ip);

frames_before = last_frame_sent_count;
arp_recv(&s, TEST_PRIMARY_IF, &arp, sizeof(arp));
ck_assert_uint_eq(last_frame_sent_count, frames_before + 1);
}
END_TEST

/* Regression: arp_recv must reject ARP packets with incorrect hardware or
* protocol type fields (htype != 1, ptype != 0x0800, hlen != 6, plen != 4).
* Without validation, non-Ethernet/IPv4 ARP packets pollute the cache. */
Expand Down
Loading
Loading