Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion src/supplicant/eap_tls_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ int eap_tls_engine_step(struct eap_tls_engine *e);
* EAP-TLS Type-Code context), which requires a wolfSSL built with
* --enable-keying-material (else the 1.3 export fails rather than returning
* a wrong MSK). Caller takes msk[0..31] as the PMK for the subsequent 4-way
* handshake; msk[32..63] becomes the EMSK (currently unused).
* handshake (RFC 5216 §2.3); msk[32..63] is the remainder of the MSK and is
* not the EMSK - the EMSK is a separate 64-octet value this function does
* not derive.
*
* Returns 0 on success.
*/
Expand Down
4 changes: 2 additions & 2 deletions src/supplicant/supplicant.c
Original file line number Diff line number Diff line change
Expand Up @@ -563,8 +563,8 @@ static int supp_handle_eap_success(struct wolfip_supplicant *s)
if (ret != 0) {
return -1;
}
/* RFC 5216: PMK = MSK[0..31]. The remaining 32 bytes form the EMSK
* and are unused in v1. */
/* RFC 5216: PMK = MSK[0..31]. The remaining 32 bytes are the unused MSK
* tail, not the EMSK. */
memcpy(s->pmk, msk, WPA_PMK_LEN);
wpa_secure_zero(msk, sizeof(msk));

