Skip to content
14 changes: 13 additions & 1 deletion src/test/unit/unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_sock_connect_tcp_txbuf_full_does_not_enter_syn_sent);
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);
tcase_add_test(tc_utils, test_sock_sendto_udp_encodes_dest_and_assigns_src_port);
tcase_add_test(tc_utils, test_sock_sendto_udp_addrlen_short);
tcase_add_test(tc_utils, test_sock_sendto_udp_len_too_large);
tcase_add_test(tc_utils, test_sock_sendto_udp_fifo_full);
Expand All @@ -383,6 +383,8 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_sock_sendto_udp_remote_ip_zero);
tcase_add_test(tc_utils, test_sock_sendto_udp_primary_ip_fallback);
tcase_add_test(tc_utils, test_sock_sendto_udp_zero_port_in_addr);
tcase_add_test(tc_utils, test_udp_sendto_connected_alt_dest_keeps_peer);
tcase_add_test(tc_utils, test_udp_sendto_connected_failed_sendto_keeps_peer);
tcase_add_test(tc_utils, test_sock_sendto_udp_src_port_low_adjusts);
tcase_add_test(tc_utils, test_sock_sendto_udp_local_ip_conf_null);
tcase_add_test(tc_utils, test_sock_sendto_udp_local_ip_from_primary);
Expand Down Expand Up @@ -973,6 +975,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_proto, test_icmp_input_dest_unreach_port_unreachable_mismatched_orig_src_port_ignored);
tcase_add_test(tc_proto, test_icmp_input_dest_unreach_port_unreachable_mismatched_orig_dst_port_ignored);
tcase_add_test(tc_proto, test_udp_sendto_and_recvfrom);
tcase_add_test(tc_proto, test_udp_sendto_unbound_socket_receives_reply);
tcase_add_test(tc_proto, test_udp_wildcard_bind_receives_all_local_addrs);
tcase_add_test(tc_proto, test_udp_sendto_respects_mtu_api);
tcase_add_test(tc_proto, test_udp_recvfrom_sets_remote_ip);
Expand Down Expand Up @@ -1194,6 +1197,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_core, test_sock_can_write_tcp_syn_sent_returns_zero);
tcase_add_test(tc_core, test_sock_can_write_tcp_established_with_space);
tcase_add_test(tc_core, test_sock_can_write_tcp_closed_returns_one);
tcase_add_test(tc_core, test_sock_can_write_tcp_close_wait_full_fifo_returns_zero);
tcase_add_test(tc_core, test_sock_can_write_tcp_invalid_fd);
#if WOLFIP_RAWSOCKETS
tcase_add_test(tc_core, test_sock_can_read_raw_empty);
Expand Down Expand Up @@ -1536,6 +1540,10 @@ Suite *wolf_suite(void)
tcase_add_test(tc_core, test_ip_recv_forward_ttl1_zero_payload_icmp_not_suppressed);
tcase_add_test(tc_core, test_ip_recv_forward_ttl1_partial_payload_quoted);
tcase_add_test(tc_core, test_forward_ttl_exceeded_copies_orig_tos);
tcase_add_test(tc_core, test_ip_recv_forward_df_oversize_sends_frag_needed);
tcase_add_test(tc_core, test_ip_recv_forward_nodf_oversize_dropped);
tcase_add_test(tc_core, test_ip_recv_forward_df_at_mtu_forwarded);
tcase_add_test(tc_core, test_ip_recv_forward_directed_bcast_ingress_net_not_forwarded);
tcase_add_test(tc_core, test_ip_recv_dest_matches_secondary_iface_ip_is_local);
tcase_add_test(tc_core, test_ip_recv_multicast_dst_not_forwarded);
tcase_add_test(tc_core, test_arp_recv_htype_not_ethernet_dropped);
Expand Down Expand Up @@ -1712,6 +1720,10 @@ Suite *wolf_suite(void)
tcase_add_test(tc_proto, test_vlan_api_delete_basic);
tcase_add_test(tc_proto, test_vlan_api_delete_physical_rejected);
tcase_add_test(tc_proto, test_vlan_api_delete_bad_ifidx_rejected);
#if WOLFIP_ENABLE_FORWARDING
tcase_add_test(tc_proto, test_vlan_delete_rejected_with_route);
#endif
tcase_add_test(tc_proto, test_vlan_delete_rejected_with_socket);
tcase_add_test(tc_proto, test_vlan_api_get_null_args_rejected);
tcase_add_test(tc_proto, test_vlan_api_get_dangling_parent_pointer_rejected);
tcase_add_test(tc_proto, test_vlan_tx_active_without_parent_rejected);
Expand Down
214 changes: 206 additions & 8 deletions src/test/unit/unit_tests_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,8 @@ START_TEST(test_udp_sendto_and_recvfrom)
uint16_t local_port = 4000;
uint16_t remote_port = 5000;
struct tsocket *ts;
struct wolfIP_udp_datagram *udp;
struct pkt_desc *desc;

