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
36 changes: 36 additions & 0 deletions src/test/unit/unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_is_timer_expired_skips_zero_head);
tcase_add_test(tc_utils, test_wolfip_getdev_ex_api);
tcase_add_test(tc_utils, test_wolfip_ll_frame_mtu_enforces_minimum);
tcase_add_test(tc_utils, test_transport_capacity_helpers_cover_guard_paths);
tcase_add_test(tc_utils, test_wolfip_if_for_local_ip_single_interface_falls_back_to_zero);
tcase_add_test(tc_utils, test_wolfip_mtu_set_get_api);
tcase_add_test(tc_utils, test_wolfip_ll_at_and_ipconf_at_invalid);
tcase_add_test(tc_utils, test_ip_is_local_conf_variants);
Expand All @@ -114,16 +116,24 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_wolfip_loopback_send_paths);
tcase_add_test(tc_utils, test_wolfip_loopback_send_drops_oversize);
tcase_add_test(tc_utils, test_wolfip_loopback_send_null_container);
tcase_add_test(tc_utils, test_wolfip_loopback_send_rejects_null_args);
#endif
tcase_add_test(tc_utils, test_wolfip_send_port_unreachable_ignores_missing_link_sender);
tcase_add_test(tc_utils, test_wolfip_send_port_unreachable_non_ethernet_skips_eth_filter);
tcase_add_test(tc_utils, test_tcp_adv_win_clamps_and_applies_window_scale);
tcase_add_test(tc_utils, test_tcp_segment_acceptable_zero_window_and_overlap_cases);
tcase_add_test(tc_utils, test_wolfip_ipconfig_ex_per_interface);
tcase_add_test(tc_utils, test_wolfip_poll_executes_timers_and_callbacks);
tcase_add_test(tc_utils, test_wolfip_poll_drains_all_expired_timers_in_one_pass);
tcase_add_test(tc_utils, test_wolfip_poll_preserves_tcp_events_raised_during_callback);
tcase_add_test(tc_utils, test_wolfip_poll_limits_device_drain_to_poll_budget);
tcase_add_test(tc_utils, test_filter_notify_tcp_metadata);
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_dispatch_lock_blocks);
tcase_add_test(tc_utils, test_filter_dispatch_meta_null_initializes);
tcase_add_test(tc_utils, test_filter_socket_event_unknown_proto);
tcase_add_test(tc_utils, test_filter_socket_event_null_socket_uses_primary_defaults);
tcase_add_test(tc_utils, test_filter_socket_event_proto_variants);
tcase_add_test(tc_utils, test_filter_setters_and_get_mask);
tcase_add_test(tc_utils, test_sock_socket_errors);
Expand Down Expand Up @@ -192,6 +202,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_sock_accept_bound_local_ip_no_match);
tcase_add_test(tc_utils, test_sock_accept_starts_rto_timer);
tcase_add_test(tc_utils, test_sock_accept_initializes_snd_una);
tcase_add_test(tc_utils, test_sock_accept_clones_half_open_state_and_queues_synack);
tcase_add_test(tc_utils, test_sock_accept_synack_retransmission);
tcase_add_test(tc_utils, test_sock_accept_synack_window_not_scaled);
tcase_add_test(tc_utils, test_sock_accept_ack_transitions_to_established);
Expand Down Expand Up @@ -281,9 +292,18 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_poll_tcp_residual_window_gates_data_segment);
tcase_add_test(tc_utils, test_poll_tcp_residual_window_allows_exact_fit);
tcase_add_test(tc_utils, test_poll_tcp_zero_window_arms_persist);
tcase_add_test(tc_utils, test_tcp_persist_start_stops_when_window_reopens_or_no_unsent_payload);
tcase_add_test(tc_utils, test_tcp_persist_helpers_ignore_non_tcp_and_null_inputs);
tcase_add_test(tc_utils, test_tcp_initial_cwnd_caps_to_iw10_and_half_rwnd);
tcase_add_test(tc_utils, test_tcp_persist_cb_sends_one_byte_probe);
tcase_add_test(tc_utils, test_tcp_zero_wnd_probe_rejects_invalid_inputs_and_empty_payload);
tcase_add_test(tc_utils, test_tcp_zero_wnd_probe_skips_ack_only_segment);
tcase_add_test(tc_utils, test_tcp_zero_wnd_probe_selects_middle_byte_at_snd_una);
tcase_add_test(tc_utils, test_tcp_persist_probe_byte_matches_snd_una_offset);
tcase_add_test(tc_utils, test_tcp_zero_wnd_probe_arp_miss_requests_resolution);
tcase_add_test(tc_utils, test_tcp_rto_cb_marks_snd_una_payload_for_retransmit);
tcase_add_test(tc_utils, test_tcp_rto_cb_clears_bookkeeping_when_no_payload_pending);
tcase_add_test(tc_utils, test_tcp_rto_cb_closes_socket_when_backoff_exhausted);
tcase_add_test(tc_utils, test_tcp_input_window_reopen_stops_persist);
tcase_add_test(tc_utils, test_tcp_persist_cb_stops_when_state_invalid);
tcase_add_test(tc_utils, test_tcp_persist_cb_stops_when_window_reopens);
Expand All @@ -295,6 +315,8 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_dhcp_send_request_renewing_sets_ciaddr_and_rebind_deadline);
tcase_add_test(tc_utils, test_dhcp_send_request_rebinding_broadcasts_to_lease_expiry);
tcase_add_test(tc_utils, test_dhcp_poll_offer_and_ack);
tcase_add_test(tc_utils, test_dhcp_poll_renewing_ack_binds_client);
tcase_add_test(tc_utils, test_dhcp_poll_rebinding_ack_binds_client);
tcase_add_test(tc_utils, test_dns_callback_ptr_response);
tcase_add_test(tc_utils, test_udp_try_recv_short_frame);
tcase_add_test(tc_utils, test_udp_try_recv_filter_drop);
Expand All @@ -309,6 +331,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_dns_callback_bad_name);
tcase_add_test(tc_utils, test_dns_callback_short_header_ignored);
tcase_add_test(tc_utils, test_dns_callback_wrong_id_ignored);
tcase_add_test(tc_utils, test_dns_callback_malformed_compressed_name_aborts_query);
tcase_add_test(tc_utils, test_dns_callback_abort_clears_query_state);
tcase_add_test(tc_utils, test_dns_abort_query_null_noop);
tcase_add_test(tc_utils, test_tcp_input_ttl_zero_local_ack_still_processes);
Expand Down Expand Up @@ -356,6 +379,9 @@ Suite *wolf_suite(void)
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_resend_query_uses_stored_query_buffer);
tcase_add_test(tc_utils, test_dns_resend_query_fails_without_valid_socket);
tcase_add_test(tc_utils, test_dns_resend_query_fails_without_cached_query_buffer);
tcase_add_test(tc_utils, test_dns_resend_query_fails_with_null_stack);
tcase_add_test(tc_utils, test_dns_abort_query_clears_timer_and_query_state);
tcase_add_test(tc_utils, test_dns_timeout_retries_then_aborts_and_allows_new_query);
tcase_add_test(tc_utils, test_dns_send_query_invalid_name);
Expand All @@ -364,6 +390,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_wolfip_ip_is_multicast_variants);
tcase_add_test(tc_utils, test_wolfip_ip_is_broadcast_variants);
tcase_add_test(tc_utils, test_wolfip_ip_is_broadcast_skips_unsuitable_configs);
tcase_add_test(tc_utils, test_wolfip_ip_is_broadcast_skips_zero_mask);
tcase_add_test(tc_utils, test_tcp_rto_cb_resets_flags_and_arms_timer);
tcase_add_test(tc_utils, test_tcp_rto_cb_no_pending_resets_backoff);
tcase_add_test(tc_utils, test_tcp_rto_cb_skips_unsent_desc);
Expand Down Expand Up @@ -396,6 +423,13 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_tcp_input_syn_listen_does_not_scale_syn_window);
tcase_add_test(tc_utils, test_tcp_input_syn_sent_does_not_scale_synack_window);
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);
tcase_add_test(tc_utils, test_tcp_parse_options_returns_when_frame_has_no_option_bytes);
tcase_add_test(tc_utils, test_tcp_parse_options_parses_and_clamps_mixed_options);
tcase_add_test(tc_utils, test_tcp_parse_options_parses_mss_sack_permitted_timestamp_and_two_sack_blocks);
tcase_add_test(tc_utils, test_tcp_parse_options_ignores_unknown_option_kinds);
tcase_add_test(tc_utils, test_tcp_parse_options_caps_sack_block_count);
tcase_add_test(tc_utils, test_tcp_parse_options_ignores_known_kinds_with_wrong_lengths);
tcase_add_test(tc_utils, test_tcp_input_rst_bad_seq_ignored);
tcase_add_test(tc_utils, test_tcp_input_rst_seq_in_window_sends_ack);
tcase_add_test(tc_utils, test_tcp_input_rst_seq_in_scaled_window_sends_ack);
Expand Down Expand Up @@ -550,6 +584,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_proto, test_send_ttl_exceeded_ip_filter_drop);
tcase_add_test(tc_proto, test_send_ttl_exceeded_eth_filter_drop);
tcase_add_test(tc_proto, test_send_ttl_exceeded_no_send);
tcase_add_test(tc_proto, test_send_ttl_exceeded_non_ethernet_skips_eth_filter);
#if WOLFIP_ENABLE_FORWARDING
tcase_add_test(tc_proto, test_wolfip_forward_ttl_exceeded_short_len_does_not_send);
#endif
Expand Down Expand Up @@ -610,6 +645,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_proto, test_select_nexthop_variants);
tcase_add_test(tc_proto, test_route_for_ip_variants);
tcase_add_test(tc_proto, test_route_for_ip_dest_matches_iface_ip);
tcase_add_test(tc_proto, test_route_for_ip_matches_exact_ip_when_mask_is_zero);
tcase_add_test(tc_proto, test_route_for_ip_no_primary_index);
tcase_add_test(tc_proto, test_route_for_ip_null_stack);
tcase_add_test(tc_proto, test_route_for_ip_gw_and_nonloop_fallback);
Expand Down
221 changes: 221 additions & 0 deletions src/test/unit/unit_tests_api.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
static struct wolfIP *poll_rearm_stack;
static int poll_rearm_cb_calls;
static int poll_rearm_recv_len;
static int poll_budget_packets_left;
static int poll_budget_poll_calls;