Expand Down
13 changes: 13 additions & 0 deletions src/test/unit/unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,9 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_tcp_input_synack_cancels_control_rto);
tcase_add_test(tc_utils, test_tcp_rto_cb_last_ack_requeues_finack_and_arms_timer);
tcase_add_test(tc_utils, test_tcp_rto_cb_last_ack_full_txbuf_keeps_retry_budget);
tcase_add_test(tc_utils, test_tcp_rto_cb_close_wait_retransmits_data);
tcase_add_test(tc_utils, test_tcp_rto_cb_last_ack_with_data_retransmits_data);
tcase_add_test(tc_utils, test_tcp_ctrl_state_needs_rto_last_ack_waits_for_payload_drain);
tcase_add_test(tc_utils, test_tcp_ctrl_state_needs_rto_fin_wait_1_waits_for_payload_drain);
tcase_add_test(tc_utils, test_tcp_rto_cb_fin_wait_1_with_data_uses_data_recovery);
tcase_add_test(tc_utils, test_tcp_rto_cb_fin_wait_1_no_data_requeues_finack);
Expand Down Expand Up @@ -691,6 +694,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_tcp_ack_duplicate_resend_clears_sent);
tcase_add_test(tc_utils, test_tcp_ack_discards_zero_len_segment);
tcase_add_test(tc_utils, test_tcp_ack_closes_last_ack_socket);
tcase_add_test(tc_utils, test_tcp_ack_ctrl_rto_not_armed_over_blocked_data);
tcase_add_test(tc_utils, test_tcp_ack_last_seq_match_no_close);
tcase_add_test(tc_utils, test_tcp_ack_fresh_desc_updates_rtt_existing);
tcase_add_test(tc_utils, test_tcp_ack_retransmitted_desc_skips_rtt_update);
Expand Down Expand Up @@ -776,6 +780,10 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_tcp_mark_unsacked_ignores_zero_ip_len_unsent_ack_only_desc);
tcase_add_test(tc_utils, test_flush_tcp_tx_pure_ack_keeps_unacked_data_desc);
tcase_add_test(tc_utils, test_tcp_ack_parked_zero_desc_keeps_rtt_sample);
tcase_add_test(tc_utils, test_flush_tcp_tx_sends_pure_ack_behind_window_blocked_data);
tcase_add_test(tc_utils, test_flush_tcp_tx_fin_ack_stays_behind_window_blocked_data);
tcase_add_test(tc_utils, test_flush_tcp_tx_fin_held_behind_blocked_data_after_ack);
tcase_add_test(tc_utils, test_tcp_first_unsent_seq_skips_pending_retransmit);
tcase_add_test(tc_utils, test_tcp_ack_sack_blocks_clamped_and_dropped);
tcase_add_test(tc_utils, test_tcp_recv_ooo_capacity_limit);
tcase_add_test(tc_utils, test_tcp_recv_overlapping_ooo_segments_coalesce_on_consume);
Expand Down Expand Up @@ -890,6 +898,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_proto, test_icmp_try_recv_full_fifo_does_not_signal_readable);
tcase_add_test(tc_proto, test_raw_socket_recv_captures_ip_header);
tcase_add_test(tc_proto, test_raw_socket_recv_honors_bound_local_ip_and_if);
tcase_add_test(tc_proto, test_raw_socket_send_and_wildcard_bind_keep_any_if_recv);
tcase_add_test(tc_proto, test_raw_socket_send_hdrincl_respected);
tcase_add_test(tc_proto, test_raw_socket_send_builds_ip_header);
tcase_add_test(tc_proto, test_regression_raw_socket_send_ip_id_network_byte_order);
Expand Down Expand Up @@ -992,6 +1001,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_proto, test_icmp_input_dest_unreach_frag_needed_below_floor_preserves_peer_mss);
tcase_add_test(tc_proto, test_icmp_input_dest_unreach_frag_needed_larger_mtu_does_not_raise_peer_mss);
tcase_add_test(tc_proto, test_icmp_input_dest_unreach_port_unreachable_closes_syn_sent_tcp_socket);
tcase_add_test(tc_proto, test_icmp_input_dest_unreach_port_unreach_reverts_syn_rcvd_listener);
tcase_add_test(tc_proto, test_icmp_input_dest_unreach_port_unreachable_quoted_ip_options_keep_established_tcp_socket);
tcase_add_test(tc_proto, test_icmp_input_dest_unreach_port_unreachable_mismatched_orig_src_ip_ignored);
tcase_add_test(tc_proto, test_icmp_input_dest_unreach_port_unreachable_mismatched_orig_dst_ip_ignored);
Expand Down Expand Up @@ -1122,6 +1132,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_core, test_sendto_udp_short_addrlen_and_zero_dest);
tcase_add_test(tc_core, test_sendto_udp_auto_assigns_src_port);
tcase_add_test(tc_core, test_sendto_icmp_branches);
tcase_add_test(tc_core, test_sendto_icmp_preserves_peer_and_filter);
tcase_add_test(tc_core, test_recvfrom_arg_validation);
tcase_add_test(tc_core, test_recvfrom_icmp_populates_sin);
tcase_add_test(tc_core, test_setsockopt_invalid_socket);
Expand Down Expand Up @@ -1351,6 +1362,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_core, test_tcp_input_syn_rcvd_rst_bad_seq_ignored);
tcase_add_test(tc_core, test_tcp_input_syn_rcvd_rst_good_seq_reverts_to_listen);
tcase_add_test(tc_core, test_tcp_input_syn_rcvd_rst_good_seq_nonlistener_closes);
tcase_add_test(tc_core, test_listener_revert_cancels_synack_retransmit_timer);
tcase_add_test(tc_core, test_tcp_input_syn_rcvd_rst_nullcb_recv_reports_eof);
tcase_add_test(tc_core, test_tcp_input_closed_bound_rst_ignored);
tcase_add_test(tc_core, test_tcp_input_time_wait_sends_ack_on_any_segment);
Expand Down Expand Up @@ -1809,6 +1821,7 @@ Suite *wolf_suite(void)
/* --- unit_tests_forwarding.c (router build) --- */
tcase_add_test(tc_proto, test_fwd_nonfirst_frag_ttl1_silent_drop);
tcase_add_test(tc_proto, test_fwd_first_frag_ttl1_sends_ttl_exceeded);
tcase_add_test(tc_proto, test_fwd_zero_source_transit_ttl1_silent_drop);
tcase_add_test(tc_proto, test_fwd_nonfirst_frag_df_oversize_silent_drop);
tcase_add_test(tc_proto, test_fwd_first_frag_df_oversize_sends_frag_needed);
tcase_add_test(tc_proto, test_fwd_nonfirst_frag_bad_option_silent_drop);
Expand Down
47 changes: 46 additions & 1 deletion src/test/unit/unit_tests_branches.c
Original file line number Diff line number Diff line change
Expand Up @@ -797,7 +797,9 @@ START_TEST(test_sendto_icmp_branches)
ck_assert_int_eq(wolfIP_sock_sendto(&s, icmp_sd, payload, sizeof(payload),
0, (struct wolfIP_sockaddr *)&sin, sizeof(sin)),
(int)sizeof(payload));
ck_assert_uint_eq(ts->remote_ip, 0x0A000002U);
/* Per-datagram destination: the connected peer / receive filter is set
* only by connect(), so an unconnected socket stays open (remote_ip==0). */
ck_assert_uint_eq(ts->remote_ip, 0);

