|
1 | 1 | static struct wolfIP *poll_rearm_stack; |
2 | 2 | static int poll_rearm_cb_calls; |
3 | 3 | static int poll_rearm_recv_len; |
| 4 | +static int poll_budget_packets_left; |
| 5 | +static int poll_budget_poll_calls; |
| 6 | + |
| 7 | +static int test_poll_budget_ll_poll(struct wolfIP_ll_dev *dev, void *frame, uint32_t len) |
| 8 | +{ |
| 9 | + struct wolfIP_eth_frame *eth = (struct wolfIP_eth_frame *)frame; |
| 10 | + |
| 11 | + (void)dev; |
| 12 | + if (len < ETH_HEADER_LEN || poll_budget_packets_left <= 0) |
| 13 | + return 0; |
| 14 | + memset(eth, 0, ETH_HEADER_LEN); |
| 15 | + poll_budget_packets_left--; |
| 16 | + poll_budget_poll_calls++; |
| 17 | + return ETH_HEADER_LEN; |
| 18 | +} |
4 | 19 |
|
5 | 20 | static void test_poll_rearm_tcp_cb(int sock_fd, uint16_t events, void *arg) |
6 | 21 | { |
@@ -47,6 +62,34 @@ START_TEST(test_wolfip_poll_executes_timers_and_callbacks) |
47 | 62 | } |
48 | 63 | END_TEST |
49 | 64 |
|
| 65 | +START_TEST(test_wolfip_poll_drains_all_expired_timers_in_one_pass) |
| 66 | +{ |
| 67 | + struct wolfIP s; |
| 68 | + struct wolfIP_timer tmr; |
| 69 | + |
| 70 | + wolfIP_init(&s); |
| 71 | + mock_link_init(&s); |
| 72 | + timer_cb_calls = 0; |
| 73 | + |
| 74 | + /* Multiple expired timers should all run during the same poll iteration. */ |
| 75 | + memset(&tmr, 0, sizeof(tmr)); |
| 76 | + tmr.cb = test_timer_cb; |
| 77 | + tmr.expires = 100; |
| 78 | + timers_binheap_insert(&s.timers, tmr); |
| 79 | + |
| 80 | + memset(&tmr, 0, sizeof(tmr)); |
| 81 | + tmr.cb = test_timer_cb; |
| 82 | + tmr.expires = 90; |
| 83 | + timers_binheap_insert(&s.timers, tmr); |
| 84 | + |
| 85 | + (void)wolfIP_poll(&s, 100); |
| 86 | + |
| 87 | + /* The timer heap should be empty once all expired callbacks have run. */ |
| 88 | + ck_assert_int_eq(timer_cb_calls, 2); |
| 89 | + ck_assert_uint_eq(s.timers.size, 0U); |
| 90 | +} |
| 91 | +END_TEST |
| 92 | + |
50 | 93 | START_TEST(test_wolfip_poll_preserves_tcp_events_raised_during_callback) |
51 | 94 | { |
52 | 95 | struct wolfIP s; |
@@ -80,6 +123,29 @@ START_TEST(test_wolfip_poll_preserves_tcp_events_raised_during_callback) |
80 | 123 | } |
81 | 124 | END_TEST |
82 | 125 |
|
| 126 | +START_TEST(test_wolfip_poll_limits_device_drain_to_poll_budget) |
| 127 | +{ |
| 128 | + struct wolfIP s; |
| 129 | + struct wolfIP_ll_dev *ll; |
| 130 | + |
| 131 | + wolfIP_init(&s); |
| 132 | + mock_link_init(&s); |
| 133 | + ll = wolfIP_ll_at(&s, TEST_PRIMARY_IF); |
| 134 | + ck_assert_ptr_nonnull(ll); |
| 135 | + ll->poll = test_poll_budget_ll_poll; |
| 136 | + ll->non_ethernet = 0; |
| 137 | + |
| 138 | + /* Feed more frames than the scheduler budget allows in a single poll call. */ |
| 139 | + poll_budget_packets_left = WOLFIP_POLL_BUDGET + 3; |
| 140 | + poll_budget_poll_calls = 0; |
| 141 | + (void)wolfIP_poll(&s, 100); |
| 142 | + |
| 143 | + /* Step 1 should stop after consuming exactly one poll budget worth of packets. */ |
| 144 | + ck_assert_int_eq(poll_budget_poll_calls, WOLFIP_POLL_BUDGET); |
| 145 | + ck_assert_int_eq(poll_budget_packets_left, 3); |
| 146 | +} |
| 147 | +END_TEST |
| 148 | + |
83 | 149 | START_TEST(test_filter_notify_tcp_metadata) |
84 | 150 | { |
85 | 151 | struct wolfIP s; |
@@ -209,6 +275,32 @@ START_TEST(test_filter_socket_event_unknown_proto) |
209 | 275 | } |
210 | 276 | END_TEST |
211 | 277 |
|
| 278 | +START_TEST(test_filter_socket_event_null_socket_uses_primary_defaults) |
| 279 | +{ |
| 280 | + struct wolfIP s; |
| 281 | + ip4 local_ip = 0x0A000001U; |
| 282 | + ip4 remote_ip = 0x0A000002U; |
| 283 | + |
| 284 | + wolfIP_init(&s); |
| 285 | + wolfIP_filter_set_callback(test_filter_cb, NULL); |
| 286 | + wolfIP_filter_set_mask(WOLFIP_FILT_MASK(WOLFIP_FILT_CONNECTING)); |
| 287 | + filter_cb_calls = 0; |
| 288 | + memset(&filter_last_event, 0xA5, sizeof(filter_last_event)); |
| 289 | + |
| 290 | + (void)wolfIP_filter_notify_socket_event(WOLFIP_FILT_CONNECTING, &s, NULL, |
| 291 | + local_ip, 1234, remote_ip, 4321); |
| 292 | + |
| 293 | + ck_assert_int_eq(filter_cb_calls, 1); |
| 294 | + ck_assert_uint_eq(filter_last_event.if_idx, WOLFIP_PRIMARY_IF_IDX); |
| 295 | + ck_assert_uint_eq(filter_last_event.meta.ip_proto, 0); |
| 296 | + ck_assert_uint_eq(filter_last_event.meta.src_ip, ee32(local_ip)); |
| 297 | + ck_assert_uint_eq(filter_last_event.meta.dst_ip, ee32(remote_ip)); |
| 298 | + |
| 299 | + wolfIP_filter_set_callback(NULL, NULL); |
| 300 | + wolfIP_filter_set_mask(0); |
| 301 | +} |
| 302 | +END_TEST |
| 303 | + |
212 | 304 | START_TEST(test_filter_socket_event_proto_variants) |
213 | 305 | { |
214 | 306 | struct wolfIP s; |
@@ -2182,6 +2274,135 @@ START_TEST(test_sock_accept_initializes_snd_una) |
2182 | 2274 | } |
2183 | 2275 | END_TEST |
2184 | 2276 |
|
| 2277 | +START_TEST(test_sock_accept_clones_half_open_state_and_queues_synack) |
| 2278 | +{ |
| 2279 | + struct wolfIP s; |
| 2280 | + int listen_sd; |
| 2281 | + int client_sd; |
| 2282 | + struct tsocket *listener; |
| 2283 | + struct tsocket *accepted; |
| 2284 | + struct wolfIP_sockaddr_in sin; |
| 2285 | + struct pkt_desc *desc; |
| 2286 | + struct wolfIP_tcp_seg *seg; |
| 2287 | + void *cb_arg = &s; |
| 2288 | + uint32_t pre_accept_seq; |
| 2289 | + uint32_t pre_accept_ack; |
| 2290 | + uint32_t pre_accept_last_ts; |
| 2291 | + uint32_t pre_accept_local_ip; |
| 2292 | + uint32_t pre_accept_remote_ip; |
| 2293 | + uint32_t pre_accept_peer_rwnd; |
| 2294 | + uint16_t pre_accept_peer_mss; |
| 2295 | + uint16_t pre_accept_src_port; |
| 2296 | + uint16_t pre_accept_dst_port; |
| 2297 | + uint8_t pre_accept_snd_wscale; |
| 2298 | + uint8_t pre_accept_rcv_wscale; |
| 2299 | + uint8_t pre_accept_ws_enabled; |
| 2300 | + uint8_t pre_accept_ws_offer; |
| 2301 | + uint8_t pre_accept_ts_enabled; |
| 2302 | + uint8_t pre_accept_ts_offer; |
| 2303 | + uint8_t pre_accept_sack_offer; |
| 2304 | + uint8_t pre_accept_sack_permitted; |
| 2305 | + |
| 2306 | + wolfIP_init(&s); |
| 2307 | + mock_link_init(&s); |
| 2308 | + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); |
| 2309 | + |
| 2310 | + listen_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); |
| 2311 | + ck_assert_int_gt(listen_sd, 0); |
| 2312 | + memset(&sin, 0, sizeof(sin)); |
| 2313 | + sin.sin_family = AF_INET; |
| 2314 | + sin.sin_port = ee16(1234); |
| 2315 | + sin.sin_addr.s_addr = ee32(0x0A000001U); |
| 2316 | + ck_assert_int_eq(wolfIP_sock_bind(&s, listen_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); |
| 2317 | + ck_assert_int_eq(wolfIP_sock_listen(&s, listen_sd, 1), 0); |
| 2318 | + |
| 2319 | + listener = &s.tcpsockets[SOCKET_UNMARK(listen_sd)]; |
| 2320 | + listener->callback = test_socket_cb; |
| 2321 | + listener->callback_arg = cb_arg; |
| 2322 | + |
| 2323 | + /* Drive the listener into SYN_RCVD so accept() has half-open state to fork. */ |
| 2324 | + inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); |
| 2325 | + ck_assert_int_eq(listener->sock.tcp.state, TCP_SYN_RCVD); |
| 2326 | + |
| 2327 | + /* Seed half-open negotiation state so accept() must clone it into the child socket. */ |
| 2328 | + listener->sock.tcp.last_ts = 0x11223344U; |
| 2329 | + listener->sock.tcp.peer_rwnd = 4096; |
| 2330 | + listener->sock.tcp.peer_mss = 1200; |
| 2331 | + listener->sock.tcp.snd_wscale = 4; |
| 2332 | + listener->sock.tcp.rcv_wscale = 2; |
| 2333 | + listener->sock.tcp.ws_enabled = 1; |
| 2334 | + listener->sock.tcp.ws_offer = 1; |
| 2335 | + listener->sock.tcp.ts_enabled = 1; |
| 2336 | + listener->sock.tcp.ts_offer = 1; |
| 2337 | + listener->sock.tcp.sack_offer = 1; |
| 2338 | + listener->sock.tcp.sack_permitted = 1; |
| 2339 | + |
| 2340 | + pre_accept_seq = listener->sock.tcp.seq; |
| 2341 | + pre_accept_ack = listener->sock.tcp.ack; |
| 2342 | + pre_accept_last_ts = listener->sock.tcp.last_ts; |
| 2343 | + pre_accept_local_ip = listener->local_ip; |
| 2344 | + pre_accept_remote_ip = listener->remote_ip; |
| 2345 | + pre_accept_peer_rwnd = listener->sock.tcp.peer_rwnd; |
| 2346 | + pre_accept_peer_mss = listener->sock.tcp.peer_mss; |
| 2347 | + pre_accept_src_port = listener->src_port; |
| 2348 | + pre_accept_dst_port = listener->dst_port; |
| 2349 | + pre_accept_snd_wscale = listener->sock.tcp.snd_wscale; |
| 2350 | + pre_accept_rcv_wscale = listener->sock.tcp.rcv_wscale; |
| 2351 | + pre_accept_ws_enabled = listener->sock.tcp.ws_enabled; |
| 2352 | + pre_accept_ws_offer = listener->sock.tcp.ws_offer; |
| 2353 | + pre_accept_ts_enabled = listener->sock.tcp.ts_enabled; |
| 2354 | + pre_accept_ts_offer = listener->sock.tcp.ts_offer; |
| 2355 | + pre_accept_sack_offer = listener->sock.tcp.sack_offer; |
| 2356 | + pre_accept_sack_permitted = listener->sock.tcp.sack_permitted; |
| 2357 | + |
| 2358 | + /* Accept should fork the half-open state into a child socket and queue a SYN-ACK there. */ |
| 2359 | + client_sd = wolfIP_sock_accept(&s, listen_sd, NULL, NULL); |
| 2360 | + ck_assert_int_gt(client_sd, 0); |
| 2361 | + |
| 2362 | + accepted = &s.tcpsockets[SOCKET_UNMARK(client_sd)]; |
| 2363 | + /* The child socket should inherit the negotiated transport parameters verbatim. */ |
| 2364 | + ck_assert_int_eq(accepted->sock.tcp.state, TCP_SYN_RCVD); |
| 2365 | + ck_assert_ptr_eq(accepted->callback, test_socket_cb); |
| 2366 | + ck_assert_ptr_eq(accepted->callback_arg, cb_arg); |
| 2367 | + ck_assert_uint_eq(accepted->local_ip, pre_accept_local_ip); |
| 2368 | + ck_assert_uint_eq(accepted->bound_local_ip, listener->bound_local_ip); |
| 2369 | + ck_assert_uint_eq(accepted->if_idx, TEST_PRIMARY_IF); |
| 2370 | + ck_assert_uint_eq(accepted->remote_ip, pre_accept_remote_ip); |
| 2371 | + ck_assert_uint_eq(accepted->src_port, pre_accept_src_port); |
| 2372 | + ck_assert_uint_eq(accepted->dst_port, pre_accept_dst_port); |
| 2373 | + ck_assert_uint_eq(accepted->sock.tcp.ack, pre_accept_ack); |
| 2374 | + ck_assert_uint_eq(accepted->sock.tcp.snd_una, pre_accept_seq); |
| 2375 | + ck_assert_uint_eq(accepted->sock.tcp.last_ts, pre_accept_last_ts); |
| 2376 | + ck_assert_uint_eq(accepted->sock.tcp.peer_rwnd, pre_accept_peer_rwnd); |
| 2377 | + ck_assert_uint_eq(accepted->sock.tcp.peer_mss, pre_accept_peer_mss); |
| 2378 | + ck_assert_uint_eq(accepted->sock.tcp.snd_wscale, pre_accept_snd_wscale); |
| 2379 | + ck_assert_uint_eq(accepted->sock.tcp.rcv_wscale, pre_accept_rcv_wscale); |
| 2380 | + ck_assert_uint_eq(accepted->sock.tcp.ws_enabled, pre_accept_ws_enabled); |
| 2381 | + ck_assert_uint_eq(accepted->sock.tcp.ws_offer, pre_accept_ws_offer); |
| 2382 | + ck_assert_uint_eq(accepted->sock.tcp.ts_enabled, pre_accept_ts_enabled); |
| 2383 | + ck_assert_uint_eq(accepted->sock.tcp.ts_offer, pre_accept_ts_offer); |
| 2384 | + ck_assert_uint_eq(accepted->sock.tcp.sack_offer, pre_accept_sack_offer); |
| 2385 | + ck_assert_uint_eq(accepted->sock.tcp.sack_permitted, pre_accept_sack_permitted); |
| 2386 | + ck_assert_uint_eq(accepted->sock.tcp.cwnd, |
| 2387 | + tcp_initial_cwnd(pre_accept_peer_rwnd, tcp_cc_mss(accepted))); |
| 2388 | + ck_assert_uint_eq(accepted->sock.tcp.ssthresh, |
| 2389 | + tcp_initial_ssthresh(pre_accept_peer_rwnd)); |
| 2390 | + |
| 2391 | + desc = fifo_peek(&accepted->sock.tcp.txbuf); |
| 2392 | + ck_assert_ptr_nonnull(desc); |
| 2393 | + seg = (struct wolfIP_tcp_seg *)(accepted->txmem + desc->pos + sizeof(*desc)); |
| 2394 | + /* SYN-ACK transmission must be queued on the accepted child, not the listener. */ |
| 2395 | + ck_assert_uint_eq(seg->flags, (TCP_FLAG_SYN | TCP_FLAG_ACK)); |
| 2396 | + ck_assert_uint_eq(ee32(seg->seq), pre_accept_seq); |
| 2397 | + ck_assert_uint_eq(ee32(seg->ack), pre_accept_ack); |
| 2398 | + |
| 2399 | + /* The listener should be reset to passive-open state once the child is created. */ |
| 2400 | + ck_assert_int_eq(listener->sock.tcp.state, TCP_LISTEN); |
| 2401 | + ck_assert_uint_eq(listener->sock.tcp.ctrl_rto_active, 0); |
| 2402 | + ck_assert_uint_eq(listener->events & CB_EVENT_READABLE, 0); |
| 2403 | +} |
| 2404 | +END_TEST |
| 2405 | + |
2185 | 2406 | START_TEST(test_sock_accept_synack_retransmission) |
2186 | 2407 | { |
2187 | 2408 | struct wolfIP s; |
|
0 commit comments