wolfIP_init(&s);
mock_link_init(&s);
Expand All @@ -898,9 +900,14 @@ START_TEST(test_udp_sendto_and_recvfrom)

ts = &s.udpsockets[SOCKET_UNMARK(sd)];
ck_assert_uint_gt(ts->src_port, 0);
ck_assert_uint_eq(ts->dst_port, remote_port);
ck_assert_uint_eq(ts->remote_ip, remote_ip);
ck_assert_uint_gt(fifo_len(&ts->sock.udp.txbuf), 0);
/* The sendto destination is encoded into the queued datagram; the
* socket is not connected, so no persistent peer is set. */
desc = fifo_peek(&ts->sock.udp.txbuf);
ck_assert_ptr_nonnull(desc);
udp = (struct wolfIP_udp_datagram *)(ts->txmem + desc->pos + sizeof(*desc));
ck_assert_uint_eq(ee16(udp->dst_port), remote_port);
ck_assert_uint_eq(ee32(udp->ip.dst), remote_ip);

inject_udp_datagram(&s, TEST_PRIMARY_IF, remote_ip, local_ip, remote_port, local_port,
payload, sizeof(payload));
Expand All @@ -914,6 +921,50 @@ START_TEST(test_udp_sendto_and_recvfrom)
}
END_TEST

START_TEST(test_udp_sendto_unbound_socket_receives_reply)
{
struct wolfIP s;
int sd;
struct wolfIP_sockaddr_in sin;
struct wolfIP_sockaddr_in from;
socklen_t from_len = sizeof(from);
uint8_t payload[4] = {1, 2, 3, 4};
uint8_t rxbuf[8] = {0};
int ret;
ip4 local_ip = 0x0A000001U;
ip4 remote_ip = 0x0A000002U;
struct tsocket *ts;

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

sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP);
ck_assert_int_gt(sd, 0);

/* No bind: a plain socket()/sendto() client. The egress state
* (local_ip/if_idx) must still be set so a reply is accepted. */
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = ee16(5000);
sin.sin_addr.s_addr = ee32(remote_ip);
ret = wolfIP_sock_sendto(&s, sd, payload, sizeof(payload), 0,
(struct wolfIP_sockaddr *)&sin, sizeof(sin));
ck_assert_int_eq(ret, (int)sizeof(payload));

ts = &s.udpsockets[SOCKET_UNMARK(sd)];
ck_assert_uint_gt(ts->src_port, 0);
inject_udp_datagram(&s, TEST_PRIMARY_IF, remote_ip, local_ip, 5000,
ts->src_port, payload, sizeof(payload));

memset(&from, 0, sizeof(from));
ret = wolfIP_sock_recvfrom(&s, 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));
}
END_TEST

START_TEST(test_udp_wildcard_bind_receives_all_local_addrs)
{
struct wolfIP s;
Expand Down Expand Up @@ -3438,12 +3489,14 @@ START_TEST(test_sock_sendto_tcp_close_wait_allowed)
}
END_TEST