static int test_poll_budget_ll_poll(struct wolfIP_ll_dev *dev, void *frame, uint32_t len)
{
struct wolfIP_eth_frame *eth = (struct wolfIP_eth_frame *)frame;

(void)dev;
if (len < ETH_HEADER_LEN || poll_budget_packets_left <= 0)
return 0;
memset(eth, 0, ETH_HEADER_LEN);
poll_budget_packets_left--;
poll_budget_poll_calls++;
return ETH_HEADER_LEN;
}

static void test_poll_rearm_tcp_cb(int sock_fd, uint16_t events, void *arg)
{
Expand Down Expand Up @@ -47,6 +62,34 @@ START_TEST(test_wolfip_poll_executes_timers_and_callbacks)
}
END_TEST

START_TEST(test_wolfip_poll_drains_all_expired_timers_in_one_pass)
{
struct wolfIP s;
struct wolfIP_timer tmr;

wolfIP_init(&s);
mock_link_init(&s);
timer_cb_calls = 0;

/* Multiple expired timers should all run during the same poll iteration. */
memset(&tmr, 0, sizeof(tmr));
tmr.cb = test_timer_cb;
tmr.expires = 100;
timers_binheap_insert(&s.timers, tmr);

memset(&tmr, 0, sizeof(tmr));
tmr.cb = test_timer_cb;
tmr.expires = 90;
timers_binheap_insert(&s.timers, tmr);

(void)wolfIP_poll(&s, 100);

/* The timer heap should be empty once all expired callbacks have run. */
ck_assert_int_eq(timer_cb_calls, 2);
ck_assert_uint_eq(s.timers.size, 0U);
}
END_TEST

