diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index d786923a..b11b18c7 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -227,7 +227,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_sock_setsockopt_recvttl); tcase_add_test(tc_utils, test_sock_setsockopt_invalid_socket); tcase_add_test(tc_utils, test_sock_setsockopt_recvttl_invalid_params); - tcase_add_test(tc_utils, test_sock_getsockopt_recvttl_value); + tcase_add_test(tc_utils, test_sock_getsockopt_recvttl_enabled_state); tcase_add_test(tc_utils, test_sock_getsockopt_invalid_socket); tcase_add_test(tc_utils, test_sock_can_read_write_paths); tcase_add_test(tc_utils, test_sock_getsockopt_recvttl_invalid_params); @@ -309,11 +309,16 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_tcp_persist_cb_stops_when_window_reopens); tcase_add_test(tc_utils, test_poll_tcp_arp_request_on_miss); tcase_add_test(tc_utils, test_poll_udp_send_on_arp_hit); + tcase_add_test(tc_utils, test_poll_udp_send_on_arp_miss_requests_arp_and_retains_queue); tcase_add_test(tc_utils, test_poll_icmp_send_on_arp_hit); + tcase_add_test(tc_utils, test_poll_icmp_send_on_arp_miss_requests_arp_and_retains_queue); tcase_add_test(tc_utils, test_dhcp_timer_cb_paths); + tcase_add_test(tc_utils, test_dhcp_timer_cb_send_failure_does_not_consume_retry_budget); 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_send_request_send_failure_retries_next_tick); + tcase_add_test(tc_utils, test_dhcp_send_discover_send_failure_retries_next_tick); 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); @@ -370,6 +375,8 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_ll_send_frame_drops_oversize); tcase_add_test(tc_utils, test_ll_helpers_invalid_inputs); tcase_add_test(tc_utils, test_non_ethernet_recv_oversize_dropped); + tcase_add_test(tc_utils, test_non_ethernet_recv_wrapper_delivers_udp_and_skips_eth_filter); + tcase_add_test(tc_utils, test_non_ethernet_recv_ex_wrapper_delivers_udp_on_second_if); #endif tcase_add_test(tc_utils, test_dns_format_ptr_name); tcase_add_test(tc_utils, test_dns_skip_and_copy_name); @@ -378,6 +385,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_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); tcase_add_test(tc_utils, test_dns_resend_query_fails_without_cached_query_buffer); diff --git a/src/test/unit/unit_esp.c b/src/test/unit/unit_esp.c index e05badec..133dd48c 100644 --- a/src/test/unit/unit_esp.c +++ b/src/test/unit/unit_esp.c @@ -129,12 +129,62 @@ static uint32_t build_ip_packet(uint8_t *buf, size_t buf_size, return frame_len; } +static uint32_t build_udp_ip_packet(uint8_t *buf, size_t buf_size, + uint32_t src_ip, uint32_t dst_ip, + uint16_t src_port, uint16_t dst_port, + const uint8_t *payload, uint16_t payload_len) +{ + struct wolfIP_ip_packet *ip; + struct wolfIP_udp_datagram *udp; + uint32_t frame_len; + uint16_t udp_len = (uint16_t)(UDP_HEADER_LEN + payload_len); + + frame_len = build_ip_packet(buf, buf_size, WI_IPPROTO_UDP, NULL, udp_len); + ip = (struct wolfIP_ip_packet *)buf; + udp = (struct wolfIP_udp_datagram *)ip; + + ip->src = ee32(src_ip); + ip->dst = ee32(dst_ip); + udp->src_port = ee16(src_port); + udp->dst_port = ee16(dst_port); + udp->len = ee16(udp_len); + udp->csum = 0; + if (payload_len > 0U) { + memcpy(udp->data, payload, payload_len); + } + ip->csum = 0; + iphdr_set_checksum(ip); + + return frame_len; +} + static void esp_setup(void) { int ret = wolfIP_esp_init(); ck_assert_int_eq(ret, 0); } +static void esp_add_cbc_test_sas(void) +{ + int ret; + + ret = wolfIP_esp_sa_new_cbc_hmac(0, (uint8_t *)spi_rt, + atoip4(T_SRC), atoip4(T_DST), + (uint8_t *)k_aes128, sizeof(k_aes128), + ESP_AUTH_SHA256_RFC4868, + (uint8_t *)k_auth16, sizeof(k_auth16), + ESP_ICVLEN_HMAC_128); + ck_assert_int_eq(ret, 0); + + ret = wolfIP_esp_sa_new_cbc_hmac(1, (uint8_t *)spi_rt, + atoip4(T_SRC), atoip4(T_DST), + (uint8_t *)k_aes128, sizeof(k_aes128), + ESP_AUTH_SHA256_RFC4868, + (uint8_t *)k_auth16, sizeof(k_auth16), + ESP_ICVLEN_HMAC_128); + ck_assert_int_eq(ret, 0); +} + /* Creating an HMAC-only SA with valid params must succeed. */ START_TEST(test_sa_hmac_good) { @@ -1278,6 +1328,105 @@ START_TEST(test_wrap_rejects_ip_len_below_header) } END_TEST +START_TEST(test_ip_recv_esp_transport_delivers_udp_payload) +{ + static uint8_t buf[LINK_MTU + 256]; + struct wolfIP s; + struct wolfIP_ip_packet *ip = (struct wolfIP_ip_packet *)buf; + struct wolfIP_sockaddr_in sin; + uint8_t payload[] = { 'e', 's', 'p', '!' }; + uint8_t rxbuf[sizeof(payload)] = {0}; + uint32_t frame_len; + uint16_t ip_len; + int udp_sd; + int ret; + + wolfIP_init(&s); + esp_setup(); + esp_add_cbc_test_sas(); + wolfIP_ipconfig_set(&s, atoip4(T_DST), 0xFFFFFF00U, 0); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(1234); + sin.sin_addr.s_addr = ee32(atoip4(T_DST)); + ck_assert_int_eq(wolfIP_sock_bind(&s, udp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + + frame_len = build_udp_ip_packet(buf, sizeof(buf), atoip4(T_SRC), atoip4(T_DST), + 4321, 1234, payload, sizeof(payload)); + ip_len = (uint16_t)(frame_len - ETH_HEADER_LEN); + + ret = esp_transport_wrap(ip, &ip_len); + ck_assert_int_eq(ret, 0); + + frame_len = (uint32_t)ip_len + ETH_HEADER_LEN; + ip->proto = 0x32U; + ip->len = ee16(ip_len); + ip->csum = 0U; + iphdr_set_checksum(ip); + + ip_recv(&s, 0, ip, frame_len); + + ret = wolfIP_sock_recvfrom(&s, udp_sd, rxbuf, sizeof(rxbuf), 0, NULL, NULL); + ck_assert_int_eq(ret, (int)sizeof(payload)); + ck_assert_mem_eq(rxbuf, payload, sizeof(payload)); +} +END_TEST + +START_TEST(test_ip_recv_esp_transport_unwrap_failure_drops_packet) +{ + static uint8_t buf[LINK_MTU + 256]; + struct wolfIP s; + struct wolfIP_ip_packet *ip = (struct wolfIP_ip_packet *)buf; + struct wolfIP_sockaddr_in sin; + uint8_t payload[] = { 'b', 'a', 'd', '!' }; + uint8_t rxbuf[sizeof(payload)] = {0}; + uint32_t frame_len; + uint16_t ip_len; + uint32_t esp_len; + int udp_sd; + int ret; + + wolfIP_init(&s); + esp_setup(); + esp_add_cbc_test_sas(); + wolfIP_ipconfig_set(&s, atoip4(T_DST), 0xFFFFFF00U, 0); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(1234); + sin.sin_addr.s_addr = ee32(atoip4(T_DST)); + ck_assert_int_eq(wolfIP_sock_bind(&s, udp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + + frame_len = build_udp_ip_packet(buf, sizeof(buf), atoip4(T_SRC), atoip4(T_DST), + 4321, 1234, payload, sizeof(payload)); + ip_len = (uint16_t)(frame_len - ETH_HEADER_LEN); + + ret = esp_transport_wrap(ip, &ip_len); + ck_assert_int_eq(ret, 0); + + frame_len = (uint32_t)ip_len + ETH_HEADER_LEN; + ip->proto = 0x32U; + ip->len = ee16(ip_len); + ip->csum = 0U; + iphdr_set_checksum(ip); + + esp_len = frame_len - ETH_HEADER_LEN - IP_HEADER_LEN; + ip->data[esp_len - 1U] ^= 0xFFU; + + ip_recv(&s, 0, ip, frame_len); + + ret = wolfIP_sock_recvfrom(&s, udp_sd, rxbuf, sizeof(rxbuf), 0, NULL, NULL); + ck_assert_int_eq(ret, -WOLFIP_EAGAIN); +} +END_TEST + static Suite *esp_suite(void) { Suite *s; @@ -1352,6 +1501,11 @@ static Suite *esp_suite(void) tcase_add_test(tc, test_ciphertext_tamper_cbc_sha256); suite_add_tcase(s, tc); + tc = tcase_create("ip_recv"); + tcase_add_test(tc, test_ip_recv_esp_transport_delivers_udp_payload); + tcase_add_test(tc, test_ip_recv_esp_transport_unwrap_failure_drops_packet); + suite_add_tcase(s, tc); + /* No-SA outbound path */ tc = tcase_create("no_sa"); tcase_add_test(tc, test_wrap_no_matching_sa); diff --git a/src/test/unit/unit_tests_api.c b/src/test/unit/unit_tests_api.c index 5b5aeb1d..c6e30b36 100644 --- a/src/test/unit/unit_tests_api.c +++ b/src/test/unit/unit_tests_api.c @@ -3358,7 +3358,7 @@ START_TEST(test_sock_setsockopt_invalid_socket) } END_TEST -START_TEST(test_sock_getsockopt_recvttl_value) +START_TEST(test_sock_getsockopt_recvttl_enabled_state) { struct wolfIP s; int udp_sd; @@ -3374,7 +3374,7 @@ START_TEST(test_sock_getsockopt_recvttl_value) s.udpsockets[SOCKET_UNMARK(udp_sd)].last_pkt_ttl = 77; ck_assert_int_eq(wolfIP_sock_getsockopt(&s, udp_sd, WOLFIP_SOL_IP, WOLFIP_IP_RECVTTL, &value, &len), 0); - ck_assert_int_eq(value, 77); + ck_assert_int_eq(value, 1); } END_TEST diff --git a/src/test/unit/unit_tests_dns_dhcp.c b/src/test/unit/unit_tests_dns_dhcp.c index 556a9ca0..35f0eead 100644 --- a/src/test/unit/unit_tests_dns_dhcp.c +++ b/src/test/unit/unit_tests_dns_dhcp.c @@ -381,7 +381,7 @@ START_TEST(test_sock_opts_and_names) ttl = 0; ck_assert_int_eq(wolfIP_sock_getsockopt(&s, udp_sd, WOLFIP_SOL_IP, WOLFIP_IP_RECVTTL, &ttl, &optlen), 0); - ck_assert_int_eq(ttl, 42); + ck_assert_int_eq(ttl, 1); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; @@ -903,6 +903,60 @@ START_TEST(test_dhcp_send_request_rebinding_broadcasts_to_lease_expiry) } END_TEST +START_TEST(test_dhcp_send_request_send_failure_retries_next_tick) +{ + struct wolfIP s; + struct tsocket *ts; + struct ipconf *primary; + uint8_t tiny[2]; + + 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_xid = 1U; + s.dhcp_state = DHCP_REQUEST_SENT; + 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)]; + fifo_init(&ts->sock.udp.txbuf, tiny, sizeof(tiny)); + + ck_assert_int_eq(dhcp_send_request(&s), -WOLFIP_EAGAIN); + ck_assert_ptr_eq(fifo_peek(&ts->sock.udp.txbuf), NULL); + ck_assert_uint_eq(ts->local_ip, 0U); + ck_assert_uint_eq(find_timer_expiry(&s, s.dhcp_timer), s.last_tick + 1U); +} +END_TEST + +START_TEST(test_dhcp_send_discover_send_failure_retries_next_tick) +{ + struct wolfIP s; + struct tsocket *ts; + uint8_t tiny[2]; + + wolfIP_init(&s); + mock_link_init(&s); + s.dhcp_xid = 1U; + 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)]; + fifo_init(&ts->sock.udp.txbuf, tiny, sizeof(tiny)); + + ck_assert_int_eq(dhcp_send_discover(&s), -WOLFIP_EAGAIN); + ck_assert_ptr_eq(fifo_peek(&ts->sock.udp.txbuf), NULL); + ck_assert_int_eq(s.dhcp_state, DHCP_DISCOVER_SENT); + ck_assert_uint_eq(find_timer_expiry(&s, s.dhcp_timer), s.last_tick + 1U); +} +END_TEST + START_TEST(test_sock_connect_tcp_src_port_low) { struct wolfIP s; @@ -1998,6 +2052,34 @@ START_TEST(test_dns_send_query_schedules_timeout) } END_TEST +START_TEST(test_dns_send_query_send_failure_clears_outstanding_state) +{ + struct wolfIP s; + struct tsocket *ts; + uint16_t id = 0; + uint8_t tiny[2]; + + wolfIP_init(&s); + mock_link_init(&s); + s.dns_server = 0x08080808U; + s.last_tick = 100U; + + s.dns_udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(s.dns_udp_sd, 0); + ts = &s.udpsockets[SOCKET_UNMARK(s.dns_udp_sd)]; + fifo_init(&ts->sock.udp.txbuf, tiny, sizeof(tiny)); + + ck_assert_int_eq(dns_send_query(&s, "example.com", &id, DNS_A), -WOLFIP_EAGAIN); + ck_assert_uint_eq(id, 0U); + ck_assert_ptr_eq(fifo_peek(&ts->sock.udp.txbuf), NULL); + ck_assert_int_eq(s.dns_timer, NO_TIMER); + 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); +} +END_TEST + START_TEST(test_dns_resend_query_uses_stored_query_buffer) { struct wolfIP s; @@ -2556,7 +2638,7 @@ START_TEST(test_poll_tcp_ack_only_skips_send) fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); ck_assert_int_eq(enqueue_tcp_tx(ts, 0, TCP_FLAG_ACK), 0); - (void)wolfIP_poll(&s, 100); + (void)wolfIP_poll(&s, 2000); ck_assert_ptr_eq(fifo_peek(&ts->sock.tcp.txbuf), NULL); ck_assert_uint_gt(last_frame_sent_size, 0); @@ -3271,13 +3353,49 @@ START_TEST(test_poll_udp_send_on_arp_hit) (struct wolfIP_sockaddr *)&sin, sizeof(sin)); ck_assert_int_eq(ret, (int)sizeof(payload)); - (void)wolfIP_poll(&s, 100); + (void)wolfIP_poll(&s, 2000); ck_assert_uint_gt(last_frame_sent_size, 0); ck_assert_uint_eq(last_frame_sent[12], 0x08); ck_assert_uint_eq(last_frame_sent[13], 0x00); } END_TEST +START_TEST(test_poll_udp_send_on_arp_miss_requests_arp_and_retains_queue) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + uint8_t payload[4] = {1, 2, 3, 4}; + int ret; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + wolfIP_filter_set_callback(NULL, NULL); + last_frame_sent_size = 0; + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + ts = &s.udpsockets[SOCKET_UNMARK(udp_sd)]; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(1234); + sin.sin_addr.s_addr = ee32(0x0A000002U); + ret = wolfIP_sock_sendto(&s, udp_sd, payload, sizeof(payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)); + ck_assert_int_eq(ret, (int)sizeof(payload)); + ts->if_idx = TEST_PRIMARY_IF; + ck_assert_ptr_nonnull(fifo_peek(&ts->sock.udp.txbuf)); + + (void)wolfIP_poll(&s, 2000); + ck_assert_uint_gt(last_frame_sent_size, 0U); + ck_assert_uint_eq(last_frame_sent[12], 0x08); + ck_assert_uint_eq(last_frame_sent[13], 0x06); + ck_assert_ptr_nonnull(fifo_peek(&ts->sock.udp.txbuf)); +} +END_TEST + START_TEST(test_poll_icmp_send_on_arp_hit) { struct wolfIP s; @@ -3315,6 +3433,43 @@ START_TEST(test_poll_icmp_send_on_arp_hit) } END_TEST +START_TEST(test_poll_icmp_send_on_arp_miss_requests_arp_and_retains_queue) +{ + struct wolfIP s; + int icmp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + uint8_t payload[ICMP_HEADER_LEN + 1]; + int ret; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + wolfIP_filter_set_callback(NULL, NULL); + last_frame_sent_size = 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)]; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ee32(0x0A000002U); + memset(payload, 0, sizeof(payload)); + payload[0] = ICMP_ECHO_REQUEST; + ret = wolfIP_sock_sendto(&s, icmp_sd, payload, sizeof(payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)); + ck_assert_int_eq(ret, (int)sizeof(payload)); + ts->if_idx = TEST_PRIMARY_IF; + ck_assert_ptr_nonnull(fifo_peek(&ts->sock.udp.txbuf)); + + (void)wolfIP_poll(&s, 2000); + ck_assert_uint_gt(last_frame_sent_size, 0U); + ck_assert_uint_eq(last_frame_sent[12], 0x08); + ck_assert_uint_eq(last_frame_sent[13], 0x06); + ck_assert_ptr_nonnull(fifo_peek(&ts->sock.udp.txbuf)); +} +END_TEST + START_TEST(test_dhcp_timer_cb_paths) { struct wolfIP s; @@ -3361,6 +3516,38 @@ START_TEST(test_dhcp_timer_cb_paths) } END_TEST +START_TEST(test_dhcp_timer_cb_send_failure_does_not_consume_retry_budget) +{ + struct wolfIP s; + struct tsocket *ts; + uint8_t tiny[2]; + + wolfIP_init(&s); + mock_link_init(&s); + s.dhcp_xid = 1U; + 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)]; + fifo_init(&ts->sock.udp.txbuf, tiny, sizeof(tiny)); + + s.dhcp_state = DHCP_DISCOVER_SENT; + s.dhcp_timeout_count = 0; + dhcp_timer_cb(&s); + ck_assert_int_eq(s.dhcp_timeout_count, 0); + ck_assert_int_eq(s.dhcp_state, DHCP_DISCOVER_SENT); + ck_assert_uint_eq(find_timer_expiry(&s, s.dhcp_timer), s.last_tick + 1U); + + s.dhcp_state = DHCP_REQUEST_SENT; + s.dhcp_timeout_count = 0; + dhcp_timer_cb(&s); + ck_assert_int_eq(s.dhcp_timeout_count, 0); + ck_assert_int_eq(s.dhcp_state, DHCP_REQUEST_SENT); + ck_assert_uint_eq(find_timer_expiry(&s, s.dhcp_timer), s.last_tick + 1U); +} +END_TEST + START_TEST(test_dhcp_client_init_and_bound) { struct wolfIP s; diff --git a/src/test/unit/unit_tests_tcp_ack.c b/src/test/unit/unit_tests_tcp_ack.c index 00238bac..870e41d4 100644 --- a/src/test/unit/unit_tests_tcp_ack.c +++ b/src/test/unit/unit_tests_tcp_ack.c @@ -2961,6 +2961,134 @@ START_TEST(test_non_ethernet_recv_oversize_dropped) } END_TEST +START_TEST(test_non_ethernet_recv_wrapper_delivers_udp_and_skips_eth_filter) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + struct wolfIP_ll_dev *ll; + struct wolfIP_sockaddr_in sin; + struct wolfIP_sockaddr_in from; + uint8_t raw[IP_HEADER_LEN + UDP_HEADER_LEN + 4]; + uint8_t payload[4] = {1, 2, 3, 4}; + uint8_t udp_buf[sizeof(struct wolfIP_udp_datagram) + sizeof(payload)]; + struct wolfIP_udp_datagram *udp = (struct wolfIP_udp_datagram *)udp_buf; + uint8_t rxbuf[sizeof(payload)]; + socklen_t from_len = sizeof(from); + int ret; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + ll = wolfIP_getdev_ex(&s, TEST_PRIMARY_IF); + ck_assert_ptr_nonnull(ll); + ll->non_ethernet = 1; + + filter_block_reason = WOLFIP_FILT_RECEIVING; + wolfIP_filter_set_callback(test_filter_cb_block, NULL); + wolfIP_filter_set_eth_mask(WOLFIP_FILT_MASK(WOLFIP_FILT_RECEIVING)); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_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, udp_sd, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ts = &s.udpsockets[SOCKET_UNMARK(udp_sd)]; + + memset(udp_buf, 0, sizeof(udp_buf)); + udp->ip.ver_ihl = 0x45; + udp->ip.ttl = 64; + udp->ip.proto = WI_IPPROTO_UDP; + udp->ip.len = ee16(IP_HEADER_LEN + UDP_HEADER_LEN + sizeof(payload)); + udp->ip.src = ee32(0x0A000002U); + udp->ip.dst = ee32(0x0A000001U); + udp->src_port = ee16(5678); + udp->dst_port = ee16(1234); + udp->len = ee16(UDP_HEADER_LEN + sizeof(payload)); + memcpy(udp->data, payload, sizeof(payload)); + fix_udp_checksums(udp); + memcpy(raw, udp_buf + ETH_HEADER_LEN, sizeof(raw)); + + wolfIP_recv(&s, raw, (uint32_t)sizeof(raw)); + + memset(&from, 0, sizeof(from)); + ret = wolfIP_sock_recvfrom(&s, udp_sd, rxbuf, sizeof(rxbuf), 0, + (struct wolfIP_sockaddr *)&from, &from_len); + ck_assert_int_eq(ret, (int)sizeof(payload)); + ck_assert_mem_eq(rxbuf, payload, sizeof(payload)); + ck_assert_uint_eq(from.sin_addr.s_addr, ee32(0x0A000002U)); + ck_assert_uint_eq(from.sin_port, ee16(5678)); + ck_assert_ptr_eq(fifo_peek(&ts->sock.udp.rxbuf), NULL); + + wolfIP_filter_set_callback(NULL, NULL); + wolfIP_filter_set_eth_mask(0); +} +END_TEST + +START_TEST(test_non_ethernet_recv_ex_wrapper_delivers_udp_on_second_if) +{ + struct wolfIP s; + int udp_sd; + struct wolfIP_ll_dev *ll; + struct wolfIP_sockaddr_in sin; + struct wolfIP_sockaddr_in from; + uint8_t raw[IP_HEADER_LEN + UDP_HEADER_LEN + 4]; + uint8_t payload[4] = {9, 8, 7, 6}; + uint8_t udp_buf[sizeof(struct wolfIP_udp_datagram) + sizeof(payload)]; + struct wolfIP_udp_datagram *udp = (struct wolfIP_udp_datagram *)udp_buf; + uint8_t rxbuf[sizeof(payload)]; + socklen_t from_len = sizeof(from); + int ret; + + wolfIP_init(&s); + mock_link_init(&s); + mock_link_init_idx(&s, TEST_SECOND_IF, NULL); + s.ipconf[TEST_SECOND_IF].ip = 0x0A000101U; + s.ipconf[TEST_SECOND_IF].mask = 0xFFFFFF00U; + + ll = wolfIP_getdev_ex(&s, TEST_SECOND_IF); + ck_assert_ptr_nonnull(ll); + ll->non_ethernet = 1; + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(2345); + sin.sin_addr.s_addr = ee32(0x0A000101U); + ck_assert_int_eq(wolfIP_sock_bind(&s, udp_sd, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + + memset(udp_buf, 0, sizeof(udp_buf)); + udp->ip.ver_ihl = 0x45; + udp->ip.ttl = 64; + udp->ip.proto = WI_IPPROTO_UDP; + udp->ip.len = ee16(IP_HEADER_LEN + UDP_HEADER_LEN + sizeof(payload)); + udp->ip.src = ee32(0x0A000102U); + udp->ip.dst = ee32(0x0A000101U); + udp->src_port = ee16(6789); + udp->dst_port = ee16(2345); + udp->len = ee16(UDP_HEADER_LEN + sizeof(payload)); + memcpy(udp->data, payload, sizeof(payload)); + fix_udp_checksums(udp); + memcpy(raw, udp_buf + ETH_HEADER_LEN, sizeof(raw)); + + wolfIP_recv_ex(&s, TEST_SECOND_IF, raw, (uint32_t)sizeof(raw)); + + memset(&from, 0, sizeof(from)); + ret = wolfIP_sock_recvfrom(&s, udp_sd, rxbuf, sizeof(rxbuf), 0, + (struct wolfIP_sockaddr *)&from, &from_len); + ck_assert_int_eq(ret, (int)sizeof(payload)); + ck_assert_mem_eq(rxbuf, payload, sizeof(payload)); + ck_assert_uint_eq(from.sin_addr.s_addr, ee32(0x0A000102U)); + ck_assert_uint_eq(from.sin_port, ee16(6789)); +} +END_TEST + START_TEST(test_forward_packet_filter_drop_udp_icmp) { struct wolfIP s; diff --git a/src/wolfip.c b/src/wolfip.c index 1a8d7ba8..221678ff 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -4759,7 +4759,9 @@ int wolfIP_sock_getsockopt(struct wolfIP *s, int sockfd, int level, int optname, int value; if (!optval || !optlen || *optlen < (socklen_t)sizeof(int)) return -WOLFIP_EINVAL; - value = ts->recv_ttl ? ts->last_pkt_ttl : 0; + /* getsockopt reports whether TTL receipt is enabled; callers obtain + * the last observed TTL via recvmsg control data or wolfIP_sock_get_recv_ttl(). */ + value = ts->recv_ttl ? 1 : 0; memcpy(optval, &value, sizeof(int)); *optlen = sizeof(int); return 0; @@ -5219,6 +5221,7 @@ static void dhcp_schedule_lease_timer(struct wolfIP *s, static void dhcp_timer_cb(void *arg) { struct wolfIP *s = (struct wolfIP *)arg; + int ret; LOG("dhcp timeout\n"); if (!s) return; @@ -5226,15 +5229,17 @@ static void dhcp_timer_cb(void *arg) switch(s->dhcp_state) { case DHCP_DISCOVER_SENT: if (s->dhcp_timeout_count < DHCP_DISCOVER_RETRIES) { - dhcp_send_discover(s); - s->dhcp_timeout_count++; + ret = dhcp_send_discover(s); + if (ret >= 0) + s->dhcp_timeout_count++; } else s->dhcp_state = DHCP_OFF; break; case DHCP_REQUEST_SENT: if (s->dhcp_timeout_count < DHCP_REQUEST_RETRIES) { - dhcp_send_request(s); - s->dhcp_timeout_count++; + ret = dhcp_send_request(s); + if (ret >= 0) + s->dhcp_timeout_count++; } else s->dhcp_state = DHCP_OFF; break; @@ -5245,24 +5250,27 @@ static void dhcp_timer_cb(void *arg) } s->dhcp_state = DHCP_RENEWING; s->dhcp_timeout_count = 0; - dhcp_send_request(s); - s->dhcp_timeout_count++; + ret = dhcp_send_request(s); + if (ret >= 0) + 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++; + ret = dhcp_send_request(s); + if (ret >= 0) + 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++; + ret = dhcp_send_request(s); + if (ret >= 0) + s->dhcp_timeout_count++; break; default: break; @@ -5599,8 +5607,10 @@ static int dhcp_send_request(struct wolfIP *s) struct dhcp_option *opt = (struct dhcp_option *)(req.options); struct wolfIP_sockaddr_in sin; struct ipconf *primary = wolfIP_primary_ipconf(s); + uint64_t retry_at = s ? (s->last_tick + 1U) : 0; int renewing = (s->dhcp_state == DHCP_RENEWING); int rebinding = (s->dhcp_state == DHCP_REBINDING); + int ret; uint32_t opt_sz = 0; /* Prepare DHCP request */ memset(&req, 0, sizeof(struct dhcp_msg)); @@ -5655,13 +5665,21 @@ static int dhcp_send_request(struct wolfIP *s) 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, + ret = wolfIP_sock_sendto(s, s->dhcp_udp_sd, &req, DHCP_HEADER_LEN + opt_sz, 0, (struct wolfIP_sockaddr *)&sin, sizeof(struct wolfIP_sockaddr_in)); 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; + } + if (ret < 0) { + /* Retry on the next tick after local backpressure instead of + * waiting a full DHCP timeout for a request that never queued. */ + dhcp_schedule_timer_at(s, retry_at); + return ret; + } + if (!renewing && !rebinding) { dhcp_schedule_retry_timer(s, 0); } else if (renewing) { dhcp_schedule_retry_timer(s, s->dhcp_rebind_at); @@ -5685,8 +5703,9 @@ static int dhcp_send_discover(struct wolfIP *s) { struct dhcp_msg disc; struct dhcp_option *opt = (struct dhcp_option *)(disc.options); - struct wolfIP_timer tmr = { }; struct wolfIP_sockaddr_in sin; + uint64_t retry_at = s ? (s->last_tick + 1U) : 0; + int ret; uint32_t opt_sz = 0; /* Prepare DHCP discover */ memset(&disc, 0, sizeof(struct dhcp_msg)); @@ -5722,14 +5741,18 @@ static int dhcp_send_discover(struct wolfIP *s) sin.sin_port = ee16(DHCP_SERVER_PORT); sin.sin_addr.s_addr = ee32(0xFFFFFFFF); /* Broadcast */ sin.sin_family = AF_INET; - wolfIP_sock_sendto(s, s->dhcp_udp_sd, &disc, DHCP_HEADER_LEN + opt_sz, 0, + ret = wolfIP_sock_sendto(s, s->dhcp_udp_sd, &disc, DHCP_HEADER_LEN + opt_sz, 0, (struct wolfIP_sockaddr *)&sin, sizeof(struct wolfIP_sockaddr_in)); - tmr.expires = s->last_tick + DHCP_DISCOVER_TIMEOUT + (wolfIP_getrandom() % 200); - tmr.arg = s; - tmr.cb = dhcp_timer_cb; + if (ret < 0) { + /* Enter discover-sent before retrying so dhcp_timer_cb() continues + * DHCP startup after local backpressure. Retry on the next tick instead of + * waiting a full discover timeout for a packet that never queued. */ + s->dhcp_state = DHCP_DISCOVER_SENT; + dhcp_schedule_timer_at(s, retry_at); + return ret; + } + dhcp_schedule_timer_at(s, s->last_tick + DHCP_DISCOVER_TIMEOUT + (wolfIP_getrandom() % 200U)); s->dhcp_state = DHCP_DISCOVER_SENT; - - s->dhcp_timer = timers_binheap_insert(&s->timers, tmr); return 0; } @@ -6735,6 +6758,7 @@ static int dns_send_query(struct wolfIP *s, const char *dname, uint16_t *id, struct dns_question *q; char *q_name, *tok_start, *tok_end; struct wolfIP_sockaddr_in dns_srv; + int ret; uint32_t tok_len = 0; uint32_t label_len = 0; if (!dname || !id) return -22; @@ -6789,8 +6813,14 @@ static int dns_send_query(struct wolfIP *s, const char *dname, uint16_t *id, 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, s->dns_query_len, 0, + ret = wolfIP_sock_sendto(s, s->dns_udp_sd, buf, s->dns_query_len, 0, (struct wolfIP_sockaddr *)&dns_srv, sizeof(struct wolfIP_sockaddr_in)); + if (ret < 0) { + /* Roll back the outstanding query state when the first send never queues. */ + dns_abort_query(s); + *id = 0; + return ret; + } dns_schedule_timer(s); return 0; }