/* bound_local_ip mismatch -> EINVAL */
ts->bound_local_ip = 0xDEADBEEFU;
Expand All @@ -806,6 +808,49 @@ START_TEST(test_sendto_icmp_branches)
}
END_TEST

/* ---- wolfIP_sock_sendto: ICMP per-datagram dest must not clobber peer ---- */

START_TEST(test_sendto_icmp_preserves_peer_and_filter)
{
struct wolfIP s;
int icmp_sd;
struct tsocket *ts;
struct wolfIP_sockaddr_in sin;
uint8_t payload[ICMP_HEADER_LEN] = {0};

wolfIP_init(&s);
mock_link_init(&s);
wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0);
icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP);
ck_assert_int_gt(icmp_sd, 0);
ts = &s.icmpsockets[SOCKET_UNMARK(icmp_sd)];

/* sendto(A) on an unconnected socket: the datagram goes to A, but the
* socket peer / icmp_try_recv source filter must stay open. */
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ee32(0x0A00000AU);
ck_assert_int_gt(wolfIP_sock_sendto(&s, icmp_sd, payload, sizeof(payload), 0,
(struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0);
ck_assert_uint_eq(ts->remote_ip, 0);

/* connect(B) then sendto(A): the connected peer must survive the send. */
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ee32(0x0A00000BU);
ck_assert_int_eq(wolfIP_sock_connect(&s, icmp_sd,
(struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0);
ck_assert_uint_eq(ts->remote_ip, 0x0A00000BU);

memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ee32(0x0A00000AU);
ck_assert_int_gt(wolfIP_sock_sendto(&s, icmp_sd, payload, sizeof(payload), 0,
(struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0);
ck_assert_uint_eq(ts->remote_ip, 0x0A00000BU);
}
END_TEST

/* ---- wolfIP_sock_recvfrom extras ---- */

START_TEST(test_recvfrom_arg_validation)
Expand Down
165 changes: 165 additions & 0 deletions src/test/unit/unit_tests_dns_dhcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -3058,6 +3058,67 @@ START_TEST(test_icmp_input_dest_unreach_port_unreachable_closes_syn_sent_tcp_soc
}
END_TEST

/* A passive listener lives in SYN_RCVD (is_listener=1). A hard ICMP error must
* abort only the half-open connection and revert the socket to LISTEN, not
* destroy the listening endpoint. */
START_TEST(test_icmp_input_dest_unreach_port_unreach_reverts_syn_rcvd_listener)
{
struct wolfIP s;
struct tsocket *ts;
struct wolfIP_icmp_dest_unreachable_packet icmp;
struct wolfIP_tcp_wire_prefix *orig;
uint32_t frame_len;

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

ts = &s.tcpsockets[0];
memset(ts, 0, sizeof(*ts));
ts->proto = WI_IPPROTO_TCP;
ts->S = &s;
ts->sock.tcp.state = TCP_SYN_RCVD;
ts->sock.tcp.is_listener = 1;
ts->sock.tcp.snd_una = 100; /* in-flight SYN-ACK occupies snd_una */
ts->local_ip = 0x0A000001U;
ts->remote_ip = 0x0A000002U;
ts->src_port = 1234;
ts->dst_port = 4321;

memset(&icmp, 0, sizeof(icmp));
icmp.ip.src = ee32(0x0A0000FEU);
icmp.ip.dst = ee32(ts->local_ip);
icmp.ip.ttl = 64;
icmp.ip.proto = WI_IPPROTO_ICMP;
icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_DEST_UNREACH_SIZE);
icmp.type = ICMP_DEST_UNREACH;
icmp.code = ICMP_PORT_UNREACH;

orig = (struct wolfIP_tcp_wire_prefix *)icmp.orig_packet;
orig->ip.ver_ihl = 0x45;
orig->ip.proto = WI_IPPROTO_TCP;
orig->ip.src = ee32(ts->local_ip);
orig->ip.dst = ee32(ts->remote_ip);
orig->ip.len = ee16(IP_HEADER_LEN + 8U);
orig->src_port = ee16(ts->src_port);
orig->dst_port = ee16(ts->dst_port);
/* Embedded SEQ must lie in [snd_una, snd_una+1): the SYN-ACK sequence. */
orig->seq = ee32(100);

icmp.csum = ee16(icmp_checksum((struct wolfIP_icmp_packet *)&icmp,
ICMP_DEST_UNREACH_SIZE));
frame_len = (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + ICMP_DEST_UNREACH_SIZE);

icmp_input(&s, TEST_PRIMARY_IF, (struct wolfIP_ip_packet *)&icmp, frame_len);

/* The listener reverts to LISTEN and stays a usable TCP socket; it is not
* destroyed (proto kept, state LISTEN, is_listener set). */
ck_assert_uint_eq(ts->proto, WI_IPPROTO_TCP);
ck_assert_uint_eq(ts->sock.tcp.state, TCP_LISTEN);
ck_assert_uint_eq(ts->sock.tcp.is_listener, 1);
}
END_TEST

START_TEST(test_icmp_input_dest_unreach_port_unreachable_quoted_ip_options_keep_established_tcp_socket)
{
struct wolfIP s;
Expand Down Expand Up @@ -4925,6 +4986,110 @@ START_TEST(test_tcp_rto_cb_last_ack_full_txbuf_keeps_retry_budget)
}
END_TEST

/* CLOSE_WAIT carries outbound data (peer half-closed, we still send). The RTO
* must retransmit a lost payload segment, not skip the state. */
START_TEST(test_tcp_rto_cb_close_wait_retransmits_data)
{
struct wolfIP s;
struct tsocket *ts;
struct pkt_desc *desc;

wolfIP_init(&s);
ts = &s.tcpsockets[0];
memset(ts, 0, sizeof(*ts));
ts->proto = WI_IPPROTO_TCP;
ts->S = &s;
ts->sock.tcp.state = TCP_CLOSE_WAIT;
ts->sock.tcp.rto = 100;
ts->sock.tcp.snd_una = 101;
ts->sock.tcp.seq = 101;
ts->sock.tcp.bytes_in_flight = 1;
fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE);

ck_assert_int_eq(enqueue_tcp_tx(ts, 1, TCP_FLAG_PSH), 0);
desc = fifo_peek(&ts->sock.tcp.txbuf);
ck_assert_ptr_nonnull(desc);
desc->flags |= PKT_FLAG_SENT;

s.last_tick = 1000;
tcp_rto_cb(ts);

ck_assert_int_ne(desc->flags & PKT_FLAG_RETRANS, 0);
ck_assert_int_eq(desc->flags & PKT_FLAG_SENT, 0);
ck_assert_int_ne(ts->sock.tcp.tmr_rto, NO_TIMER);
}
END_TEST

/* LAST_ACK with payload still in flight: the control RTO armed by close() must
* yield to the data path, which retransmits the data. The FIN is re-queued only
* once the data drains (covered by the drained LAST_ACK tests). */
START_TEST(test_tcp_rto_cb_last_ack_with_data_retransmits_data)
{
struct wolfIP s;
struct tsocket *ts;
struct pkt_desc *desc;

wolfIP_init(&s);
ts = &s.tcpsockets[0];
memset(ts, 0, sizeof(*ts));
ts->proto = WI_IPPROTO_TCP;
ts->S = &s;
ts->sock.tcp.state = TCP_LAST_ACK;
ts->sock.tcp.rto = 100;
ts->sock.tcp.snd_una = 101;
ts->sock.tcp.seq = 101;
ts->sock.tcp.bytes_in_flight = 1;
ts->src_port = 12345;
ts->dst_port = 5001;
ts->local_ip = 0x0A000001U;
ts->remote_ip = 0x0A000002U;
fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE);

ck_assert_int_eq(enqueue_tcp_tx(ts, 1, TCP_FLAG_PSH), 0);
desc = fifo_peek(&ts->sock.tcp.txbuf);
ck_assert_ptr_nonnull(desc);
desc->flags |= PKT_FLAG_SENT;

/* close() armed the control RTO over the data timer. */
s.last_tick = 1000;
ck_assert_int_eq(tcp_ctrl_rto_start(ts, 1000), 0);

/* The control timeout has already expired: it yields to the data path and
* the outstanding payload is retransmitted now, not after a second RTO. */
tcp_rto_cb(ts);
ck_assert_uint_eq(ts->sock.tcp.ctrl_rto_active, 0);
ck_assert_int_ne(ts->sock.tcp.tmr_rto, NO_TIMER);
ck_assert_int_ne(desc->flags & PKT_FLAG_RETRANS, 0);
ck_assert_int_eq(desc->flags & PKT_FLAG_SENT, 0);
}
END_TEST

/* LAST_ACK mirrors FIN_WAIT_1: control RTO only after the payload drains. */
START_TEST(test_tcp_ctrl_state_needs_rto_last_ack_waits_for_payload_drain)
{
struct wolfIP s;
struct tsocket *ts;

wolfIP_init(&s);
ts = &s.tcpsockets[0];
memset(ts, 0, sizeof(*ts));
ts->proto = WI_IPPROTO_TCP;
ts->S = &s;
ts->sock.tcp.state = TCP_LAST_ACK;
fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE);

ts->sock.tcp.bytes_in_flight = 1;
ck_assert_int_eq(tcp_ctrl_state_needs_rto(ts), 0);

ts->sock.tcp.bytes_in_flight = 0;
ck_assert_int_eq(enqueue_tcp_tx(ts, 1, (TCP_FLAG_ACK | TCP_FLAG_PSH)), 0);
ck_assert_int_eq(tcp_ctrl_state_needs_rto(ts), 0);

fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE);
ck_assert_int_eq(tcp_ctrl_state_needs_rto(ts), 1);
}
END_TEST