START_TEST(test_wolfip_poll_preserves_tcp_events_raised_during_callback)
{
struct wolfIP s;
Expand Down Expand Up @@ -80,6 +123,29 @@ START_TEST(test_wolfip_poll_preserves_tcp_events_raised_during_callback)
}
END_TEST

START_TEST(test_wolfip_poll_limits_device_drain_to_poll_budget)
{
struct wolfIP s;
struct wolfIP_ll_dev *ll;

wolfIP_init(&s);
mock_link_init(&s);
ll = wolfIP_ll_at(&s, TEST_PRIMARY_IF);
ck_assert_ptr_nonnull(ll);
ll->poll = test_poll_budget_ll_poll;
ll->non_ethernet = 0;

/* Feed more frames than the scheduler budget allows in a single poll call. */
poll_budget_packets_left = WOLFIP_POLL_BUDGET + 3;
poll_budget_poll_calls = 0;
(void)wolfIP_poll(&s, 100);

/* Step 1 should stop after consuming exactly one poll budget worth of packets. */
ck_assert_int_eq(poll_budget_poll_calls, WOLFIP_POLL_BUDGET);
ck_assert_int_eq(poll_budget_packets_left, 3);
}
END_TEST

START_TEST(test_filter_notify_tcp_metadata)
{
struct wolfIP s;
Expand Down Expand Up @@ -209,6 +275,32 @@ START_TEST(test_filter_socket_event_unknown_proto)
}
END_TEST

