diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 7a05f880..a29c0a11 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -117,6 +117,7 @@ Suite *wolf_suite(void) #endif 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_preserves_tcp_events_raised_during_callback); 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); @@ -237,6 +238,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_sock_connect_icmp_primary_fallback); tcase_add_test(tc_utils, test_sock_connect_tcp_filter_drop); tcase_add_test(tc_utils, test_sock_connect_tcp_src_port_low); + tcase_add_test(tc_utils, test_sock_connect_tcp_initial_seq_randomized); tcase_add_test(tc_utils, test_sock_sendto_more_error_paths); tcase_add_test(tc_utils, test_sock_sendto_udp_no_dest); tcase_add_test(tc_utils, test_sock_sendto_udp_sets_dest_and_assigns); @@ -290,6 +292,8 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_poll_icmp_send_on_arp_hit); tcase_add_test(tc_utils, test_dhcp_timer_cb_paths); tcase_add_test(tc_utils, test_dhcp_client_init_and_bound); + 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_dns_callback_ptr_response); tcase_add_test(tc_utils, test_udp_try_recv_short_frame); @@ -348,6 +352,12 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_dns_skip_and_copy_name); tcase_add_test(tc_utils, test_sock_opts_and_names); tcase_add_test(tc_utils, test_dns_send_query_errors); + 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_resend_query_uses_stored_query_buffer); + 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); tcase_add_test(tc_utils, test_dns_wrapper_apis); tcase_add_test(tc_utils, test_wolfip_static_instance_apis); @@ -383,6 +393,8 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_tcp_input_syn_sent_unexpected_flags); tcase_add_test(tc_utils, test_tcp_input_syn_sent_synack_transitions); tcase_add_test(tc_utils, test_tcp_input_syn_sent_synack_invalid_ack_rejected); + 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_input_rst_bad_seq_ignored); tcase_add_test(tc_utils, test_tcp_input_rst_seq_in_window_sends_ack); @@ -577,6 +589,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_proto, test_arp_recv_request_other_ip_no_reply); tcase_add_test(tc_proto, test_arp_recv_null_stack); tcase_add_test(tc_proto, test_arp_recv_request_sends_reply); + tcase_add_test(tc_proto, test_arp_recv_request_does_not_store_self_neighbor); tcase_add_test(tc_proto, test_arp_recv_request_no_send_fn); tcase_add_test(tc_proto, test_wolfip_if_for_local_ip_paths); tcase_add_test(tc_proto, test_wolfip_if_for_local_ip_null_found); @@ -640,6 +653,8 @@ Suite *wolf_suite(void) tcase_add_test(tc_proto, test_udp_recvfrom_src_equals_local_ip_does_not_persist_remote); tcase_add_test(tc_proto, test_dns_query_and_callback_a); tcase_add_test(tc_proto, test_dhcp_parse_offer_and_ack); + tcase_add_test(tc_proto, test_dhcp_schedule_lease_timer_defaults_t1_t2); + tcase_add_test(tc_proto, test_dhcp_schedule_lease_timer_small_lease_clamps_t1_t2); tcase_add_test(tc_proto, test_dhcp_parse_offer_defaults_mask_when_missing); tcase_add_test(tc_proto, test_dhcp_parse_offer_rejects_mismatched_xid); tcase_add_test(tc_proto, test_dhcp_parse_ack_rejects_mismatched_xid); @@ -652,6 +667,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_proto, test_regression_udp_inflated_udp_len); tcase_add_test(tc_proto, test_regression_udp_len_below_header_discards_and_unblocks); tcase_add_test(tc_proto, test_regression_udp_payload_exceeds_buffer_discards_and_unblocks); + tcase_add_test(tc_proto, test_regression_icmp_payload_exceeds_buffer_discards_and_unblocks); tcase_add_test(tc_proto, test_regression_tcp_ip_len_below_ip_header); tcase_add_test(tc_utils, test_transport_checksum); diff --git a/src/test/unit/unit_tests_api.c b/src/test/unit/unit_tests_api.c index 7f72bbd6..30fe13c5 100644 --- a/src/test/unit/unit_tests_api.c +++ b/src/test/unit/unit_tests_api.c @@ -1,3 +1,19 @@ +static struct wolfIP *poll_rearm_stack; +static int poll_rearm_cb_calls; +static int poll_rearm_recv_len; + +static void test_poll_rearm_tcp_cb(int sock_fd, uint16_t events, void *arg) +{ + uint8_t buf[8]; + + (void)arg; + poll_rearm_cb_calls++; + ck_assert_ptr_nonnull(poll_rearm_stack); + ck_assert_uint_eq(events & CB_EVENT_READABLE, CB_EVENT_READABLE); + ck_assert_int_eq(wolfIP_sock_recvfrom(poll_rearm_stack, sock_fd, buf, + (size_t)poll_rearm_recv_len, 0, NULL, NULL), poll_rearm_recv_len); +} + START_TEST(test_wolfip_poll_executes_timers_and_callbacks) { struct wolfIP s; @@ -31,6 +47,39 @@ START_TEST(test_wolfip_poll_executes_timers_and_callbacks) } END_TEST +START_TEST(test_wolfip_poll_preserves_tcp_events_raised_during_callback) +{ + struct wolfIP s; + int tcp_sd; + struct tsocket *ts; + uint8_t payload[8] = {0}; + + wolfIP_init(&s); + mock_link_init(&s); + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + ts->sock.tcp.state = TCP_ESTABLISHED; + queue_init(&ts->sock.tcp.rxbuf, ts->rxmem, RXBUF_SIZE, 0); + ck_assert_int_eq(queue_insert(&ts->sock.tcp.rxbuf, payload, 0, sizeof(payload)), 0); + + poll_rearm_stack = &s; + poll_rearm_cb_calls = 0; + poll_rearm_recv_len = 4; + wolfIP_register_callback(&s, tcp_sd, test_poll_rearm_tcp_cb, NULL); + ts->events = CB_EVENT_READABLE; + + (void)wolfIP_poll(&s, 100); + ck_assert_int_eq(poll_rearm_cb_calls, 1); + ck_assert_uint_eq(ts->events & CB_EVENT_READABLE, CB_EVENT_READABLE); + + (void)wolfIP_poll(&s, 101); + ck_assert_int_eq(poll_rearm_cb_calls, 2); + ck_assert_uint_eq(ts->events & CB_EVENT_READABLE, 0U); +} +END_TEST + START_TEST(test_filter_notify_tcp_metadata) { struct wolfIP s; diff --git a/src/test/unit/unit_tests_dns_dhcp.c b/src/test/unit/unit_tests_dns_dhcp.c index 08ee9769..95be3e75 100644 --- a/src/test/unit/unit_tests_dns_dhcp.c +++ b/src/test/unit/unit_tests_dns_dhcp.c @@ -1,3 +1,14 @@ +static uint64_t find_timer_expiry(const struct wolfIP *s, uint32_t timer_id) +{ + uint32_t i; + + for (i = 0; i < s->timers.size; i++) { + if (s->timers.timers[i].id == timer_id) + return s->timers.timers[i].expires; + } + return 0; +} + START_TEST(test_wolfip_static_instance_apis) { struct wolfIP *s = NULL; @@ -19,6 +30,7 @@ START_TEST(test_dhcp_parse_offer_and_ack) uint32_t router_ip = 0x0A000002U; uint32_t dns_ip = 0x08080808U; uint32_t mask = 0xFFFFFF00U; + uint32_t lease_s = 120U; wolfIP_init(&s); primary = wolfIP_primary_ipconf(&s); @@ -54,6 +66,7 @@ START_TEST(test_dhcp_parse_offer_and_ack) ck_assert_uint_eq(s.dhcp_server_ip, server_ip); ck_assert_uint_eq(primary->mask, mask); ck_assert_int_eq(s.dhcp_state, DHCP_REQUEST_SENT); + s.last_tick = 1000U; memset(&msg, 0, sizeof(msg)); msg.magic = ee32(DHCP_MAGIC); @@ -90,6 +103,13 @@ START_TEST(test_dhcp_parse_offer_and_ack) opt->data[2] = (router_ip >> 8) & 0xFF; opt->data[3] = (router_ip >> 0) & 0xFF; opt = (struct dhcp_option *)((uint8_t *)opt + 6); + opt->code = DHCP_OPTION_LEASE_TIME; + opt->len = 4; + opt->data[0] = (lease_s >> 24) & 0xFF; + opt->data[1] = (lease_s >> 16) & 0xFF; + opt->data[2] = (lease_s >> 8) & 0xFF; + opt->data[3] = (lease_s >> 0) & 0xFF; + opt = (struct dhcp_option *)((uint8_t *)opt + 6); opt->code = DHCP_OPTION_OFFER_IP; opt->len = 4; opt->data[0] = (offer_ip >> 24) & 0xFF; @@ -106,6 +126,42 @@ START_TEST(test_dhcp_parse_offer_and_ack) ck_assert_uint_eq(primary->mask, mask); ck_assert_uint_eq(primary->gw, router_ip); ck_assert_uint_eq(s.dns_server, dns_ip); + ck_assert_int_ne(s.dhcp_timer, NO_TIMER); + ck_assert_uint_eq(find_timer_expiry(&s, s.dhcp_timer), 61000U); +} +END_TEST + +START_TEST(test_dhcp_schedule_lease_timer_defaults_t1_t2) +{ + struct wolfIP s; + + wolfIP_init(&s); + s.last_tick = 1000U; + + dhcp_schedule_lease_timer(&s, 120U, 0, 0); + + ck_assert_int_ne(s.dhcp_timer, NO_TIMER); + ck_assert_uint_eq(s.dhcp_renew_at, 61000U); + ck_assert_uint_eq(s.dhcp_rebind_at, 106000U); + ck_assert_uint_eq(s.dhcp_lease_expires, 121000U); + ck_assert_uint_eq(find_timer_expiry(&s, s.dhcp_timer), s.dhcp_renew_at); +} +END_TEST + +START_TEST(test_dhcp_schedule_lease_timer_small_lease_clamps_t1_t2) +{ + struct wolfIP s; + + wolfIP_init(&s); + s.last_tick = 1000U; + + dhcp_schedule_lease_timer(&s, 1U, 0, 0); + + ck_assert_int_ne(s.dhcp_timer, NO_TIMER); + ck_assert_uint_eq(s.dhcp_renew_at, 2000U); + ck_assert_uint_eq(s.dhcp_rebind_at, 2000U); + ck_assert_uint_eq(s.dhcp_lease_expires, 2000U); + ck_assert_uint_eq(find_timer_expiry(&s, s.dhcp_timer), s.dhcp_renew_at); } END_TEST @@ -154,6 +210,7 @@ START_TEST(test_sock_recvfrom_tcp_states) struct wolfIP s; int tcp_sd; struct tsocket *ts; + uint8_t payload[4] = {1, 2, 3, 4}; uint8_t buf[4]; int ret; @@ -177,6 +234,20 @@ START_TEST(test_sock_recvfrom_tcp_states) ts->events = 0; ret = wolfIP_sock_recvfrom(&s, tcp_sd, buf, sizeof(buf), 0, NULL, 0); ck_assert_int_eq(ret, -WOLFIP_EAGAIN); + + queue_init(&ts->sock.tcp.rxbuf, ts->rxmem, RXBUF_SIZE, 0); + ck_assert_int_eq(queue_insert(&ts->sock.tcp.rxbuf, payload, 0, sizeof(payload)), 0); + ts->sock.tcp.state = TCP_FIN_WAIT_1; + ret = wolfIP_sock_recvfrom(&s, tcp_sd, buf, sizeof(buf), 0, NULL, 0); + ck_assert_int_eq(ret, sizeof(payload)); + ck_assert_mem_eq(buf, payload, sizeof(payload)); + + queue_init(&ts->sock.tcp.rxbuf, ts->rxmem, RXBUF_SIZE, 0); + ck_assert_int_eq(queue_insert(&ts->sock.tcp.rxbuf, payload, 0, sizeof(payload)), 0); + ts->sock.tcp.state = TCP_FIN_WAIT_2; + ret = wolfIP_sock_recvfrom(&s, tcp_sd, buf, sizeof(buf), 0, NULL, 0); + ck_assert_int_eq(ret, sizeof(payload)); + ck_assert_mem_eq(buf, payload, sizeof(payload)); } END_TEST @@ -714,6 +785,80 @@ START_TEST(test_sock_connect_tcp_filter_drop) } END_TEST +START_TEST(test_dhcp_send_request_renewing_sets_ciaddr_and_rebind_deadline) +{ + struct wolfIP s; + struct tsocket *ts; + struct ipconf *primary; + struct pkt_desc *desc; + struct wolfIP_udp_datagram *udp; + struct dhcp_msg *req; + + wolfIP_init(&s); + mock_link_init(&s); + primary = wolfIP_primary_ipconf(&s); + ck_assert_ptr_nonnull(primary); + primary->ip = 0x0A000064U; + primary->mask = 0xFFFFFF00U; + s.dhcp_server_ip = 0x0A000001U; + s.dhcp_ip = primary->ip; + s.dhcp_state = DHCP_RENEWING; + s.dhcp_rebind_at = 1500U; + s.last_tick = 1000U; + + s.dhcp_udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(s.dhcp_udp_sd, 0); + ts = &s.udpsockets[SOCKET_UNMARK(s.dhcp_udp_sd)]; + + ck_assert_int_eq(dhcp_send_request(&s), 0); + ck_assert_uint_eq(ts->remote_ip, s.dhcp_server_ip); + ck_assert_uint_eq(find_timer_expiry(&s, s.dhcp_timer), s.dhcp_rebind_at); + + desc = fifo_peek(&ts->sock.udp.txbuf); + ck_assert_ptr_nonnull(desc); + udp = (struct wolfIP_udp_datagram *)(ts->txmem + desc->pos + sizeof(*desc)); + req = (struct dhcp_msg *)udp->data; + ck_assert_uint_eq(req->ciaddr, ee32(primary->ip)); +} +END_TEST + +START_TEST(test_dhcp_send_request_rebinding_broadcasts_to_lease_expiry) +{ + struct wolfIP s; + struct tsocket *ts; + struct ipconf *primary; + struct pkt_desc *desc; + struct wolfIP_udp_datagram *udp; + struct dhcp_msg *req; + + wolfIP_init(&s); + mock_link_init(&s); + primary = wolfIP_primary_ipconf(&s); + ck_assert_ptr_nonnull(primary); + primary->ip = 0x0A000064U; + primary->mask = 0xFFFFFF00U; + s.dhcp_server_ip = 0x0A000001U; + s.dhcp_ip = primary->ip; + s.dhcp_state = DHCP_REBINDING; + s.dhcp_lease_expires = 1300U; + s.last_tick = 1000U; + + s.dhcp_udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(s.dhcp_udp_sd, 0); + ts = &s.udpsockets[SOCKET_UNMARK(s.dhcp_udp_sd)]; + + ck_assert_int_eq(dhcp_send_request(&s), 0); + ck_assert_uint_eq(ts->remote_ip, 0xFFFFFFFFU); + ck_assert_uint_eq(find_timer_expiry(&s, s.dhcp_timer), s.dhcp_lease_expires); + + desc = fifo_peek(&ts->sock.udp.txbuf); + ck_assert_ptr_nonnull(desc); + udp = (struct wolfIP_udp_datagram *)(ts->txmem + desc->pos + sizeof(*desc)); + req = (struct dhcp_msg *)udp->data; + ck_assert_uint_eq(req->ciaddr, ee32(primary->ip)); +} +END_TEST + START_TEST(test_sock_connect_tcp_src_port_low) { struct wolfIP s; @@ -741,6 +886,35 @@ START_TEST(test_sock_connect_tcp_src_port_low) } END_TEST +START_TEST(test_sock_connect_tcp_initial_seq_randomized) +{ + struct wolfIP s; + int tcp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + ts->sock.tcp.state = TCP_CLOSED; + ts->src_port = 23456; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(5555); + sin.sin_addr.s_addr = ee32(0x0A000002U); + + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), + -WOLFIP_EAGAIN); + ck_assert_uint_ne(ts->sock.tcp.seq, 0U); + ck_assert_uint_eq(ts->sock.tcp.snd_una, ts->sock.tcp.seq); +} +END_TEST + START_TEST(test_sock_sendto_more_error_paths) { struct wolfIP s; @@ -1131,7 +1305,7 @@ START_TEST(test_sock_recvfrom_icmp_paths) ck_assert_int_eq(ret, -1); ret = wolfIP_sock_recvfrom(&s, icmp_sd, rxbuf, sizeof(rxbuf), 0, NULL, NULL); - ck_assert_int_eq(ret, ICMP_HEADER_LEN + 2); + ck_assert_int_eq(ret, -WOLFIP_EAGAIN); } END_TEST @@ -1715,6 +1889,170 @@ START_TEST(test_dns_send_query_errors) ck_assert_int_eq(dns_send_query(&s, "example.com", &id, DNS_A), -16); } END_TEST + +START_TEST(test_dns_schedule_timer_initial_jitter_and_cancel) +{ + struct wolfIP s; + uint32_t timer_id; + + wolfIP_init(&s); + s.last_tick = 100U; + s.dns_retry_count = 0; + test_rand_override_enabled = 1; + test_rand_override_value = 390U; + + dns_schedule_timer(&s); + ck_assert_int_ne(s.dns_timer, NO_TIMER); + ck_assert_uint_eq(find_timer_expiry(&s, s.dns_timer), 2290U); + + timer_id = s.dns_timer; + dns_cancel_timer(&s); + ck_assert_int_eq(s.dns_timer, NO_TIMER); + ck_assert_uint_eq(find_timer_expiry(&s, timer_id), 0U); + + s.dns_retry_count = 1; + dns_schedule_timer(&s); + ck_assert_int_ne(s.dns_timer, NO_TIMER); + ck_assert_uint_eq(find_timer_expiry(&s, s.dns_timer), 4100U); + + test_rand_override_enabled = 0; +} +END_TEST + +START_TEST(test_dns_schedule_timer_caps_large_retry_shift) +{ + struct wolfIP s; + + wolfIP_init(&s); + s.last_tick = 100U; + s.dns_retry_count = 64U; + + dns_schedule_timer(&s); + ck_assert_int_ne(s.dns_timer, NO_TIMER); + ck_assert_uint_eq(find_timer_expiry(&s, s.dns_timer), UINT64_MAX); +} +END_TEST + +START_TEST(test_dns_send_query_schedules_timeout) +{ + struct wolfIP s; + uint16_t id = 0; + + wolfIP_init(&s); + mock_link_init(&s); + s.dns_server = 0x08080808U; + s.last_tick = 100U; + test_rand_override_enabled = 1; + test_rand_override_value = 390U; + + ck_assert_int_eq(dns_send_query(&s, "example.com", &id, DNS_A), 0); + ck_assert_uint_ne(id, 0U); + ck_assert_uint_eq(s.dns_id, id); + ck_assert_int_ne(s.dns_timer, NO_TIMER); + ck_assert_uint_eq(find_timer_expiry(&s, s.dns_timer), 2290U); + test_rand_override_enabled = 0; +} +END_TEST + +START_TEST(test_dns_resend_query_uses_stored_query_buffer) +{ + struct wolfIP s; + struct tsocket *ts; + uint16_t id = 0; + uint32_t tx_before; + + wolfIP_init(&s); + mock_link_init(&s); + s.dns_server = 0x08080808U; + s.last_tick = 100U; + test_rand_override_enabled = 1; + test_rand_override_value = 1U; + + ck_assert_int_eq(dns_send_query(&s, "example.com", &id, DNS_A), 0); + ts = &s.udpsockets[SOCKET_UNMARK(s.dns_udp_sd)]; + tx_before = fifo_len(&ts->sock.udp.txbuf); + ck_assert_uint_gt(s.dns_query_len, 0U); + ck_assert_int_gt(dns_resend_query(&s), 0); + ck_assert_uint_gt(fifo_len(&ts->sock.udp.txbuf), tx_before); + + test_rand_override_enabled = 0; +} +END_TEST + +START_TEST(test_dns_abort_query_clears_timer_and_query_state) +{ + struct wolfIP s; + uint32_t timer_id; + + wolfIP_init(&s); + s.last_tick = 100U; + s.dns_id = 0x1234U; + s.dns_retry_count = 2U; + s.dns_query_type = DNS_QUERY_TYPE_A; + s.dns_query_len = 32U; + s.dns_lookup_cb = test_dns_lookup_cb; + s.dns_ptr_cb = test_dns_ptr_cb; + test_rand_override_enabled = 1; + test_rand_override_value = 0U; + + dns_schedule_timer(&s); + timer_id = s.dns_timer; + ck_assert_int_ne(timer_id, NO_TIMER); + + dns_abort_query(&s); + ck_assert_int_eq(s.dns_timer, NO_TIMER); + ck_assert_uint_eq(find_timer_expiry(&s, timer_id), 0U); + ck_assert_uint_eq(s.dns_id, 0U); + ck_assert_uint_eq(s.dns_retry_count, 0U); + ck_assert_int_eq(s.dns_query_type, DNS_QUERY_TYPE_NONE); + ck_assert_uint_eq(s.dns_query_len, 0U); + ck_assert_ptr_eq(s.dns_lookup_cb, NULL); + ck_assert_ptr_eq(s.dns_ptr_cb, NULL); + + test_rand_override_enabled = 0; +} +END_TEST + +START_TEST(test_dns_timeout_retries_then_aborts_and_allows_new_query) +{ + struct wolfIP s; + struct tsocket *ts; + uint16_t id1 = 0; + uint16_t id2 = 0; + uint32_t tx_before; + int i; + + wolfIP_init(&s); + mock_link_init(&s); + s.dns_server = 0x08080808U; + s.last_tick = 100U; + test_rand_override_enabled = 1; + test_rand_override_value = 1U; + + ck_assert_int_eq(dns_send_query(&s, "example.com", &id1, DNS_A), 0); + ck_assert_uint_ne(id1, 0U); + ts = &s.udpsockets[SOCKET_UNMARK(s.dns_udp_sd)]; + tx_before = fifo_len(&ts->sock.udp.txbuf); + + ck_assert_int_eq(dns_send_query(&s, "example.net", &id2, DNS_A), -16); + + for (i = 0; i < DNS_QUERY_RETRIES; i++) { + dns_timeout_cb(&s); + ck_assert_uint_gt(fifo_len(&ts->sock.udp.txbuf), tx_before); + tx_before = fifo_len(&ts->sock.udp.txbuf); + } + + dns_timeout_cb(&s); + ck_assert_uint_eq(s.dns_id, 0U); + ck_assert_int_eq(s.dns_query_type, DNS_QUERY_TYPE_NONE); + ck_assert_ptr_eq(s.dns_lookup_cb, NULL); + + ck_assert_int_eq(dns_send_query(&s, "example.net", &id2, DNS_A), 0); + ck_assert_uint_ne(id2, 0U); + test_rand_override_enabled = 0; +} +END_TEST + START_TEST(test_dns_send_query_invalid_name) { struct wolfIP s; @@ -2886,10 +3224,13 @@ END_TEST START_TEST(test_dhcp_timer_cb_paths) { struct wolfIP s; + struct ipconf *primary; int ret; wolfIP_init(&s); mock_link_init(&s); + primary = wolfIP_primary_ipconf(&s); + ck_assert_ptr_nonnull(primary); s.dhcp_udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); ck_assert_int_gt(s.dhcp_udp_sd, 0); s.dhcp_xid = 1; @@ -2911,6 +3252,18 @@ START_TEST(test_dhcp_timer_cb_paths) ck_assert_int_eq(ret, 0); dhcp_timer_cb(&s); ck_assert_int_eq(s.dhcp_timeout_count, 1); + + primary->ip = 0x0A000064U; + primary->mask = 0xFFFFFF00U; + s.dhcp_ip = primary->ip; + s.dhcp_server_ip = 0x0A000001U; + s.last_tick = 1000U; + s.dhcp_state = DHCP_BOUND; + last_frame_sent_size = 0; + dhcp_timer_cb(&s); + (void)wolfIP_poll(&s, s.last_tick); + ck_assert_uint_gt(last_frame_sent_size, 0U); + ck_assert_int_eq(s.dhcp_state, DHCP_RENEWING); } END_TEST @@ -3759,4 +4112,3 @@ START_TEST(test_dns_callback_abort_clears_query_state) ck_assert_ptr_eq(s.dns_lookup_cb, NULL); } END_TEST - diff --git a/src/test/unit/unit_tests_proto.c b/src/test/unit/unit_tests_proto.c index 90e1a68e..83a6c0c0 100644 --- a/src/test/unit/unit_tests_proto.c +++ b/src/test/unit/unit_tests_proto.c @@ -3745,6 +3745,43 @@ START_TEST(test_regression_udp_payload_exceeds_buffer_discards_and_unblocks) } END_TEST +START_TEST(test_regression_icmp_payload_exceeds_buffer_discards_and_unblocks) +{ + struct wolfIP s; + struct tsocket *ts; + uint8_t buf[sizeof(struct wolfIP_icmp_packet) + 32]; + struct wolfIP_icmp_packet *icmp = (struct wolfIP_icmp_packet *)buf; + uint8_t rxbuf[8]; + int sd, ret; + + wolfIP_init(&s); + mock_link_init(&s); + + ts = icmp_new_socket(&s); + ck_assert_ptr_nonnull(ts); + ts->src_port = 1234; + ts->local_ip = 0x0A000001U; + + sd = (int)(MARK_ICMP_SOCKET | (uint32_t)(ts - s.icmpsockets)); + + memset(buf, 0, sizeof(buf)); + icmp->ip.len = ee16(IP_HEADER_LEN + ICMP_HEADER_LEN + 32); + icmp->type = ICMP_ECHO_REPLY; + ret = fifo_push(&ts->sock.udp.rxbuf, icmp, + sizeof(struct wolfIP_icmp_packet) + 32); + ck_assert_int_eq(ret, 0); + ts->events |= CB_EVENT_READABLE; + + ret = wolfIP_sock_recvfrom(&s, sd, rxbuf, sizeof(rxbuf), 0, NULL, NULL); + ck_assert_int_eq(ret, -1); + ck_assert_ptr_eq(fifo_peek(&ts->sock.udp.rxbuf), NULL); + ck_assert_uint_eq(ts->events & CB_EVENT_READABLE, 0U); + + ret = wolfIP_sock_recvfrom(&s, sd, rxbuf, sizeof(rxbuf), 0, NULL, NULL); + ck_assert_int_eq(ret, -WOLFIP_EAGAIN); +} +END_TEST + START_TEST(test_regression_icmp_ip_len_below_header) { struct wolfIP s; @@ -3812,4 +3849,3 @@ END_TEST /* ----------------------------------------------------------------------- */ - diff --git a/src/test/unit/unit_tests_tcp_ack.c b/src/test/unit/unit_tests_tcp_ack.c index 6fbdbb37..7ceb2c2f 100644 --- a/src/test/unit/unit_tests_tcp_ack.c +++ b/src/test/unit/unit_tests_tcp_ack.c @@ -2012,6 +2012,48 @@ START_TEST(test_arp_recv_request_sends_reply) } END_TEST +START_TEST(test_arp_recv_request_does_not_store_self_neighbor) +{ + struct wolfIP s; + struct arp_packet arp_req; + const ip4 local_ip = 0x0A000001U; + const ip4 sender_ip = 0x0A000002U; + const uint8_t sender_mac[6] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; + int i; + int sender_count = 0; + int self_count = 0; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); + wolfIP_filter_set_callback(NULL, NULL); + wolfIP_filter_set_mask(0); + + memset(&arp_req, 0, sizeof(arp_req)); + arp_req.opcode = ee16(ARP_REQUEST); + arp_req.sip = ee32(sender_ip); + memcpy(arp_req.sma, sender_mac, sizeof(sender_mac)); + arp_req.tip = ee32(local_ip); + + arp_recv(&s, TEST_PRIMARY_IF, &arp_req, sizeof(arp_req)); + + for (i = 0; i < MAX_NEIGHBORS; i++) { + if (s.arp.neighbors[i].if_idx != TEST_PRIMARY_IF) + continue; + if (s.arp.neighbors[i].ip == sender_ip) { + sender_count++; + ck_assert_mem_eq(s.arp.neighbors[i].mac, sender_mac, + sizeof(sender_mac)); + } + if (s.arp.neighbors[i].ip == local_ip) + self_count++; + } + + ck_assert_int_eq(sender_count, 1); + ck_assert_int_eq(self_count, 0); +} +END_TEST + START_TEST(test_send_ttl_exceeded_filter_drop) { struct wolfIP s; diff --git a/src/test/unit/unit_tests_tcp_flow.c b/src/test/unit/unit_tests_tcp_flow.c index 2fb90dc6..4ac8f956 100644 --- a/src/test/unit/unit_tests_tcp_flow.c +++ b/src/test/unit/unit_tests_tcp_flow.c @@ -567,6 +567,118 @@ START_TEST(test_tcp_input_syn_sent_synack_invalid_ack_rejected) } END_TEST +START_TEST(test_tcp_input_syn_listen_does_not_scale_syn_window) +{ + struct wolfIP s; + int listen_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + struct { + struct wolfIP_tcp_seg seg; + uint8_t ws_opt[4]; + } syn; + + 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); + + ts = &s.tcpsockets[SOCKET_UNMARK(listen_sd)]; + + memset(&syn, 0, sizeof(syn)); + syn.seg.ip.ver_ihl = 0x45; + syn.seg.ip.proto = WI_IPPROTO_TCP; + syn.seg.ip.ttl = 64; + syn.seg.ip.src = ee32(0x0A0000A1U); + syn.seg.ip.dst = ee32(0x0A000001U); + syn.seg.ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN + 4); + syn.seg.src_port = ee16(40000); + syn.seg.dst_port = ee16(1234); + syn.seg.seq = ee32(1); + syn.seg.hlen = (TCP_HEADER_LEN + 4) << 2; + syn.seg.flags = TCP_FLAG_SYN; + syn.seg.win = ee16(29200); + syn.ws_opt[0] = TCP_OPTION_WS; + syn.ws_opt[1] = TCP_OPTION_WS_LEN; + syn.ws_opt[2] = 7; + syn.ws_opt[3] = TCP_OPTION_NOP; + fix_tcp_checksums(&syn.seg); + + tcp_input(&s, TEST_PRIMARY_IF, &syn.seg, + (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN + 4)); + + ck_assert_uint_eq(ts->sock.tcp.ws_enabled, 1); + ck_assert_uint_eq(ts->sock.tcp.snd_wscale, 7); + ck_assert_uint_eq(ts->sock.tcp.peer_rwnd, 29200U); +} +END_TEST + +START_TEST(test_tcp_input_syn_sent_does_not_scale_synack_window) +{ + struct wolfIP s; + int tcp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + struct { + struct wolfIP_tcp_seg seg; + uint8_t ws_opt[4]; + } synack; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + ts->src_port = 23456; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(5001); + sin.sin_addr.s_addr = ee32(0x0A000002U); + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EAGAIN); + ck_assert_int_eq(ts->sock.tcp.state, TCP_SYN_SENT); + + memset(&synack, 0, sizeof(synack)); + synack.seg.ip.ver_ihl = 0x45; + synack.seg.ip.proto = WI_IPPROTO_TCP; + synack.seg.ip.ttl = 64; + synack.seg.ip.src = ee32(0x0A000002U); + synack.seg.ip.dst = ee32(0x0A000001U); + synack.seg.ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN + 4); + synack.seg.src_port = ee16(5001); + synack.seg.dst_port = ee16(ts->src_port); + synack.seg.seq = ee32(100); + synack.seg.ack = ee32(ts->sock.tcp.seq + 1); + synack.seg.hlen = (TCP_HEADER_LEN + 4) << 2; + synack.seg.flags = (TCP_FLAG_SYN | TCP_FLAG_ACK); + synack.seg.win = ee16(29200); + synack.ws_opt[0] = TCP_OPTION_WS; + synack.ws_opt[1] = TCP_OPTION_WS_LEN; + synack.ws_opt[2] = 7; + synack.ws_opt[3] = TCP_OPTION_NOP; + fix_tcp_checksums(&synack.seg); + + tcp_input(&s, TEST_PRIMARY_IF, &synack.seg, + (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN + 4)); + + ck_assert_int_eq(ts->sock.tcp.state, TCP_ESTABLISHED); + ck_assert_uint_eq(ts->sock.tcp.ws_enabled, 1); + ck_assert_uint_eq(ts->sock.tcp.snd_wscale, 7); + ck_assert_uint_eq(ts->sock.tcp.peer_rwnd, 29200U); +} +END_TEST + START_TEST(test_tcp_parse_sack_wraparound_block_accepted) { uint8_t seg_buf[sizeof(struct wolfIP_tcp_seg) + 10]; @@ -3658,4 +3770,3 @@ START_TEST(test_tcp_input_established_fin_out_of_order_no_transition) ck_assert_uint_eq(ts->events & CB_EVENT_CLOSED, 0); } END_TEST - diff --git a/src/wolfip.c b/src/wolfip.c index e229ea22..62d2f568 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -984,8 +984,11 @@ static int wolfIP_filter_notify_icmp(enum wolfIP_filter_reason reason, #define DHCP_OPTION_SUBNET_MASK 1 #define DHCP_OPTION_ROUTER 3 #define DHCP_OPTION_DNS 6 +#define DHCP_OPTION_LEASE_TIME 51 #define DHCP_OPTION_SERVER_ID 54 #define DHCP_OPTION_PARAM_REQ 55 +#define DHCP_OPTION_RENEWAL_TIME 58 +#define DHCP_OPTION_REBIND_TIME 59 #define DHCP_OPTION_OFFER_IP 50 #define DHCP_OPTION_END 0xFF #define DHCP_DISCOVER_TIMEOUT 2000 @@ -1002,6 +1005,8 @@ enum dhcp_state { DHCP_DISCOVER_SENT, DHCP_REQUEST_SENT, DHCP_BOUND, + DHCP_RENEWING, + DHCP_REBINDING, }; #define DHCP_IS_RUNNING(s) \ @@ -1193,10 +1198,17 @@ struct wolfIP { uint32_t dhcp_timeout_count; /* DHCP timeout counter */ ip4 dhcp_server_ip; /* DHCP server IP */ ip4 dhcp_ip; /* IP address assigned by DHCP */ + uint64_t dhcp_renew_at; /* Renewal time (T1) */ + uint64_t dhcp_rebind_at; /* Rebind time (T2) */ + uint64_t dhcp_lease_expires; /* Lease expiration time */ ip4 dns_server; uint16_t dns_id; int dns_udp_sd; + uint32_t dns_timer; + uint8_t dns_retry_count; uint8_t dns_query_type; + uint16_t dns_query_len; + uint8_t dns_query_buf[512]; void (*dns_lookup_cb)(ip4 ip); void (*dns_ptr_cb)(const char *name); char dns_ptr_name[256]; @@ -1757,8 +1769,6 @@ static int timers_binheap_insert(struct timers_binheap *heap, struct wolfIP_time if (heap->size >= MAX_TIMERS) return 0; /* heap full */ tmr.id = timer_id++; - if (heap->size >= MAX_TIMERS) - return NO_TIMER; /* Insert at the end */ heap->timers[heap->size] = tmr; heap->size++; @@ -3594,7 +3604,9 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, if (!(tcp->flags & TCP_FLAG_RST)) { uint32_t prev_peer_rwnd = t->sock.tcp.peer_rwnd; uint16_t raw_win = ee16(tcp->win); - uint8_t ws_shift = t->sock.tcp.ws_enabled ? t->sock.tcp.snd_wscale : 0; + uint8_t ws_shift = + (t->sock.tcp.ws_enabled && !(tcp->flags & TCP_FLAG_SYN)) ? + t->sock.tcp.snd_wscale : 0; t->sock.tcp.peer_rwnd = (uint32_t)raw_win << ws_shift; if (t->sock.tcp.peer_rwnd > prev_peer_rwnd) { if (t->sock.tcp.persist_active) @@ -4202,6 +4214,7 @@ int wolfIP_sock_connect(struct wolfIP *s, int sockfd, const struct wolfIP_sockad if (ts->src_port < 1024) ts->src_port += 1024; ts->dst_port = ee16(sin->sin_port); + ts->sock.tcp.seq = wolfIP_getrandom(); ts->sock.tcp.snd_una = ts->sock.tcp.seq; if (wolfIP_filter_notify_socket_event( WOLFIP_FILT_CONNECTING, s, ts, @@ -4560,7 +4573,9 @@ int wolfIP_sock_recvfrom(struct wolfIP *s, int sockfd, void *buf, size_t len, in } return ret; } - } else if (ts->sock.tcp.state == TCP_ESTABLISHED) { + } else if (ts->sock.tcp.state == TCP_ESTABLISHED || + ts->sock.tcp.state == TCP_FIN_WAIT_1 || + ts->sock.tcp.state == TCP_FIN_WAIT_2) { uint16_t win_before = tcp_adv_win(ts, 1); int ret = queue_pop(&ts->sock.tcp.rxbuf, buf, len); if (ret > 0) { @@ -4628,8 +4643,12 @@ int wolfIP_sock_recvfrom(struct wolfIP *s, int sockfd, void *buf, size_t len, in return -WOLFIP_EAGAIN; icmp = (struct wolfIP_icmp_packet *)(ts->rxmem + desc->pos + sizeof(*desc)); seg_len = ee16(icmp->ip.len) - IP_HEADER_LEN; - if (seg_len > len) + if (seg_len > len) { + fifo_pop(&ts->sock.udp.rxbuf); + if (fifo_peek(&ts->sock.udp.rxbuf) == NULL) + ts->events &= ~CB_EVENT_READABLE; return -1; + } if (sin) { sin->sin_family = AF_INET; sin->sin_port = 0; @@ -5085,12 +5104,78 @@ static void icmp_input(struct wolfIP *s, unsigned int if_idx, struct wolfIP_ip_p static int dhcp_send_discover(struct wolfIP *s); static int dhcp_send_request(struct wolfIP *s); +static void dhcp_timer_cb(void *arg); + +static void dhcp_schedule_timer_at(struct wolfIP *s, uint64_t when) +{ + struct wolfIP_timer tmr = { }; + + if (!s) + return; + tmr.expires = (when > s->last_tick) ? when : (s->last_tick + 1U); + tmr.arg = s; + tmr.cb = dhcp_timer_cb; + s->dhcp_timer = timers_binheap_insert(&s->timers, tmr); +} + +static void dhcp_schedule_retry_timer(struct wolfIP *s, uint64_t deadline) +{ + uint64_t next; + + if (!s) + return; + next = s->last_tick + DHCP_REQUEST_TIMEOUT + (wolfIP_getrandom() % 200U); + if (deadline != 0 && next > deadline) + next = deadline; + dhcp_schedule_timer_at(s, next); +} + +static void dhcp_schedule_lease_timer(struct wolfIP *s, + uint32_t lease_s, + uint32_t renew_s, + uint32_t rebind_s) +{ + uint64_t lease_ms; + uint64_t renew_ms; + uint64_t rebind_ms; + + if (!s || lease_s == 0) + return; + + if (renew_s == 0 || renew_s > lease_s) { + renew_s = lease_s / 2U; + if (renew_s == 0) + renew_s = 1U; + } + if (rebind_s == 0 || rebind_s > lease_s) { + rebind_s = (uint32_t)(((uint64_t)lease_s * 7U) / 8U); + if (rebind_s == 0) + rebind_s = 1U; + } + if (rebind_s < renew_s) + rebind_s = renew_s; + if (renew_s > lease_s) + renew_s = lease_s; + if (rebind_s > lease_s) + rebind_s = lease_s; + + lease_ms = (uint64_t)lease_s * 1000U; + renew_ms = (uint64_t)renew_s * 1000U; + rebind_ms = (uint64_t)rebind_s * 1000U; + + s->dhcp_renew_at = s->last_tick + renew_ms; + s->dhcp_rebind_at = s->last_tick + rebind_ms; + s->dhcp_lease_expires = s->last_tick + lease_ms; + dhcp_schedule_timer_at(s, s->dhcp_renew_at); +} + static void dhcp_timer_cb(void *arg) { struct wolfIP *s = (struct wolfIP *)arg; LOG("dhcp timeout\n"); if (!s) return; + s->dhcp_timer = NO_TIMER; switch(s->dhcp_state) { case DHCP_DISCOVER_SENT: if (s->dhcp_timeout_count < DHCP_DISCOVER_RETRIES) { @@ -5106,6 +5191,32 @@ static void dhcp_timer_cb(void *arg) } else s->dhcp_state = DHCP_OFF; break; + case DHCP_BOUND: + if (s->dhcp_lease_expires != 0 && s->last_tick >= s->dhcp_lease_expires) { + s->dhcp_state = DHCP_OFF; + break; + } + s->dhcp_state = DHCP_RENEWING; + s->dhcp_timeout_count = 0; + dhcp_send_request(s); + s->dhcp_timeout_count++; + break; + case DHCP_RENEWING: + if (s->dhcp_rebind_at != 0 && s->last_tick >= s->dhcp_rebind_at) { + s->dhcp_state = DHCP_REBINDING; + s->dhcp_timeout_count = 0; + } + dhcp_send_request(s); + s->dhcp_timeout_count++; + break; + case DHCP_REBINDING: + if (s->dhcp_lease_expires != 0 && s->last_tick >= s->dhcp_lease_expires) { + s->dhcp_state = DHCP_OFF; + break; + } + dhcp_send_request(s); + s->dhcp_timeout_count++; + break; default: break; } @@ -5118,6 +5229,9 @@ static void dhcp_cancel_timer(struct wolfIP *s) s->dhcp_timer = NO_TIMER; s->dhcp_timeout_count = 0; } + s->dhcp_renew_at = 0; + s->dhcp_rebind_at = 0; + s->dhcp_lease_expires = 0; } #define DHCP_OPT_data_to_u32(opt) \ @@ -5243,6 +5357,9 @@ static int dhcp_parse_ack(struct wolfIP *s, struct dhcp_msg *msg, uint32_t msg_l uint8_t *opt_end; int saw_end = 0; struct ipconf *primary = wolfIP_primary_ipconf(s); + uint32_t lease_s = 0; + uint32_t renew_s = 0; + uint32_t rebind_s = 0; if (msg_len < DHCP_HEADER_LEN) return -1; if (ee32(msg->magic) != DHCP_MAGIC) @@ -5323,6 +5440,18 @@ static int dhcp_parse_ack(struct wolfIP *s, struct dhcp_msg *msg, uint32_t msg_l return -1; data = DHCP_OPT_data_to_u32(inner); s->dns_server = ee32(data); + } else if (code == DHCP_OPTION_LEASE_TIME) { + if (len < 4) + return -1; + lease_s = ee32(DHCP_OPT_data_to_u32(inner)); + } else if (code == DHCP_OPTION_RENEWAL_TIME) { + if (len < 4) + return -1; + renew_s = ee32(DHCP_OPT_data_to_u32(inner)); + } else if (code == DHCP_OPTION_REBIND_TIME) { + if (len < 4) + return -1; + rebind_s = ee32(DHCP_OPT_data_to_u32(inner)); } opt += 2 + len; } @@ -5331,6 +5460,7 @@ static int dhcp_parse_ack(struct wolfIP *s, struct dhcp_msg *msg, uint32_t msg_l if (primary && (primary->ip != 0) && (primary->mask != 0)) { dhcp_cancel_timer(s); s->dhcp_state = DHCP_BOUND; + dhcp_schedule_lease_timer(s, lease_s, renew_s, rebind_s); return 0; } } @@ -5354,7 +5484,10 @@ static int dhcp_poll(struct wolfIP *s) return -1; if ((s->dhcp_state == DHCP_DISCOVER_SENT) && (dhcp_parse_offer(s, &msg, (uint32_t)len) == 0)) dhcp_send_request(s); - else if ((s->dhcp_state == DHCP_REQUEST_SENT) && (dhcp_parse_ack(s, &msg, (uint32_t)len) == 0)) { + else if ((s->dhcp_state == DHCP_REQUEST_SENT || + s->dhcp_state == DHCP_RENEWING || + s->dhcp_state == DHCP_REBINDING) && + (dhcp_parse_ack(s, &msg, (uint32_t)len) == 0)) { struct ipconf *primary = wolfIP_primary_ipconf(s); LOG("DHCP configuration received.\n"); if (primary) { @@ -5372,17 +5505,22 @@ static int dhcp_send_request(struct wolfIP *s) { struct dhcp_msg req; struct dhcp_option *opt = (struct dhcp_option *)(req.options); - struct wolfIP_timer tmr = { }; struct wolfIP_sockaddr_in sin; + struct ipconf *primary = wolfIP_primary_ipconf(s); + int renewing = (s->dhcp_state == DHCP_RENEWING); + int rebinding = (s->dhcp_state == DHCP_REBINDING); uint32_t opt_sz = 0; /* Prepare DHCP request */ memset(&req, 0, sizeof(struct dhcp_msg)); req.op = BOOT_REQUEST; - s->dhcp_state = DHCP_REQUEST_SENT; + if (!renewing && !rebinding) + s->dhcp_state = DHCP_REQUEST_SENT; req.htype = 1; /* Ethernet */ req.hlen = 6; /* MAC */ req.xid = ee32(s->dhcp_xid); req.magic = ee32(DHCP_MAGIC); + if ((renewing || rebinding) && primary) + req.ciaddr = ee32(primary->ip); { struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, WOLFIP_PRIMARY_IF_IDX); if (ll) @@ -5405,31 +5543,39 @@ static int dhcp_send_request(struct wolfIP *s) opt->data[2] = 6; /* DNS */ opt_sz += 5; opt = (struct dhcp_option *)((uint8_t *)opt + 5); - opt->code = DHCP_OPTION_SERVER_ID; /* Server ID */ - opt->len = 4; - DHCP_OPT_u32_to_data(opt, s->dhcp_server_ip); - opt_sz += 6; - opt = (struct dhcp_option *)((uint8_t *)opt + 6); - opt->code = DHCP_OPTION_OFFER_IP; /* Requested IP */ - opt->len = 4; - DHCP_OPT_u32_to_data(opt, s->dhcp_ip); - opt_sz += 6; + if (!renewing && !rebinding) { + opt->code = DHCP_OPTION_SERVER_ID; /* Server ID */ + opt->len = 4; + DHCP_OPT_u32_to_data(opt, s->dhcp_server_ip); + opt_sz += 6; + opt = (struct dhcp_option *)((uint8_t *)opt + 6); + opt->code = DHCP_OPTION_OFFER_IP; /* Requested IP */ + opt->len = 4; + DHCP_OPT_u32_to_data(opt, s->dhcp_ip); + opt_sz += 6; + } opt_sz++; memset(&sin, 0, sizeof(struct wolfIP_sockaddr_in)); sin.sin_port = ee16(DHCP_SERVER_PORT); - sin.sin_addr.s_addr = ee32(0xFFFFFFFF); /* Broadcast */ + if (renewing) + sin.sin_addr.s_addr = ee32(s->dhcp_server_ip); + else + sin.sin_addr.s_addr = ee32(0xFFFFFFFF); /* Broadcast */ sin.sin_family = AF_INET; wolfIP_sock_sendto(s, s->dhcp_udp_sd, &req, DHCP_HEADER_LEN + opt_sz, 0, (struct wolfIP_sockaddr *)&sin, sizeof(struct wolfIP_sockaddr_in)); - /* Reset local_ip so DHCP ACK matches via DHCP_IS_RUNNING path in - * udp_try_recv(). wolfIP_sock_sendto() sets local_ip from conf->ip - * (the offered IP), but we haven't confirmed the lease yet. */ - s->udpsockets[SOCKET_UNMARK(s->dhcp_udp_sd)].local_ip = 0; - tmr.expires = s->last_tick + DHCP_REQUEST_TIMEOUT + (wolfIP_getrandom() % 200); - tmr.arg = s; - tmr.cb = dhcp_timer_cb; - s->dhcp_timer = timers_binheap_insert(&s->timers, tmr); + if (!renewing && !rebinding) { + /* Reset local_ip so DHCP ACK matches via DHCP_IS_RUNNING path in + * udp_try_recv(). wolfIP_sock_sendto() sets local_ip from conf->ip + * (the offered IP), but we haven't confirmed the lease yet. */ + s->udpsockets[SOCKET_UNMARK(s->dhcp_udp_sd)].local_ip = 0; + dhcp_schedule_retry_timer(s, 0); + } else if (renewing) { + dhcp_schedule_retry_timer(s, s->dhcp_rebind_at); + } else { + dhcp_schedule_retry_timer(s, s->dhcp_lease_expires); + } return 0; } @@ -5497,7 +5643,9 @@ static int dhcp_send_discover(struct wolfIP *s) int dhcp_bound(struct wolfIP *s) { - return (s->dhcp_state == DHCP_BOUND); + return (s->dhcp_state == DHCP_BOUND || + s->dhcp_state == DHCP_RENEWING || + s->dhcp_state == DHCP_REBINDING); } int dhcp_client_is_running(struct wolfIP *s) @@ -5808,7 +5956,7 @@ static void arp_recv(struct wolfIP *s, unsigned int if_idx, void *buf, int len) ll->send(ll, buf, len); } } - if (arp->opcode == ee16(ARP_REPLY)) { + else if (arp->opcode == ee16(ARP_REPLY)) { ip4 sip = ee32(arp->sip); int idx = arp_neighbor_index(s, if_idx, sip); int pending = arp_pending_match_and_clear(s, if_idx, sip); @@ -6168,6 +6316,12 @@ void wolfIP_recv_ex(struct wolfIP *s, unsigned int if_idx, void *buf, uint32_t l #define DNS_QUERY_TYPE_PTR 2 #define MAX_DNS_NAME_LEN 255 #define MAX_DNS_LABEL_LEN 63 +#define DNS_QUERY_TIMEOUT 2000U +#define DNS_QUERY_TIMEOUT_INITIAL 1800U +#define DNS_QUERY_TIMEOUT_INITIAL_JITTER 391U +#ifndef DNS_QUERY_RETRIES +#define DNS_QUERY_RETRIES 3 +#endif struct PACKED dns_header { uint16_t id; @@ -6308,16 +6462,93 @@ static int dns_copy_name(const uint8_t *buf, int len, int offset, char *out, return -1; } +static void dns_timeout_cb(void *arg); + +static void dns_cancel_timer(struct wolfIP *s) +{ + if (!s) + return; + if (s->dns_timer != NO_TIMER) { + timer_binheap_cancel(&s->timers, s->dns_timer); + s->dns_timer = NO_TIMER; + } +} + +static void dns_schedule_timer(struct wolfIP *s) +{ + struct wolfIP_timer tmr = { }; + uint64_t interval = DNS_QUERY_TIMEOUT; + uint8_t shift; + + if (!s) + return; + if (s->dns_retry_count == 0) { + /* RFC 1035 recommends a 2s initial retransmission interval. On embedded + * targets, add a small 0..390 ms random offset to 1800 ms so many + * devices do not synchronize their first retry after a shared loss. */ + interval = DNS_QUERY_TIMEOUT_INITIAL + + (wolfIP_getrandom() % DNS_QUERY_TIMEOUT_INITIAL_JITTER); + } else { + shift = s->dns_retry_count; + if (shift >= 64U || interval > (UINT64_MAX >> shift)) + interval = UINT64_MAX - s->last_tick; + else + interval <<= shift; + } + tmr.expires = s->last_tick + interval; + tmr.arg = s; + tmr.cb = dns_timeout_cb; + s->dns_timer = timers_binheap_insert(&s->timers, tmr); +} + +static int dns_resend_query(struct wolfIP *s) +{ + struct wolfIP_sockaddr_in dns_srv; + + if (!s || s->dns_udp_sd <= 0 || s->dns_query_len == 0) + return -1; + memset(&dns_srv, 0, sizeof(struct wolfIP_sockaddr_in)); + dns_srv.sin_family = AF_INET; + dns_srv.sin_port = ee16(DNS_PORT); + dns_srv.sin_addr.s_addr = ee32(s->dns_server); + return wolfIP_sock_sendto(s, s->dns_udp_sd, s->dns_query_buf, s->dns_query_len, 0, + (struct wolfIP_sockaddr *)&dns_srv, sizeof(struct wolfIP_sockaddr_in)); +} + static void dns_abort_query(struct wolfIP *s) { if (!s) return; + dns_cancel_timer(s); s->dns_id = 0; + s->dns_retry_count = 0; s->dns_query_type = DNS_QUERY_TYPE_NONE; + s->dns_query_len = 0; s->dns_lookup_cb = NULL; s->dns_ptr_cb = NULL; } +static void dns_timeout_cb(void *arg) +{ + struct wolfIP *s = (struct wolfIP *)arg; + + if (!s) + return; + s->dns_timer = NO_TIMER; + if (s->dns_id == 0) + return; + if (s->dns_retry_count < DNS_QUERY_RETRIES) { + if (dns_resend_query(s) < 0) { + dns_abort_query(s); + return; + } + s->dns_retry_count++; + dns_schedule_timer(s); + } else { + dns_abort_query(s); + } +} + void dns_callback(int dns_sd, uint16_t ev, void *arg) { struct wolfIP *s = (struct wolfIP *)arg; @@ -6448,11 +6679,16 @@ static int dns_send_query(struct wolfIP *s, const char *dname, uint16_t *id, q = (struct dns_question *)(buf + sizeof(struct dns_header) + tok_len); q->qtype = ee16(qtype); q->qclass = ee16(1); + s->dns_query_len = (uint16_t)(sizeof(struct dns_header) + tok_len + sizeof(struct dns_question)); + memcpy(s->dns_query_buf, buf, s->dns_query_len); + s->dns_retry_count = 0; memset(&dns_srv, 0, sizeof(struct wolfIP_sockaddr_in)); dns_srv.sin_family = AF_INET; dns_srv.sin_port = ee16(DNS_PORT); dns_srv.sin_addr.s_addr = ee32(s->dns_server); - wolfIP_sock_sendto(s, s->dns_udp_sd, buf, sizeof(struct dns_header) + tok_len + sizeof(struct dns_question), 0, (struct wolfIP_sockaddr *)&dns_srv, sizeof(struct wolfIP_sockaddr_in)); + wolfIP_sock_sendto(s, s->dns_udp_sd, buf, s->dns_query_len, 0, + (struct wolfIP_sockaddr *)&dns_srv, sizeof(struct wolfIP_sockaddr_in)); + dns_schedule_timer(s); return 0; } @@ -6538,22 +6774,25 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) for (i = 0; i < MAX_TCPSOCKETS; i++) { struct tsocket *ts = &s->tcpsockets[i]; if ((ts->sock.tcp.state != TCP_CLOSED) && (ts->callback) && (ts->events)) { - ts->callback(i | MARK_TCP_SOCKET, ts->events, ts->callback_arg); + uint16_t events = ts->events; ts->events = 0; + ts->callback(i | MARK_TCP_SOCKET, events, ts->callback_arg); } } for (i = 0; i < MAX_UDPSOCKETS; i++) { struct tsocket *ts = &s->udpsockets[i]; if ((ts->callback) && (ts->events)) { - ts->callback(i | MARK_UDP_SOCKET, ts->events, ts->callback_arg); + uint16_t events = ts->events; ts->events = 0; + ts->callback(i | MARK_UDP_SOCKET, events, ts->callback_arg); } } for (i = 0; i < MAX_ICMPSOCKETS; i++) { struct tsocket *ts = &s->icmpsockets[i]; if ((ts->callback) && (ts->events)) { - ts->callback(i | MARK_ICMP_SOCKET, ts->events, ts->callback_arg); + uint16_t events = ts->events; ts->events = 0; + ts->callback(i | MARK_ICMP_SOCKET, events, ts->callback_arg); } }