START_TEST(test_sock_sendto_udp_sets_dest_and_assigns)
START_TEST(test_sock_sendto_udp_encodes_dest_and_assigns_src_port)
{
struct wolfIP s;
int udp_sd;
struct tsocket *ts;
struct wolfIP_sockaddr_in sin;
struct wolfIP_udp_datagram *udp;
struct pkt_desc *desc;
uint8_t buf[4] = {1,2,3,4};
ip4 local_ip = 0x0A000001U;

Expand All @@ -3464,10 +3517,15 @@ START_TEST(test_sock_sendto_udp_sets_dest_and_assigns)

ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, sizeof(buf), 0,
(struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(buf));
ck_assert_uint_eq(ts->dst_port, 9999U);
ck_assert_uint_eq(ts->remote_ip, 0x0A000002U);
ck_assert_uint_ge(ts->src_port, 1024U);
ck_assert_uint_eq(ts->local_ip, local_ip);
/* The sendto destination is encoded into the queued datagram; the
* socket is not connected, so no persistent peer is set. */
desc = fifo_peek(&ts->sock.udp.txbuf);
ck_assert_ptr_nonnull(desc);
udp = (struct wolfIP_udp_datagram *)(ts->txmem + desc->pos + sizeof(*desc));
ck_assert_uint_eq(ee16(udp->dst_port), 9999U);
ck_assert_uint_eq(ee32(udp->ip.dst), 0x0A000002U);
ck_assert_uint_eq(ee32(udp->ip.src), local_ip);
}
END_TEST

Expand Down Expand Up @@ -3563,6 +3621,8 @@ START_TEST(test_sock_sendto_udp_primary_ip_fallback)
int udp_sd;
struct tsocket *ts;
struct wolfIP_sockaddr_in sin;
struct wolfIP_udp_datagram *udp;
struct pkt_desc *desc;
uint8_t buf[4] = {1,2,3,4};
ip4 primary_ip = 0x0A000001U;
ip4 secondary_ip = 0xC0A80101U;
Expand All @@ -3584,7 +3644,11 @@ START_TEST(test_sock_sendto_udp_primary_ip_fallback)

ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, sizeof(buf), 0,
(struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(buf));
ck_assert_uint_eq(ts->local_ip, primary_ip);
/* The fallback source address is encoded into the queued datagram. */
desc = fifo_peek(&ts->sock.udp.txbuf);
ck_assert_ptr_nonnull(desc);
udp = (struct wolfIP_udp_datagram *)(ts->txmem + desc->pos + sizeof(*desc));
ck_assert_uint_eq(ee32(udp->ip.src), primary_ip);
}
END_TEST

Expand Down Expand Up @@ -3616,6 +3680,134 @@ START_TEST(test_sock_sendto_udp_zero_port_in_addr)
}
END_TEST