START_TEST(test_filter_socket_event_null_socket_uses_primary_defaults)
{
struct wolfIP s;
ip4 local_ip = 0x0A000001U;
ip4 remote_ip = 0x0A000002U;

wolfIP_init(&s);
wolfIP_filter_set_callback(test_filter_cb, NULL);
wolfIP_filter_set_mask(WOLFIP_FILT_MASK(WOLFIP_FILT_CONNECTING));
filter_cb_calls = 0;
memset(&filter_last_event, 0xA5, sizeof(filter_last_event));

(void)wolfIP_filter_notify_socket_event(WOLFIP_FILT_CONNECTING, &s, NULL,
local_ip, 1234, remote_ip, 4321);

ck_assert_int_eq(filter_cb_calls, 1);
ck_assert_uint_eq(filter_last_event.if_idx, WOLFIP_PRIMARY_IF_IDX);
ck_assert_uint_eq(filter_last_event.meta.ip_proto, 0);
ck_assert_uint_eq(filter_last_event.meta.src_ip, ee32(local_ip));
ck_assert_uint_eq(filter_last_event.meta.dst_ip, ee32(remote_ip));

wolfIP_filter_set_callback(NULL, NULL);
wolfIP_filter_set_mask(0);
}
END_TEST

START_TEST(test_filter_socket_event_proto_variants)
{
struct wolfIP s;
Expand Down Expand Up @@ -2182,6 +2274,135 @@ START_TEST(test_sock_accept_initializes_snd_una)
}
END_TEST

START_TEST(test_sock_accept_clones_half_open_state_and_queues_synack)
{
struct wolfIP s;
int listen_sd;
int client_sd;
struct tsocket *listener;
struct tsocket *accepted;
struct wolfIP_sockaddr_in sin;
struct pkt_desc *desc;
struct wolfIP_tcp_seg *seg;
void *cb_arg = &s;
uint32_t pre_accept_seq;
uint32_t pre_accept_ack;
uint32_t pre_accept_last_ts;
uint32_t pre_accept_local_ip;
uint32_t pre_accept_remote_ip;
uint32_t pre_accept_peer_rwnd;
uint16_t pre_accept_peer_mss;
uint16_t pre_accept_src_port;
uint16_t pre_accept_dst_port;
uint8_t pre_accept_snd_wscale;
uint8_t pre_accept_rcv_wscale;
uint8_t pre_accept_ws_enabled;
uint8_t pre_accept_ws_offer;
uint8_t pre_accept_ts_enabled;
uint8_t pre_accept_ts_offer;
uint8_t pre_accept_sack_offer;
uint8_t pre_accept_sack_permitted;

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

listen_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP);
ck_assert_int_gt(listen_sd, 0);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = ee16(1234);
sin.sin_addr.s_addr = ee32(0x0A000001U);
ck_assert_int_eq(wolfIP_sock_bind(&s, listen_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0);
ck_assert_int_eq(wolfIP_sock_listen(&s, listen_sd, 1), 0);

listener = &s.tcpsockets[SOCKET_UNMARK(listen_sd)];
listener->callback = test_socket_cb;
listener->callback_arg = cb_arg;

/* Drive the listener into SYN_RCVD so accept() has half-open state to fork. */
inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234);
ck_assert_int_eq(listener->sock.tcp.state, TCP_SYN_RCVD);

/* Seed half-open negotiation state so accept() must clone it into the child socket. */
listener->sock.tcp.last_ts = 0x11223344U;
listener->sock.tcp.peer_rwnd = 4096;
listener->sock.tcp.peer_mss = 1200;
listener->sock.tcp.snd_wscale = 4;
listener->sock.tcp.rcv_wscale = 2;
listener->sock.tcp.ws_enabled = 1;
listener->sock.tcp.ws_offer = 1;
listener->sock.tcp.ts_enabled = 1;
listener->sock.tcp.ts_offer = 1;
listener->sock.tcp.sack_offer = 1;
listener->sock.tcp.sack_permitted = 1;