START_TEST(test_tcp_ctrl_state_needs_rto_fin_wait_1_waits_for_payload_drain)
{
struct wolfIP s;
Expand Down
49 changes: 49 additions & 0 deletions src/test/unit/unit_tests_forwarding.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,55 @@ START_TEST(test_fwd_first_frag_ttl1_sends_ttl_exceeded)
}
END_TEST

/* =========================================================================
* RFC 1812 4.3.2.7: zero-network-prefix source, transit, TTL=1 - silent
* drop
* =========================================================================
* The general per-packet filter lets src=0.0.0.0 through while the DHCP
* client is unbound (for local DHCP/BOOTP traffic). A non-local (transit)
* packet with a zero source must not be relayed or used as an ICMP-error
* destination: RFC 1812 4.3.2.7 forbids an ICMP error for a packet whose
* source has a zero network prefix. Drop it in the forwarding RPF block,
* not a Time Exceeded addressed to 0.0.0.0.
*/
START_TEST(test_fwd_zero_source_transit_ttl1_silent_drop)
{
struct wolfIP s;
uint8_t frame[ETH_HEADER_LEN + IP_HEADER_LEN + 8];
struct wolfIP_ip_packet *ip = (struct wolfIP_ip_packet *)frame;
ip4 primary_ip = 0x0A000001U;
ip4 secondary_ip = 0xC0A80101U;
ip4 dest_ip = 0xC0A80155U;
static const uint8_t dest_mac[6] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};

setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip);
wolfIP_filter_set_callback(NULL, NULL);
fwd_arp_store(&s, TEST_SECOND_IF, dest_ip, dest_mac);
/* Unbound DHCP: the general zero-source filter is bypassed, so the
* forwarding RPF block is what must drop the zero-source transit packet. */
s.dhcp_state = DHCP_DISCOVER_SENT;
last_frame_sent_count = 0;

memset(frame, 0, sizeof(frame));
memcpy(ip->eth.dst, s.ll_dev[TEST_PRIMARY_IF].mac, 6);
memcpy(ip->eth.src, "\x01\x02\x03\x04\x05\x06", 6);
ip->eth.type = ee16(ETH_TYPE_IP);
ip->ver_ihl = 0x45;
ip->flags_fo = ee16(0x2000U); /* MF=1, offset=0 (first fragment) */
ip->ttl = 1;
ip->proto = WI_IPPROTO_UDP;
ip->len = ee16(IP_HEADER_LEN + 8);
ip->src = ee32(0x00010203U); /* 0.1.2.3: in 0/8, not the exact zero */
ip->dst = ee32(dest_ip);
fix_ip_checksum(ip);

ip_recv(&s, TEST_PRIMARY_IF, ip, (uint32_t)sizeof(frame));

/* Silent: no Time Exceeded addressed to 0.1.2.3, no relay. */
ck_assert_uint_eq(last_frame_sent_count, 0);
}
END_TEST

/* =========================================================================
* RFC 1812 4.3.2.7: non-first fragment, DF set, larger than egress MTU
* - silent drop
Expand Down
Loading
Loading