/* A sendto to an alternate destination on a connected UDP socket must
* not steal the connected peer: the queued datagram carries the
* alternate destination, while the persistent peer and
* udp_try_recv's filter stay exactly as connect() set them. */
START_TEST(test_udp_sendto_connected_alt_dest_keeps_peer)
{
struct wolfIP s;
int udp_sd;
struct tsocket *ts;
struct wolfIP_sockaddr_in sin;
struct wolfIP_sockaddr_in from;
socklen_t peer_len = sizeof(struct wolfIP_sockaddr_in);
struct wolfIP_udp_datagram *udp;
struct pkt_desc *desc;
uint8_t buf[4] = {1, 2, 3, 4};
uint8_t rxbuf[16];
ip4 local_ip = 0x0A000001U;
ip4 peer1 = 0x0A000002U;
ip4 peer2 = 0x0A000003U;

wolfIP_init(&s);
mock_link_init(&s);
wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 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(5000);
sin.sin_addr.s_addr = ee32(local_ip);
ck_assert_int_eq(wolfIP_sock_bind(&s, udp_sd,
(struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0);

memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = ee16(6000);
sin.sin_addr.s_addr = ee32(peer1);
ck_assert_int_eq(wolfIP_sock_connect(&s, udp_sd,
(struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0);
ck_assert_uint_eq(ts->remote_ip, peer1);
ck_assert_uint_eq(ts->dst_port, 6000);

/* Alternate-destination sendto: the datagram goes to peer2 ... */
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = ee16(7000);
sin.sin_addr.s_addr = ee32(peer2);
ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, sizeof(buf), 0,
(struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(buf));

desc = fifo_peek(&ts->sock.udp.txbuf);
ck_assert_ptr_nonnull(desc);
udp = (struct wolfIP_udp_datagram *)(ts->txmem + desc->pos + sizeof(*desc));
ck_assert_uint_eq(ee32(udp->ip.dst), peer2);
ck_assert_uint_eq(ee16(udp->dst_port), 7000);

/* ... but the connected peer and its receive filter are untouched. */
ck_assert_uint_eq(ts->remote_ip, peer1);
ck_assert_uint_eq(ts->dst_port, 6000);

/* A reply from the connected peer is still accepted ... */
inject_udp_datagram(&s, TEST_PRIMARY_IF, peer1, local_ip, 6000, 5000,
buf, sizeof(buf));
ck_assert_int_eq(wolfIP_sock_can_read(&s, udp_sd), 1);
memset(&from, 0, sizeof(from));
ck_assert_int_ge(wolfIP_sock_recvfrom(&s, udp_sd, rxbuf, sizeof(rxbuf), 0,
(struct wolfIP_sockaddr *)&from, &peer_len), (int)sizeof(buf));

/* ... while a datagram from the alternate peer is not. */
inject_udp_datagram(&s, TEST_PRIMARY_IF, peer2, local_ip, 7000, 5000,
buf, sizeof(buf));
ck_assert_int_eq(wolfIP_sock_can_read(&s, udp_sd), 0);
}
END_TEST

/* A failed sendto (payload over the MTU) must not mutate the connected
* peer either: the destination resolution must not leave the socket's
* peer and receive filter changed when validation fails. */
START_TEST(test_udp_sendto_connected_failed_sendto_keeps_peer)
{
struct wolfIP s;
int udp_sd;
struct tsocket *ts;
struct wolfIP_sockaddr_in sin;
uint8_t big[1600];
ip4 local_ip = 0x0A000001U;
ip4 peer1 = 0x0A000002U;
ip4 peer2 = 0x0A000003U;

wolfIP_init(&s);
mock_link_init(&s);
wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 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(5000);
sin.sin_addr.s_addr = ee32(local_ip);
ck_assert_int_eq(wolfIP_sock_bind(&s, udp_sd,
(struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0);

memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = ee16(6000);
sin.sin_addr.s_addr = ee32(peer1);
ck_assert_int_eq(wolfIP_sock_connect(&s, udp_sd,
(struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0);

memset(big, 0, sizeof(big));
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = ee16(7000);
sin.sin_addr.s_addr = ee32(peer2);
/* 1600 > ip_mtu(1522) - IP/UDP headers: the send must fail ... */
ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, big, sizeof(big), 0,
(struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1);
/* ... and must leave the connected peer untouched. */
ck_assert_uint_eq(ts->remote_ip, peer1);
ck_assert_uint_eq(ts->dst_port, 6000);
ck_assert_uint_eq(fifo_len(&ts->sock.udp.txbuf), 0);
}
END_TEST

START_TEST(test_sock_sendto_udp_src_port_low_adjusts)
{
struct wolfIP s;
Expand Down Expand Up @@ -3685,6 +3877,8 @@ START_TEST(test_sock_sendto_udp_local_ip_from_primary)
int udp_sd;
struct tsocket *ts;
struct wolfIP_sockaddr_in sin;
struct wolfIP_udp_datagram *udp;
struct pkt_desc *desc;
uint8_t buf[4] = {1,2,3,4};
ip4 primary_ip = 0x0A000001U;

Expand All @@ -3709,7 +3903,11 @@ START_TEST(test_sock_sendto_udp_local_ip_from_primary)

ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, sizeof(buf), 0,
(struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(buf));
ck_assert_uint_eq(ts->local_ip, primary_ip);
/* The fallback source address is encoded into the queued datagram. */
desc = fifo_peek(&ts->sock.udp.txbuf);
ck_assert_ptr_nonnull(desc);
udp = (struct wolfIP_udp_datagram *)(ts->txmem + desc->pos + sizeof(*desc));
ck_assert_uint_eq(ee32(udp->ip.src), primary_ip);
}
END_TEST

Expand Down
Loading
Loading