pre_accept_seq = listener->sock.tcp.seq;
pre_accept_ack = listener->sock.tcp.ack;
pre_accept_last_ts = listener->sock.tcp.last_ts;
pre_accept_local_ip = listener->local_ip;
pre_accept_remote_ip = listener->remote_ip;
pre_accept_peer_rwnd = listener->sock.tcp.peer_rwnd;
pre_accept_peer_mss = listener->sock.tcp.peer_mss;
pre_accept_src_port = listener->src_port;
pre_accept_dst_port = listener->dst_port;
pre_accept_snd_wscale = listener->sock.tcp.snd_wscale;
pre_accept_rcv_wscale = listener->sock.tcp.rcv_wscale;
pre_accept_ws_enabled = listener->sock.tcp.ws_enabled;
pre_accept_ws_offer = listener->sock.tcp.ws_offer;
pre_accept_ts_enabled = listener->sock.tcp.ts_enabled;
pre_accept_ts_offer = listener->sock.tcp.ts_offer;
pre_accept_sack_offer = listener->sock.tcp.sack_offer;
pre_accept_sack_permitted = listener->sock.tcp.sack_permitted;

/* Accept should fork the half-open state into a child socket and queue a SYN-ACK there. */
client_sd = wolfIP_sock_accept(&s, listen_sd, NULL, NULL);
ck_assert_int_gt(client_sd, 0);

accepted = &s.tcpsockets[SOCKET_UNMARK(client_sd)];
/* The child socket should inherit the negotiated transport parameters verbatim. */
ck_assert_int_eq(accepted->sock.tcp.state, TCP_SYN_RCVD);
ck_assert_ptr_eq(accepted->callback, test_socket_cb);
ck_assert_ptr_eq(accepted->callback_arg, cb_arg);
ck_assert_uint_eq(accepted->local_ip, pre_accept_local_ip);
ck_assert_uint_eq(accepted->bound_local_ip, listener->bound_local_ip);
ck_assert_uint_eq(accepted->if_idx, TEST_PRIMARY_IF);
ck_assert_uint_eq(accepted->remote_ip, pre_accept_remote_ip);
ck_assert_uint_eq(accepted->src_port, pre_accept_src_port);
ck_assert_uint_eq(accepted->dst_port, pre_accept_dst_port);
ck_assert_uint_eq(accepted->sock.tcp.ack, pre_accept_ack);
ck_assert_uint_eq(accepted->sock.tcp.snd_una, pre_accept_seq);
ck_assert_uint_eq(accepted->sock.tcp.last_ts, pre_accept_last_ts);
ck_assert_uint_eq(accepted->sock.tcp.peer_rwnd, pre_accept_peer_rwnd);
ck_assert_uint_eq(accepted->sock.tcp.peer_mss, pre_accept_peer_mss);
ck_assert_uint_eq(accepted->sock.tcp.snd_wscale, pre_accept_snd_wscale);
ck_assert_uint_eq(accepted->sock.tcp.rcv_wscale, pre_accept_rcv_wscale);
ck_assert_uint_eq(accepted->sock.tcp.ws_enabled, pre_accept_ws_enabled);
ck_assert_uint_eq(accepted->sock.tcp.ws_offer, pre_accept_ws_offer);
ck_assert_uint_eq(accepted->sock.tcp.ts_enabled, pre_accept_ts_enabled);
ck_assert_uint_eq(accepted->sock.tcp.ts_offer, pre_accept_ts_offer);
ck_assert_uint_eq(accepted->sock.tcp.sack_offer, pre_accept_sack_offer);
ck_assert_uint_eq(accepted->sock.tcp.sack_permitted, pre_accept_sack_permitted);
ck_assert_uint_eq(accepted->sock.tcp.cwnd,
tcp_initial_cwnd(pre_accept_peer_rwnd, tcp_cc_mss(accepted)));
ck_assert_uint_eq(accepted->sock.tcp.ssthresh,
tcp_initial_ssthresh(pre_accept_peer_rwnd));

desc = fifo_peek(&accepted->sock.tcp.txbuf);
ck_assert_ptr_nonnull(desc);
seg = (struct wolfIP_tcp_seg *)(accepted->txmem + desc->pos + sizeof(*desc));
/* SYN-ACK transmission must be queued on the accepted child, not the listener. */
ck_assert_uint_eq(seg->flags, (TCP_FLAG_SYN | TCP_FLAG_ACK));
ck_assert_uint_eq(ee32(seg->seq), pre_accept_seq);
ck_assert_uint_eq(ee32(seg->ack), pre_accept_ack);

/* The listener should be reset to passive-open state once the child is created. */
ck_assert_int_eq(listener->sock.tcp.state, TCP_LISTEN);
ck_assert_uint_eq(listener->sock.tcp.ctrl_rto_active, 0);
ck_assert_uint_eq(listener->events & CB_EVENT_READABLE, 0);
}
END_TEST

START_TEST(test_sock_accept_synack_retransmission)
{
struct wolfIP s;
Expand Down
Loading
Loading