diff --git a/README.md b/README.md index 2bae28e..691b733 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ A single network interface can be associated with the device. | **Transport** | UDP | Unicast datagrams, checksum | [RFC 768](https://datatracker.ietf.org/doc/html/rfc768) | | **Transport** | TCP | Connection management, reliable delivery | [RFC 793](https://datatracker.ietf.org/doc/html/rfc793), [RFC 9293](https://datatracker.ietf.org/doc/html/rfc9293) | | **Transport** | TCP Options: MSS | Maximum Segment Size negotiation | [RFC 793](https://datatracker.ietf.org/doc/html/rfc793) | -| **Transport** | TCP Options: Timestamps | RTT measurement, PAWS | [RFC 7323](https://datatracker.ietf.org/doc/html/rfc7323) | +| **Transport** | TCP Options: Timestamps | RTT measurement, PAWS, Window Scaling | [RFC 7323](https://datatracker.ietf.org/doc/html/rfc7323) | | **Transport** | TCP Congestion Control | Slow start, congestion avoidance | [RFC 5681](https://datatracker.ietf.org/doc/html/rfc5681) | | **Transport** | TCP Fast Retransmit | Triple duplicate ACK detection | [RFC 5681](https://datatracker.ietf.org/doc/html/rfc5681) | | **Application** | DHCP | Client only (DORA) | [RFC 2131](https://datatracker.ietf.org/doc/html/rfc2131) | diff --git a/src/port/posix/bsd_socket.c b/src/port/posix/bsd_socket.c index b1c7a8f..4c35e34 100644 --- a/src/port/posix/bsd_socket.c +++ b/src/port/posix/bsd_socket.c @@ -1431,7 +1431,7 @@ void __attribute__((constructor)) init_wolfip_posix() { wolfIP_ipconfig_set(IPSTACK, atoip4(WOLFIP_IP), atoip4("255.255.255.0"), atoip4(HOST_STACK_IP)); printf("IP: manually configured - %s\n", WOLFIP_IP); - sleep(1); + sleep(2); pthread_create(&wolfIP_thread, NULL, wolfIP_sock_posix_ip_loop, IPSTACK); in_the_stack = 0; } diff --git a/src/port/posix/tap_linux.c b/src/port/posix/tap_linux.c index d16858b..cb8b5db 100644 --- a/src/port/posix/tap_linux.c +++ b/src/port/posix/tap_linux.c @@ -101,9 +101,14 @@ int tap_init(struct wolfIP_ll_dev *ll, const char *ifname, uint32_t host_ip) tap_fd = -1; } } - if (tap_fd < 0) { - return -1; - } + if (tap_fd < 0) { + return -1; + } + { + int flags = fcntl(tap_fd, F_GETFL, 0); + if (flags >= 0) + (void)fcntl(tap_fd, F_SETFL, flags | O_NONBLOCK); + } } /* Get mac address */ if (ioctl(tap_fd, SIOCGIFHWADDR, &ifr) < 0) { diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index f59f884..90c61c8 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -48,9 +48,14 @@ /* MOCKS */ /* pseudo random number generator to mock the random number generator */ +static int test_rand_override_enabled; +static uint32_t test_rand_override_value; + uint32_t wolfIP_getrandom(void) { unsigned int seed = 0xDAC0FFEE; + if (test_rand_override_enabled) + return test_rand_override_value; srandom(seed); return random(); } @@ -487,9 +492,11 @@ START_TEST(test_fifo_peek_wraps_tail_when_head_lt_tail) f.tail = 4; f.h_wrap = 0; + /* With head at 0 and tail aligned, peek should return the current tail + * descriptor without altering tail or wrap state. */ desc = fifo_peek(&f); ck_assert_ptr_nonnull(desc); - ck_assert_uint_eq(f.tail, 0); + ck_assert_uint_eq(f.tail, 4); } END_TEST @@ -504,6 +511,7 @@ START_TEST(test_fifo_peek_no_wrap_when_space_available) f.tail = 4; f.h_wrap = 0; + /* When no wrap boundary is set, peek must not change tail. */ desc = fifo_peek(&f); ck_assert_ptr_nonnull(desc); ck_assert_uint_eq(f.tail, 4); @@ -548,6 +556,7 @@ START_TEST(test_fifo_pop_aligns_tail_to_head_returns_null) f.head = 4; f.tail = 1; + /* Aligning tail to head means the FIFO is empty; pop should return NULL. */ ck_assert_ptr_eq(fifo_pop(&f), NULL); } END_TEST @@ -563,12 +572,13 @@ START_TEST(test_fifo_pop_wraps_tail_when_head_lt_tail) f.tail = 4; f.h_wrap = 0; - desc = (struct pkt_desc *)data; - desc->pos = 0; + desc = (struct pkt_desc *)(data + 4); + desc->pos = 4; desc->len = 0; + /* Popping a zero-length packet should advance tail past the descriptor. */ ck_assert_ptr_nonnull(fifo_pop(&f)); - ck_assert_uint_eq(f.tail, sizeof(struct pkt_desc)); + ck_assert_uint_eq(f.tail, 4 + sizeof(struct pkt_desc)); } END_TEST @@ -589,6 +599,7 @@ START_TEST(test_fifo_pop_no_wrap_when_space_available) desc->len = 0; expected_tail = 4 + sizeof(struct pkt_desc); + /* With no wrap, pop should advance tail to the next descriptor. */ ck_assert_ptr_nonnull(fifo_pop(&f)); ck_assert_uint_eq(f.tail, expected_tail); } @@ -808,6 +819,52 @@ START_TEST(test_filter_socket_event_unknown_proto) } END_TEST +START_TEST(test_filter_socket_event_proto_variants) +{ + struct wolfIP s; + struct tsocket ts; + ip4 local_ip = 0x0A000001U; + ip4 remote_ip = 0x0A000002U; + uint16_t local_port = 1234; + uint16_t remote_port = 4321; + + wolfIP_init(&s); + memset(&ts, 0, sizeof(ts)); + ts.S = &s; + ts.if_idx = TEST_PRIMARY_IF; + + wolfIP_filter_set_callback(test_filter_cb, NULL); + wolfIP_filter_set_mask(WOLFIP_FILT_MASK(WOLFIP_FILT_CONNECTING)); + filter_cb_calls = 0; + + ts.proto = WI_IPPROTO_TCP; + (void)wolfIP_filter_notify_socket_event(WOLFIP_FILT_CONNECTING, &s, &ts, + local_ip, local_port, remote_ip, remote_port); + ck_assert_int_eq(filter_cb_calls, 1); + ck_assert_uint_eq(filter_last_event.meta.ip_proto, WOLFIP_FILTER_PROTO_TCP); + ck_assert_uint_eq(filter_last_event.meta.l4.tcp.src_port, ee16(local_port)); + ck_assert_uint_eq(filter_last_event.meta.l4.tcp.dst_port, ee16(remote_port)); + + ts.proto = WI_IPPROTO_UDP; + (void)wolfIP_filter_notify_socket_event(WOLFIP_FILT_CONNECTING, &s, &ts, + local_ip, local_port, remote_ip, remote_port); + ck_assert_int_eq(filter_cb_calls, 2); + ck_assert_uint_eq(filter_last_event.meta.ip_proto, WOLFIP_FILTER_PROTO_UDP); + ck_assert_uint_eq(filter_last_event.meta.l4.udp.src_port, ee16(local_port)); + ck_assert_uint_eq(filter_last_event.meta.l4.udp.dst_port, ee16(remote_port)); + + ts.proto = WI_IPPROTO_ICMP; + (void)wolfIP_filter_notify_socket_event(WOLFIP_FILT_CONNECTING, &s, &ts, + local_ip, local_port, remote_ip, remote_port); + ck_assert_int_eq(filter_cb_calls, 3); + ck_assert_uint_eq(filter_last_event.meta.ip_proto, WOLFIP_FILTER_PROTO_ICMP); + ck_assert_uint_eq(filter_last_event.meta.l4.icmp.type, 0); + ck_assert_uint_eq(filter_last_event.meta.l4.icmp.code, 0); + + wolfIP_filter_set_callback(NULL, NULL); +} +END_TEST + START_TEST(test_filter_setters_and_get_mask) { wolfIP_filter_set_mask(0x1234U); @@ -1129,6 +1186,176 @@ START_TEST(test_sock_bind_wrong_family) } END_TEST +START_TEST(test_sock_bind_invalid_fd) +{ + struct wolfIP s; + struct wolfIP_sockaddr_in sin; + + wolfIP_init(&s); + mock_link_init(&s); + + 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, -1, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_bind(&s, MARK_TCP_SOCKET | MAX_TCPSOCKETS, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); + ck_assert_int_eq(wolfIP_sock_bind(&s, MARK_UDP_SOCKET | MAX_UDPSOCKETS, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); + ck_assert_int_eq(wolfIP_sock_bind(&s, MARK_ICMP_SOCKET | MAX_ICMPSOCKETS, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); +} +END_TEST + +START_TEST(test_sock_bind_tcp_state_not_closed) +{ + 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_ESTABLISHED; + + 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, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); +} +END_TEST + +START_TEST(test_sock_bind_tcp_filter_blocks) +{ + 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->local_ip = 0; + ts->src_port = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(1234); + sin.sin_addr.s_addr = ee32(0x0A000001U); + + filter_block_reason = WOLFIP_FILT_BINDING; + filter_block_calls = 0; + wolfIP_filter_set_callback(test_filter_cb_block, NULL); + wolfIP_filter_set_mask(WOLFIP_FILT_MASK(WOLFIP_FILT_BINDING)); + + ck_assert_int_eq(wolfIP_sock_bind(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); + ck_assert_uint_eq(ts->local_ip, 0U); + ck_assert_uint_eq(ts->src_port, 0U); + + wolfIP_filter_set_callback(NULL, NULL); +} +END_TEST + +START_TEST(test_sock_bind_udp_src_port_nonzero) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 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)]; + ts->src_port = 1234; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(5678); + sin.sin_addr.s_addr = ee32(0x0A000001U); + + ck_assert_int_eq(wolfIP_sock_bind(&s, udp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); +} +END_TEST + +START_TEST(test_sock_bind_udp_filter_blocks) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 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)]; + ts->src_port = 0; + ts->local_ip = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(2222); + sin.sin_addr.s_addr = ee32(0x0A000001U); + + filter_block_reason = WOLFIP_FILT_BINDING; + filter_block_calls = 0; + wolfIP_filter_set_callback(test_filter_cb_block, NULL); + wolfIP_filter_set_mask(WOLFIP_FILT_MASK(WOLFIP_FILT_BINDING)); + + ck_assert_int_eq(wolfIP_sock_bind(&s, udp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); + ck_assert_uint_eq(ts->local_ip, 0U); + ck_assert_uint_eq(ts->src_port, 0U); + + wolfIP_filter_set_callback(NULL, NULL); +} +END_TEST + +START_TEST(test_sock_bind_icmp_success) +{ + struct wolfIP s; + int icmp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 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)]; + ts->src_port = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(7); + sin.sin_addr.s_addr = ee32(0x0A000001U); + + ck_assert_int_eq(wolfIP_sock_bind(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_uint_eq(ts->src_port, 7U); +} +END_TEST + START_TEST(test_sock_connect_wrong_family) { struct wolfIP s; @@ -1171,6 +1398,41 @@ START_TEST(test_sock_accept_error_paths) } END_TEST +START_TEST(test_sock_accept_non_tcp_socket_sets_addrlen) +{ + struct wolfIP s; + int udp_sd; + struct wolfIP_sockaddr_in sin; + socklen_t alen = sizeof(sin); + + wolfIP_init(&s); + mock_link_init(&s); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + + ck_assert_int_eq(wolfIP_sock_accept(&s, udp_sd, (struct wolfIP_sockaddr *)&sin, &alen), -WOLFIP_EINVAL); + ck_assert_uint_eq(alen, sizeof(sin)); +} +END_TEST + +START_TEST(test_sock_accept_null_addr_with_addrlen) +{ + struct wolfIP s; + int tcp_sd; + socklen_t alen = 123; + + 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); + + ck_assert_int_eq(wolfIP_sock_accept(&s, tcp_sd, NULL, &alen), -1); + ck_assert_uint_eq(alen, sizeof(struct wolfIP_sockaddr_in)); +} +END_TEST + START_TEST(test_sock_recvfrom_short_addrlen) { struct wolfIP s; @@ -1305,6 +1567,26 @@ START_TEST(test_sock_connect_bad_addrlen) } END_TEST +START_TEST(test_sock_connect_tcp_bad_addrlen) +{ + struct wolfIP s; + int tcp_sd; + struct wolfIP_sockaddr_in sin; + + 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); + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(80); + sin.sin_addr.s_addr = ee32(0x0A000002U); + + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, (socklen_t)1), -WOLFIP_EINVAL); +} +END_TEST + START_TEST(test_sock_connect_invalid_args) { struct wolfIP s; @@ -1479,10 +1761,10 @@ START_TEST(test_sock_connect_udp_bound_local_ip_no_match) } END_TEST -START_TEST(test_sock_connect_icmp_sets_local_ip_from_conf) +START_TEST(test_sock_connect_udp_bound_local_ip_match) { struct wolfIP s; - int icmp_sd; + int udp_sd; struct tsocket *ts; struct wolfIP_sockaddr_in sin; ip4 local_ip = 0x0A000001U; @@ -1491,28 +1773,33 @@ START_TEST(test_sock_connect_icmp_sets_local_ip_from_conf) mock_link_init(&s); wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 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)]; - ts->local_ip = 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)]; + ts->bound_local_ip = local_ip; + memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; + sin.sin_port = ee16(1234); sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_connect(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_int_eq(wolfIP_sock_connect(&s, udp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); ck_assert_uint_eq(ts->local_ip, local_ip); + ck_assert_uint_eq(ts->if_idx, TEST_PRIMARY_IF); } END_TEST -START_TEST(test_sock_connect_icmp_falls_back_to_primary) +START_TEST(test_sock_connect_icmp_sets_local_ip_from_conf) { struct wolfIP s; int icmp_sd; struct tsocket *ts; struct wolfIP_sockaddr_in sin; + ip4 local_ip = 0x0A000001U; wolfIP_init(&s); mock_link_init(&s); + wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); ck_assert_int_gt(icmp_sd, 0); @@ -1523,398 +1810,1004 @@ START_TEST(test_sock_connect_icmp_falls_back_to_primary) sin.sin_addr.s_addr = ee32(0x0A000002U); ck_assert_int_eq(wolfIP_sock_connect(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); - ck_assert_uint_eq(ts->local_ip, IPADDR_ANY); + ck_assert_uint_eq(ts->local_ip, local_ip); } END_TEST -START_TEST(test_sock_connect_tcp_established_returns_zero) +START_TEST(test_sock_connect_icmp_wrong_family) { struct wolfIP s; - int tcp_sd; - struct tsocket *ts; + int icmp_sd; struct wolfIP_sockaddr_in sin; 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; - + icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(icmp_sd, 0); memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = ee16(80); + sin.sin_family = 0; sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_int_eq(wolfIP_sock_connect(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EINVAL); } END_TEST -START_TEST(test_sock_connect_tcp_bound_local_ip_no_match) +START_TEST(test_sock_connect_icmp_local_ip_pre_set) { struct wolfIP s; - int tcp_sd; + int icmp_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->bound_local_ip = 0x0B000001U; + 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)]; + ts->local_ip = 0x0A000001U; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; - sin.sin_port = ee16(80); sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_connect(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_uint_eq(ts->local_ip, 0x0A000001U); } END_TEST -START_TEST(test_sock_connect_tcp_local_ip_from_conf) +START_TEST(test_sock_connect_icmp_conf_null) { struct wolfIP s; - int tcp_sd; + int icmp_sd; struct tsocket *ts; struct wolfIP_sockaddr_in sin; - ip4 local_ip = 0x0A000001U; wolfIP_init(&s); mock_link_init(&s); - wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); + s.if_count = 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; + 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)]; + ts->local_ip = 0; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; - sin.sin_port = ee16(80); 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_eq(ts->local_ip, local_ip); + ck_assert_int_eq(wolfIP_sock_connect(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_uint_eq(ts->local_ip, IPADDR_ANY); } END_TEST -START_TEST(test_sock_connect_tcp_local_ip_from_primary) +START_TEST(test_sock_connect_icmp_falls_back_to_primary) { struct wolfIP s; - int tcp_sd; + int icmp_sd; struct tsocket *ts; struct wolfIP_sockaddr_in sin; 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_CLOSED; - + 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)]; + ts->local_ip = 0; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; - sin.sin_port = ee16(80); 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(wolfIP_sock_connect(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); ck_assert_uint_eq(ts->local_ip, IPADDR_ANY); } END_TEST -START_TEST(test_sock_accept_negative_fd) +START_TEST(test_sock_connect_icmp_primary_ip_any) { struct wolfIP s; + int icmp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; wolfIP_init(&s); mock_link_init(&s); - ck_assert_int_eq(wolfIP_sock_accept(&s, -1, NULL, NULL), -WOLFIP_EINVAL); -} -END_TEST - -START_TEST(test_sock_accept_invalid_tcp_fd) -{ - struct wolfIP s; - int bad_fd = MARK_TCP_SOCKET | MAX_TCPSOCKETS; + 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)]; + ts->local_ip = 0; - wolfIP_init(&s); - mock_link_init(&s); + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_accept(&s, bad_fd, NULL, NULL), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_connect(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_uint_eq(ts->local_ip, IPADDR_ANY); } END_TEST -START_TEST(test_sock_accept_success_sets_addr) +START_TEST(test_sock_connect_icmp_primary_ip_fallback) { struct wolfIP s; - int listen_sd; - int client_sd; - struct tsocket *listener; + int icmp_sd; + struct tsocket *ts; struct wolfIP_sockaddr_in sin; - socklen_t alen = sizeof(sin); + ip4 primary_ip = 0x0A000001U; wolfIP_init(&s); mock_link_init(&s); - wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + s.if_count = TEST_SECOND_IF + 1; + s.ipconf[TEST_PRIMARY_IF].ip = primary_ip; + s.ipconf[TEST_PRIMARY_IF].mask = 0xFFFFFF00U; + s.ipconf[TEST_SECOND_IF].ip = IPADDR_ANY; + s.ipconf[TEST_SECOND_IF].gw = 0xC0A801FEU; + + 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)]; + ts->local_ip = 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); - - inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); - listener = &s.tcpsockets[SOCKET_UNMARK(listen_sd)]; - ck_assert_int_eq(listener->sock.tcp.state, TCP_SYN_RCVD); + sin.sin_addr.s_addr = ee32(0x0B000001U); - client_sd = wolfIP_sock_accept(&s, listen_sd, (struct wolfIP_sockaddr *)&sin, &alen); - ck_assert_int_gt(client_sd, 0); - ck_assert_uint_eq(alen, sizeof(sin)); - ck_assert_uint_eq(sin.sin_family, AF_INET); + ck_assert_int_eq(wolfIP_sock_connect(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_uint_eq(ts->local_ip, primary_ip); } END_TEST -START_TEST(test_sock_accept_no_available_socket) +START_TEST(test_sock_connect_tcp_established_returns_zero) { struct wolfIP s; - int listen_sd; - struct wolfIP_sockaddr_in sin; + int tcp_sd; struct tsocket *ts; - int i; + struct wolfIP_sockaddr_in sin; 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); + 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; + 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); - - inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); - - for (i = 0; i < MAX_TCPSOCKETS; i++) { - ts = &s.tcpsockets[i]; - ts->proto = WI_IPPROTO_TCP; - ts->sock.tcp.state = TCP_ESTABLISHED; - } + sin.sin_port = ee16(80); + sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -1); + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); } END_TEST -START_TEST(test_sock_accept_no_free_socket_syn_rcvd) +START_TEST(test_sock_connect_tcp_bound_local_ip_match) { struct wolfIP s; - int listen_sd; - struct wolfIP_sockaddr_in sin; + int tcp_sd; struct tsocket *ts; - int i; + struct wolfIP_sockaddr_in sin; + ip4 local_ip = 0x0A000001U; wolfIP_init(&s); mock_link_init(&s); - wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + wolfIP_ipconfig_set(&s, local_ip, 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->bound_local_ip = local_ip; - 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); - - inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); - ts = &s.tcpsockets[SOCKET_UNMARK(listen_sd)]; - ck_assert_int_eq(ts->sock.tcp.state, TCP_SYN_RCVD); - - for (i = 0; i < MAX_TCPSOCKETS; i++) { - if (i == SOCKET_UNMARK(listen_sd)) { - continue; - } - s.tcpsockets[i].proto = WI_IPPROTO_TCP; - s.tcpsockets[i].sock.tcp.state = TCP_ESTABLISHED; - } + sin.sin_port = ee16(80); + sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -1); + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EAGAIN); + ck_assert_uint_eq(ts->local_ip, local_ip); + ck_assert_uint_eq(ts->if_idx, TEST_PRIMARY_IF); } END_TEST -START_TEST(test_sock_accept_listen_no_connection) +START_TEST(test_sock_connect_tcp_bound_local_ip_no_match) { struct wolfIP s; - int listen_sd; + 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); - listen_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); - ck_assert_int_gt(listen_sd, 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->bound_local_ip = 0x0B000001U; + 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); + sin.sin_port = ee16(80); + sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EINVAL); } END_TEST -START_TEST(test_sock_accept_bound_local_ip_no_match) +START_TEST(test_sock_connect_tcp_filter_blocks) { struct wolfIP s; - int listen_sd; - int client_sd; - struct tsocket *listener; + 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); - listen_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); - ck_assert_int_gt(listen_sd, 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; + 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); + sin.sin_port = ee16(80); + sin.sin_addr.s_addr = ee32(0x0A000002U); - listener = &s.tcpsockets[SOCKET_UNMARK(listen_sd)]; - listener->bound_local_ip = 0x0A000001U; - listener->if_idx = TEST_PRIMARY_IF; + filter_block_reason = WOLFIP_FILT_CONNECTING; + filter_block_calls = 0; + wolfIP_filter_set_callback(test_filter_cb_block, NULL); + wolfIP_filter_set_mask(WOLFIP_FILT_MASK(WOLFIP_FILT_CONNECTING)); - inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); - s.if_count = 0; + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); + ck_assert_int_eq(ts->sock.tcp.state, TCP_CLOSED); - client_sd = wolfIP_sock_accept(&s, listen_sd, NULL, NULL); - ck_assert_int_gt(client_sd, 0); - ck_assert_uint_eq(listener->if_idx, TEST_PRIMARY_IF); + wolfIP_filter_set_callback(NULL, NULL); } END_TEST - -START_TEST(test_sock_sendto_error_paths) +START_TEST(test_sock_connect_tcp_local_ip_from_conf) { struct wolfIP s; int tcp_sd; - int udp_sd; - int icmp_sd; + struct tsocket *ts; struct wolfIP_sockaddr_in sin; - uint8_t buf[8]; + ip4 local_ip = 0x0A000001U; wolfIP_init(&s); mock_link_init(&s); - wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); ck_assert_int_gt(tcp_sd, 0); - ck_assert_int_eq(wolfIP_sock_sendto(&s, tcp_sd, buf, sizeof(buf), 0, NULL, 0), -1); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + ts->sock.tcp.state = TCP_CLOSED; - 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(80); + 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_eq(ts->local_ip, local_ip); +} +END_TEST + +START_TEST(test_sock_connect_tcp_local_ip_from_primary) +{ + struct wolfIP s; + int tcp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + + 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_CLOSED; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(80); + 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_eq(ts->local_ip, IPADDR_ANY); +} +END_TEST + +START_TEST(test_sock_connect_tcp_primary_ip_fallback) +{ + struct wolfIP s; + int tcp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + ip4 primary_ip = 0x0A000001U; + + wolfIP_init(&s); + mock_link_init(&s); + s.if_count = TEST_SECOND_IF + 1; + s.ipconf[TEST_PRIMARY_IF].ip = primary_ip; + s.ipconf[TEST_PRIMARY_IF].mask = 0xFFFFFF00U; + s.ipconf[TEST_SECOND_IF].ip = IPADDR_ANY; + s.ipconf[TEST_SECOND_IF].gw = 0xC0A801FEU; + + 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; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(80); + sin.sin_addr.s_addr = ee32(0x0B000001U); + + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EAGAIN); + ck_assert_uint_eq(ts->local_ip, primary_ip); +} +END_TEST +START_TEST(test_sock_accept_negative_fd) +{ + struct wolfIP s; + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_sock_accept(&s, -1, NULL, NULL), -WOLFIP_EINVAL); +} +END_TEST + +START_TEST(test_sock_connect_tcp_conf_null_primary_null) +{ + struct wolfIP s; + int tcp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + + wolfIP_init(&s); + mock_link_init(&s); + s.if_count = 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; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(80); + 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_eq(ts->local_ip, IPADDR_ANY); +} +END_TEST + +START_TEST(test_sock_connect_tcp_state_not_closed) +{ + struct wolfIP s; + int tcp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + + 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_LISTEN; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(80); + sin.sin_addr.s_addr = ee32(0x0A000002U); + + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EINVAL); +} +END_TEST +START_TEST(test_sock_accept_invalid_tcp_fd) +{ + struct wolfIP s; + int bad_fd = MARK_TCP_SOCKET | MAX_TCPSOCKETS; + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_sock_accept(&s, bad_fd, NULL, NULL), -WOLFIP_EINVAL); +} +END_TEST + +START_TEST(test_sock_accept_success_sets_addr) +{ + struct wolfIP s; + int listen_sd; + int client_sd; + struct tsocket *listener; + struct wolfIP_sockaddr_in sin; + socklen_t alen = sizeof(sin); + + 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); + + inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); + listener = &s.tcpsockets[SOCKET_UNMARK(listen_sd)]; + ck_assert_int_eq(listener->sock.tcp.state, TCP_SYN_RCVD); + + client_sd = wolfIP_sock_accept(&s, listen_sd, (struct wolfIP_sockaddr *)&sin, &alen); + ck_assert_int_gt(client_sd, 0); + ck_assert_uint_eq(alen, sizeof(sin)); + ck_assert_uint_eq(sin.sin_family, AF_INET); +} +END_TEST + +START_TEST(test_sock_accept_no_available_socket) +{ + struct wolfIP s; + int listen_sd; + struct wolfIP_sockaddr_in sin; + struct tsocket *ts; + int i; + + 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); + + inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); + + for (i = 0; i < MAX_TCPSOCKETS; i++) { + ts = &s.tcpsockets[i]; + ts->proto = WI_IPPROTO_TCP; + ts->sock.tcp.state = TCP_ESTABLISHED; + } + + ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -1); +} +END_TEST + +START_TEST(test_sock_accept_no_free_socket_syn_rcvd) +{ + struct wolfIP s; + int listen_sd; + struct wolfIP_sockaddr_in sin; + struct tsocket *ts; + int i; + + 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); + + inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); + ts = &s.tcpsockets[SOCKET_UNMARK(listen_sd)]; + ck_assert_int_eq(ts->sock.tcp.state, TCP_SYN_RCVD); + + for (i = 0; i < MAX_TCPSOCKETS; i++) { + if (i == SOCKET_UNMARK(listen_sd)) { + continue; + } + s.tcpsockets[i].proto = WI_IPPROTO_TCP; + s.tcpsockets[i].sock.tcp.state = TCP_ESTABLISHED; + } + + ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -1); +} +END_TEST + +START_TEST(test_sock_accept_listen_no_connection) +{ + struct wolfIP s; + int listen_sd; + struct wolfIP_sockaddr_in sin; + + 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); + + ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -WOLFIP_EAGAIN); +} +END_TEST + +START_TEST(test_sock_accept_bound_local_ip_no_match) +{ + struct wolfIP s; + int listen_sd; + int client_sd; + struct tsocket *listener; + struct wolfIP_sockaddr_in sin; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + listen_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(listen_sd, 0); + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(1234); + sin.sin_addr.s_addr = ee32(0x0A000001U); + ck_assert_int_eq(wolfIP_sock_bind(&s, listen_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_int_eq(wolfIP_sock_listen(&s, listen_sd, 1), 0); + + listener = &s.tcpsockets[SOCKET_UNMARK(listen_sd)]; + listener->bound_local_ip = 0x0A000001U; + listener->if_idx = TEST_PRIMARY_IF; + + inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); + s.if_count = 0; + + client_sd = wolfIP_sock_accept(&s, listen_sd, NULL, NULL); + ck_assert_int_gt(client_sd, 0); + ck_assert_uint_eq(listener->if_idx, TEST_PRIMARY_IF); +} +END_TEST + +START_TEST(test_sock_sendto_error_paths) +{ + struct wolfIP s; + int tcp_sd; + int udp_sd; + int icmp_sd; + struct wolfIP_sockaddr_in sin; + uint8_t buf[8]; + + 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); + ck_assert_int_eq(wolfIP_sock_sendto(&s, tcp_sd, buf, sizeof(buf), 0, NULL, 0), -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(1234); + sin.sin_addr.s_addr = ee32(0x0A000002U); + ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, (size_t)(WI_IP_MTU), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); + + icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(icmp_sd, 0); + ck_assert_int_eq(wolfIP_sock_sendto(&s, icmp_sd, buf, (size_t)(ICMP_HEADER_LEN - 1), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EINVAL); +} +END_TEST + +START_TEST(test_sock_sendto_null_buf_or_len_zero) +{ + struct wolfIP s; + int udp_sd; + uint8_t buf[1] = {0}; + + wolfIP_init(&s); + mock_link_init(&s); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, NULL, 1, 0, NULL, 0), -1); + ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, 0, 0, NULL, 0), -1); +} +END_TEST + +START_TEST(test_sock_sendto_tcp_not_established) +{ + struct wolfIP s; + int tcp_sd; + struct tsocket *ts; + uint8_t buf[4] = {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_SYN_SENT; + + ck_assert_int_eq(wolfIP_sock_sendto(&s, tcp_sd, buf, sizeof(buf), 0, NULL, 0), -1); +} +END_TEST + +START_TEST(test_sock_sendto_udp_sets_dest_and_assigns) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + uint8_t buf[4] = {1,2,3,4}; + ip4 local_ip = 0x0A000001U; + + 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)]; + ts->src_port = 0; + ts->local_ip = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(9999); sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, (size_t)(WI_IP_MTU), 0, - (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); + + 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); +} +END_TEST + +START_TEST(test_sock_sendto_icmp_assigns_src_port_and_sets_echo_id) +{ + struct wolfIP s; + int icmp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + uint8_t payload[ICMP_HEADER_LEN] = {0}; + struct pkt_desc *desc; + struct wolfIP_icmp_packet *icmp; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); ck_assert_int_gt(icmp_sd, 0); - ck_assert_int_eq(wolfIP_sock_sendto(&s, icmp_sd, buf, (size_t)(ICMP_HEADER_LEN - 1), 0, - (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EINVAL); + ts = &s.icmpsockets[SOCKET_UNMARK(icmp_sd)]; + ts->src_port = 0; + ts->local_ip = 0; + + payload[0] = ICMP_ECHO_REQUEST; + payload[1] = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = 0; + sin.sin_addr.s_addr = ee32(0x0A000002U); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, icmp_sd, payload, sizeof(payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(payload)); + ck_assert_uint_ne(ts->src_port, 0); + ck_assert_uint_eq(ts->local_ip, 0x0A000001U); + + desc = fifo_peek(&ts->sock.udp.txbuf); + ck_assert_ptr_nonnull(desc); + icmp = (struct wolfIP_icmp_packet *)(ts->txmem + desc->pos + sizeof(*desc)); + ck_assert_uint_eq(icmp_echo_id(icmp), ts->src_port); } END_TEST -START_TEST(test_sock_sendto_null_buf_or_len_zero) +START_TEST(test_sock_sendto_invalid_socket_ids) +{ + struct wolfIP s; + uint8_t buf[1] = {0}; + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, MARK_TCP_SOCKET | MAX_TCPSOCKETS, buf, sizeof(buf), 0, NULL, 0), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_sendto(&s, MARK_UDP_SOCKET | MAX_UDPSOCKETS, buf, sizeof(buf), 0, NULL, 0), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_sendto(&s, MARK_ICMP_SOCKET | MAX_ICMPSOCKETS, buf, sizeof(buf), 0, NULL, 0), -WOLFIP_EINVAL); +} +END_TEST + +START_TEST(test_sock_sendto_non_socket_returns_minus_one) { struct wolfIP s; - int udp_sd; uint8_t buf[1] = {0}; wolfIP_init(&s); mock_link_init(&s); + ck_assert_int_eq(wolfIP_sock_sendto(&s, 1, buf, sizeof(buf), 0, NULL, 0), -1); +} +END_TEST + +START_TEST(test_sock_sendto_udp_remote_ip_zero) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + uint8_t buf[4] = {1,2,3,4}; + + wolfIP_init(&s); + mock_link_init(&s); + 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)]; + ts->dst_port = 1234; + ts->remote_ip = 0; - ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, NULL, 1, 0, NULL, 0), -1); - ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, 0, 0, NULL, 0), -1); + ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, sizeof(buf), 0, NULL, 0), -1); } END_TEST -START_TEST(test_sock_sendto_tcp_not_established) +START_TEST(test_sock_sendto_udp_primary_ip_fallback) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + uint8_t buf[4] = {1,2,3,4}; + ip4 primary_ip = 0x0A000001U; + ip4 secondary_ip = 0xC0A80101U; + + setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip); + s.ipconf[TEST_SECOND_IF].ip = IPADDR_ANY; + s.ipconf[TEST_SECOND_IF].gw = 0xC0A801FEU; + + 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)]; + ts->src_port = 0; + ts->local_ip = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(7777); + sin.sin_addr.s_addr = ee32(0xC0A80199U); + + 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); +} +END_TEST + +START_TEST(test_sock_sendto_udp_zero_port_in_addr) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + uint8_t buf[4] = {1,2,3,4}; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 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)]; + ts->src_port = 1234; + ts->local_ip = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = 0; + sin.sin_addr.s_addr = ee32(0x0A000002U); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, sizeof(buf), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); +} +END_TEST + +START_TEST(test_sock_sendto_udp_src_port_low_adjusts) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + uint8_t buf[4] = {1,2,3,4}; + ip4 local_ip = 0x0A000001U; + + 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)]; + ts->src_port = 0; + ts->local_ip = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(9999); + sin.sin_addr.s_addr = ee32(0x0A000002U); + + test_rand_override_enabled = 1; + test_rand_override_value = 5; + ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, sizeof(buf), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(buf)); + test_rand_override_enabled = 0; + + ck_assert_uint_eq(ts->src_port, 5U + 1024U); +} +END_TEST + +START_TEST(test_sock_sendto_udp_local_ip_conf_null) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + uint8_t buf[4] = {1,2,3,4}; + + wolfIP_init(&s); + mock_link_init(&s); + s.if_count = 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)]; + ts->src_port = 1234; + ts->local_ip = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(7777); + sin.sin_addr.s_addr = ee32(0x0A000002U); + + 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, 0U); +} +END_TEST + +START_TEST(test_sock_sendto_udp_local_ip_from_primary) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + uint8_t buf[4] = {1,2,3,4}; + ip4 primary_ip = 0x0A000001U; + + wolfIP_init(&s); + mock_link_init(&s); + s.if_count = TEST_SECOND_IF + 1; + s.ipconf[TEST_PRIMARY_IF].ip = primary_ip; + s.ipconf[TEST_PRIMARY_IF].mask = 0xFFFFFF00U; + s.ipconf[TEST_SECOND_IF].ip = IPADDR_ANY; + s.ipconf[TEST_SECOND_IF].gw = 0xC0A801FEU; + + 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)]; + ts->src_port = 1234; + ts->local_ip = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(7777); + sin.sin_addr.s_addr = ee32(0x0B000001U); + + 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); +} +END_TEST + +START_TEST(test_sock_sendto_tcp_multiple_segments_flags) { struct wolfIP s; int tcp_sd; struct tsocket *ts; - uint8_t buf[4] = {0}; + size_t seg_payload = TCP_MSS - TCP_OPTIONS_LEN; + size_t payload_len = seg_payload * 2; + uint8_t buf[TCP_MSS * 2]; + uint8_t txbuf[4096]; + struct pkt_desc *first; + struct pkt_desc *second; + struct wolfIP_tcp_seg *tcp1; + struct wolfIP_tcp_seg *tcp2; + int ret; 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_SYN_SENT; + 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; + ts->src_port = 1234; + ts->dst_port = 4321; + ts->local_ip = 0x0A000001U; + ts->remote_ip = 0x0A000002U; + queue_init(&ts->sock.tcp.rxbuf, ts->rxmem, RXBUF_SIZE, 0); + fifo_init(&ts->sock.tcp.txbuf, txbuf, sizeof(txbuf)); + + memset(buf, 0xAA, payload_len); + ret = wolfIP_sock_sendto(&s, tcp_sd, buf, payload_len, 0, NULL, 0); + ck_assert_int_eq(ret, (int)payload_len); - ck_assert_int_eq(wolfIP_sock_sendto(&s, tcp_sd, buf, sizeof(buf), 0, NULL, 0), -1); + first = fifo_peek(&ts->sock.tcp.txbuf); + ck_assert_ptr_nonnull(first); + tcp1 = (struct wolfIP_tcp_seg *)(txbuf + first->pos + sizeof(*first)); + ck_assert_uint_eq(tcp1->flags & 0x18, 0x18); + + second = fifo_next(&ts->sock.tcp.txbuf, first); + ck_assert_ptr_nonnull(second); + tcp2 = (struct wolfIP_tcp_seg *)(txbuf + second->pos + sizeof(*second)); + ck_assert_uint_eq(tcp2->flags & 0x18, 0x10); } END_TEST -START_TEST(test_sock_sendto_udp_sets_dest_and_assigns) +START_TEST(test_sock_sendto_icmp_src_port_zero_random_zero_sets_one) { struct wolfIP s; - int udp_sd; + int icmp_sd; struct tsocket *ts; struct wolfIP_sockaddr_in sin; - uint8_t buf[4] = {1,2,3,4}; - ip4 local_ip = 0x0A000001U; + uint8_t payload[ICMP_HEADER_LEN] = {0}; wolfIP_init(&s); mock_link_init(&s); - wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); + wolfIP_ipconfig_set(&s, 0x0A000001U, 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)]; + 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)]; ts->src_port = 0; ts->local_ip = 0; + payload[0] = ICMP_ECHO_REQUEST; + memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; - sin.sin_port = ee16(9999); sin.sin_addr.s_addr = ee32(0x0A000002U); - 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); + test_rand_override_enabled = 1; + test_rand_override_value = 0; + ck_assert_int_eq(wolfIP_sock_sendto(&s, icmp_sd, payload, sizeof(payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(payload)); + test_rand_override_enabled = 0; + + ck_assert_uint_eq(ts->src_port, 1U); } END_TEST -START_TEST(test_sock_sendto_icmp_assigns_src_port_and_sets_echo_id) +START_TEST(test_sock_sendto_icmp_non_echo_no_set_id) { struct wolfIP s; int icmp_sd; @@ -1931,94 +2824,119 @@ START_TEST(test_sock_sendto_icmp_assigns_src_port_and_sets_echo_id) 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)]; - ts->src_port = 0; + ts->src_port = 1234; ts->local_ip = 0; - payload[0] = ICMP_ECHO_REQUEST; - payload[1] = 0; + payload[0] = ICMP_ECHO_REPLY; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; - sin.sin_port = 0; sin.sin_addr.s_addr = ee32(0x0A000002U); ck_assert_int_eq(wolfIP_sock_sendto(&s, icmp_sd, payload, sizeof(payload), 0, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(payload)); - ck_assert_uint_ne(ts->src_port, 0); - ck_assert_uint_eq(ts->local_ip, 0x0A000001U); desc = fifo_peek(&ts->sock.udp.txbuf); ck_assert_ptr_nonnull(desc); icmp = (struct wolfIP_icmp_packet *)(ts->txmem + desc->pos + sizeof(*desc)); - ck_assert_uint_eq(icmp_echo_id(icmp), ts->src_port); + ck_assert_uint_eq(icmp_echo_id(icmp), 0U); } END_TEST -START_TEST(test_sock_sendto_invalid_socket_ids) +START_TEST(test_sock_sendto_icmp_local_ip_from_primary) { struct wolfIP s; - uint8_t buf[1] = {0}; + int icmp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + uint8_t payload[ICMP_HEADER_LEN] = {0}; + ip4 primary_ip = 0x0A000001U; wolfIP_init(&s); mock_link_init(&s); + s.if_count = TEST_SECOND_IF + 1; + s.ipconf[TEST_PRIMARY_IF].ip = primary_ip; + s.ipconf[TEST_PRIMARY_IF].mask = 0xFFFFFF00U; + s.ipconf[TEST_SECOND_IF].ip = IPADDR_ANY; + s.ipconf[TEST_SECOND_IF].gw = 0xC0A801FEU; - ck_assert_int_eq(wolfIP_sock_sendto(&s, MARK_TCP_SOCKET | MAX_TCPSOCKETS, buf, sizeof(buf), 0, NULL, 0), -WOLFIP_EINVAL); - ck_assert_int_eq(wolfIP_sock_sendto(&s, MARK_UDP_SOCKET | MAX_UDPSOCKETS, buf, sizeof(buf), 0, NULL, 0), -WOLFIP_EINVAL); - ck_assert_int_eq(wolfIP_sock_sendto(&s, MARK_ICMP_SOCKET | MAX_ICMPSOCKETS, buf, sizeof(buf), 0, NULL, 0), -WOLFIP_EINVAL); + 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)]; + ts->local_ip = 0; + ts->src_port = 1234; + + payload[0] = ICMP_ECHO_REQUEST; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ee32(0x0B000001U); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, icmp_sd, payload, sizeof(payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(payload)); + ck_assert_uint_eq(ts->local_ip, primary_ip); } END_TEST -START_TEST(test_sock_sendto_udp_remote_ip_zero) +START_TEST(test_sock_sendto_icmp_local_ip_pre_set) { struct wolfIP s; - int udp_sd; + int icmp_sd; struct tsocket *ts; - uint8_t buf[4] = {1,2,3,4}; + struct wolfIP_sockaddr_in sin; + uint8_t payload[ICMP_HEADER_LEN] = {0}; wolfIP_init(&s); mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 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)]; - ts->dst_port = 1234; - ts->remote_ip = 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)]; + ts->local_ip = 0x0A000001U; + ts->src_port = 1234; - ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, sizeof(buf), 0, NULL, 0), -1); + payload[0] = ICMP_ECHO_REQUEST; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ee32(0x0A000002U); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, icmp_sd, payload, sizeof(payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(payload)); + ck_assert_uint_eq(ts->local_ip, 0x0A000001U); } END_TEST -START_TEST(test_sock_sendto_udp_primary_ip_fallback) +START_TEST(test_sock_sendto_icmp_conf_null_primary_null) { struct wolfIP s; - int udp_sd; + int icmp_sd; struct tsocket *ts; struct wolfIP_sockaddr_in sin; - uint8_t buf[4] = {1,2,3,4}; - ip4 primary_ip = 0x0A000001U; - ip4 secondary_ip = 0xC0A80101U; + uint8_t payload[ICMP_HEADER_LEN] = {0}; - setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip); - s.ipconf[TEST_SECOND_IF].ip = IPADDR_ANY; - s.ipconf[TEST_SECOND_IF].gw = 0xC0A801FEU; + wolfIP_init(&s); + mock_link_init(&s); + s.if_count = 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)]; - ts->src_port = 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)]; ts->local_ip = 0; + ts->src_port = 1234; + + payload[0] = ICMP_ECHO_REQUEST; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; - sin.sin_port = ee16(7777); - sin.sin_addr.s_addr = ee32(0xC0A80199U); + sin.sin_addr.s_addr = ee32(0x0A000002U); - 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); + ck_assert_int_eq(wolfIP_sock_sendto(&s, icmp_sd, payload, sizeof(payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(payload)); + ck_assert_uint_eq(ts->local_ip, 0U); } END_TEST - START_TEST(test_sock_recvfrom_tcp_close_wait_empty_returns_zero) { struct wolfIP s; @@ -2122,6 +3040,18 @@ START_TEST(test_sock_recvfrom_invalid_socket_ids) } END_TEST +START_TEST(test_sock_recvfrom_non_socket) +{ + struct wolfIP s; + uint8_t buf[4]; + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_sock_recvfrom(&s, 1, buf, sizeof(buf), 0, NULL, NULL), -WOLFIP_EINVAL); +} +END_TEST + START_TEST(test_sock_recvfrom_icmp_success) { struct wolfIP s; @@ -2394,6 +3324,24 @@ START_TEST(test_sock_opts_unknown_level) } END_TEST +START_TEST(test_sock_opts_sol_ip_unknown_optname) +{ + struct wolfIP s; + int udp_sd; + int value = 1; + socklen_t len = sizeof(value); + + wolfIP_init(&s); + mock_link_init(&s); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + + ck_assert_int_eq(wolfIP_sock_setsockopt(&s, udp_sd, WOLFIP_SOL_IP, 0xFFFF, &value, len), 0); + ck_assert_int_eq(wolfIP_sock_getsockopt(&s, udp_sd, WOLFIP_SOL_IP, 0xFFFF, &value, &len), 0); +} +END_TEST + START_TEST(test_sock_accept_wrong_state) { struct wolfIP s; @@ -2496,6 +3444,23 @@ START_TEST(test_sock_get_recv_ttl_disabled) } END_TEST +START_TEST(test_sock_get_recv_ttl_null) +{ + struct wolfIP s; + int udp_sd; + + wolfIP_init(&s); + mock_link_init(&s); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + s.udpsockets[SOCKET_UNMARK(udp_sd)].recv_ttl = 1; + s.udpsockets[SOCKET_UNMARK(udp_sd)].last_pkt_ttl = 99; + + ck_assert_int_eq(wolfIP_sock_get_recv_ttl(&s, udp_sd, NULL), 1); +} +END_TEST + START_TEST(test_sock_connect_tcp_states) { struct wolfIP s; @@ -2605,6 +3570,63 @@ START_TEST(test_sock_getsockname_errors) } END_TEST +START_TEST(test_sock_getsockname_null_addr) +{ + struct wolfIP s; + int tcp_sd; + socklen_t len = sizeof(struct wolfIP_sockaddr_in); + + 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); + ck_assert_int_eq(wolfIP_sock_getsockname(&s, tcp_sd, NULL, &len), -WOLFIP_EINVAL); +} +END_TEST + +START_TEST(test_sock_getsockname_invalid_socket_ids) +{ + struct wolfIP s; + struct wolfIP_sockaddr_in sin; + socklen_t len = sizeof(sin); + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_sock_getsockname(&s, MARK_TCP_SOCKET | MAX_TCPSOCKETS, + (struct wolfIP_sockaddr *)&sin, &len), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_getsockname(&s, MARK_UDP_SOCKET | MAX_UDPSOCKETS, + (struct wolfIP_sockaddr *)&sin, &len), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_getsockname(&s, MARK_ICMP_SOCKET | MAX_ICMPSOCKETS, + (struct wolfIP_sockaddr *)&sin, &len), -WOLFIP_EINVAL); +} +END_TEST + +START_TEST(test_sock_getsockname_icmp_success) +{ + struct wolfIP s; + int icmp_sd; + struct wolfIP_sockaddr_in sin; + socklen_t len = sizeof(sin); + struct tsocket *ts; + + wolfIP_init(&s); + mock_link_init(&s); + + 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)]; + ts->src_port = 7; + ts->local_ip = 0x0A000001U; + + ck_assert_int_eq(wolfIP_sock_getsockname(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, &len), 0); + ck_assert_uint_eq(sin.sin_family, AF_INET); + ck_assert_uint_eq(sin.sin_port, ee16(7)); + ck_assert_uint_eq(sin.sin_addr.s_addr, ee32(0x0A000001U)); +} +END_TEST + START_TEST(test_sock_socket_udp_protocol_zero) { struct wolfIP s; @@ -3073,50 +4095,123 @@ START_TEST(test_sock_sendto_tcp_txbuf_full) 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; - ts->src_port = 1234; + 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; + ts->src_port = 1234; + ts->dst_port = 4321; + ts->local_ip = 0x0A000001U; + ts->remote_ip = 0x0A000002U; + queue_init(&ts->sock.tcp.rxbuf, ts->rxmem, RXBUF_SIZE, 0); + fifo_init(&ts->sock.tcp.txbuf, tiny, sizeof(tiny)); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, tcp_sd, buf, sizeof(buf), 0, NULL, 0), -WOLFIP_EAGAIN); +} +END_TEST + +START_TEST(test_sock_sendto_tcp_partial_send_only) +{ + struct wolfIP s; + int tcp_sd; + struct tsocket *ts; + size_t seg_payload = TCP_MSS - TCP_OPTIONS_LEN; + size_t payload_len = seg_payload; + uint8_t buf[seg_payload * 2]; + size_t required = sizeof(struct pkt_desc) + sizeof(struct wolfIP_tcp_seg) + + TCP_OPTIONS_LEN + payload_len; + /* Provide a small alignment cushion so one segment fits but two do not. */ + uint8_t txbuf[required + 8]; + int ret; + + 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; + ts->src_port = 1234; + ts->dst_port = 4321; + ts->local_ip = 0x0A000001U; + ts->remote_ip = 0x0A000002U; + queue_init(&ts->sock.tcp.rxbuf, ts->rxmem, RXBUF_SIZE, 0); + fifo_init(&ts->sock.tcp.txbuf, txbuf, sizeof(txbuf)); + + memset(buf, 0xAB, sizeof(buf)); + /* Expect a partial send because the tx buffer fits only one segment + * (includes full TCP/IP header size and alignment padding). */ + ret = wolfIP_sock_sendto(&s, tcp_sd, buf, sizeof(buf), 0, NULL, 0); + ck_assert_msg(ret > 0, "expected partial send, got %d", ret); +} +END_TEST + +START_TEST(test_sock_sendto_udp_fifo_push_fails_returns_eagain) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + uint8_t txbuf[4096]; + enum { UDP_PAYLOAD_LEN = + (WI_IP_MTU - IP_HEADER_LEN - UDP_HEADER_LEN) > 1000 + ? 1000 + : (WI_IP_MTU - IP_HEADER_LEN - UDP_HEADER_LEN) + }; + uint8_t payload[UDP_PAYLOAD_LEN]; + int ret; + + wolfIP_init(&s); + mock_link_init(&s); + + 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)]; ts->dst_port = 4321; - ts->local_ip = 0x0A000001U; ts->remote_ip = 0x0A000002U; - queue_init(&ts->sock.tcp.rxbuf, ts->rxmem, RXBUF_SIZE, 0); - fifo_init(&ts->sock.tcp.txbuf, tiny, sizeof(tiny)); + ts->local_ip = 0x0A000001U; - ck_assert_int_eq(wolfIP_sock_sendto(&s, tcp_sd, buf, sizeof(buf), 0, NULL, 0), -WOLFIP_EAGAIN); + fifo_init(&ts->sock.udp.txbuf, txbuf, sizeof(txbuf)); + ts->sock.udp.txbuf.head = 100; + ts->sock.udp.txbuf.tail = 100 + UDP_PAYLOAD_LEN; + ts->sock.udp.txbuf.h_wrap = 3000; + + memset(payload, 0xEE, sizeof(payload)); + ret = wolfIP_sock_sendto(&s, udp_sd, payload, sizeof(payload), 0, NULL, 0); + ck_assert_int_eq(ret, -WOLFIP_EAGAIN); } END_TEST -START_TEST(test_sock_sendto_tcp_partial_send) +START_TEST(test_sock_sendto_icmp_fifo_push_fails_returns_eagain) { struct wolfIP s; - int tcp_sd; + int icmp_sd; struct tsocket *ts; - size_t seg_payload = TCP_MSS - TCP_OPTIONS_LEN; - size_t payload_len = seg_payload; - uint8_t buf[seg_payload * 2]; - size_t required = sizeof(struct pkt_desc) + IP_HEADER_LEN + TCP_HEADER_LEN + TCP_OPTIONS_LEN + payload_len; - uint8_t txbuf[required + 4]; + uint8_t txbuf[4096]; + enum { ICMP_PAYLOAD_LEN = + (WI_IP_MTU - IP_HEADER_LEN) > 1000 + ? 1000 + : (WI_IP_MTU - IP_HEADER_LEN) + }; + uint8_t payload[ICMP_PAYLOAD_LEN]; int ret; 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; - ts->src_port = 1234; - ts->dst_port = 4321; - ts->local_ip = 0x0A000001U; + 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)]; ts->remote_ip = 0x0A000002U; - queue_init(&ts->sock.tcp.rxbuf, ts->rxmem, RXBUF_SIZE, 0); - fifo_init(&ts->sock.tcp.txbuf, txbuf, sizeof(txbuf)); - memset(buf, 0xAB, sizeof(buf)); - ret = wolfIP_sock_sendto(&s, tcp_sd, buf, sizeof(buf), 0, NULL, 0); - ck_assert_int_gt(ret, 0); + fifo_init(&ts->sock.udp.txbuf, txbuf, sizeof(txbuf)); + ts->sock.udp.txbuf.head = 100; + ts->sock.udp.txbuf.tail = 100 + ICMP_PAYLOAD_LEN; + ts->sock.udp.txbuf.h_wrap = 3000; + + memset(payload, 0xEE, sizeof(payload)); + payload[0] = ICMP_ECHO_REQUEST; + ret = wolfIP_sock_sendto(&s, icmp_sd, payload, sizeof(payload), 0, NULL, 0); + ck_assert_int_eq(ret, -WOLFIP_EAGAIN); } END_TEST @@ -3476,10 +4571,10 @@ START_TEST(test_fifo_push_and_pop) { fifo_init(&f, mem, memsz); ck_assert_int_eq(fifo_space(&f), memsz); - // Test push + /* Push one payload and verify descriptors reflect its size. */ ck_assert_int_eq(fifo_push(&f, data, sizeof(data)), 0); - // Test peek + /* Peek should return the current head descriptor without consuming data. */ desc = fifo_peek(&f); ck_assert_ptr_nonnull(desc); ck_assert_int_eq(desc->len, sizeof(data)); @@ -3490,13 +4585,13 @@ START_TEST(test_fifo_push_and_pop) { ck_assert_int_eq(fifo_len(&f), desc->len + sizeof(struct pkt_desc)); - // Test pop + /* Pop should consume the packet and restore full space. */ desc = fifo_pop(&f); ck_assert_int_eq(fifo_space(&f), memsz); ck_assert_ptr_nonnull(desc); ck_assert_int_eq(desc->len, sizeof(data)); ck_assert_mem_eq((const uint8_t *)f.data + desc->pos + sizeof(struct pkt_desc), data, sizeof(data)); - ck_assert_int_eq(fifo_len(&f), 0); + ck_assert_ptr_eq(fifo_peek(&f), NULL); } END_TEST @@ -3646,6 +4741,9 @@ START_TEST(test_poll_tcp_ack_only_skips_send) ts->sock.tcp.ack = 10; ts->sock.tcp.last_ack = 10; ts->sock.tcp.rto = 100; + /* Ensure send window allows processing of the queued ACK-only segment. */ + ts->sock.tcp.cwnd = TCP_MSS; + ts->sock.tcp.peer_rwnd = TCP_MSS; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); ck_assert_int_eq(enqueue_tcp_tx(ts, 0, 0x10), 0); @@ -3688,6 +4786,9 @@ START_TEST(test_poll_tcp_send_on_arp_hit) ts->sock.tcp.ack = 20; ts->sock.tcp.last_ack = 0; ts->sock.tcp.rto = 100; + /* Ensure send window allows emitting the queued data segment. */ + ts->sock.tcp.cwnd = TCP_MSS * 4; + ts->sock.tcp.peer_rwnd = TCP_MSS * 4; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); ck_assert_int_eq(enqueue_tcp_tx(ts, 1, 0x18), 0); @@ -4012,6 +5113,77 @@ START_TEST(test_sock_close_udp_icmp) ck_assert_int_eq(ts->proto, 0); } END_TEST + +START_TEST(test_sock_close_invalid_fds) +{ + struct wolfIP s; + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_sock_close(&s, -1), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_close(&s, MARK_TCP_SOCKET | MAX_TCPSOCKETS), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_close(&s, MARK_UDP_SOCKET | MAX_UDPSOCKETS), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_close(&s, MARK_ICMP_SOCKET | MAX_ICMPSOCKETS), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_close(&s, 1), -1); +} +END_TEST + +START_TEST(test_sock_close_tcp_fin_wait_1) +{ + struct wolfIP s; + struct tsocket *ts; + int sd; + + wolfIP_init(&s); + mock_link_init(&s); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(sd)]; + ts->sock.tcp.state = TCP_FIN_WAIT_1; + + ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EAGAIN); + ck_assert_int_eq(ts->sock.tcp.state, TCP_CLOSING); +} +END_TEST + +START_TEST(test_sock_close_tcp_other_state_closes) +{ + struct wolfIP s; + struct tsocket *ts; + int sd; + + wolfIP_init(&s); + mock_link_init(&s); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(sd)]; + ts->sock.tcp.state = TCP_SYN_SENT; + + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); + ck_assert_int_eq(ts->proto, 0); +} +END_TEST + +START_TEST(test_sock_close_tcp_closed_returns_minus_one) +{ + struct wolfIP s; + struct tsocket *ts; + int sd; + + wolfIP_init(&s); + mock_link_init(&s); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(sd)]; + ts->sock.tcp.state = TCP_CLOSED; + + ck_assert_int_eq(wolfIP_sock_close(&s, sd), -1); +} +END_TEST START_TEST(test_fifo_push_and_pop_multiple) { struct fifo f; uint8_t data[] = {1, 2, 3, 4, 5}; @@ -4884,6 +6056,19 @@ START_TEST(test_route_for_ip_variants) } END_TEST +START_TEST(test_route_for_ip_dest_matches_iface_ip) +{ + struct wolfIP s; + ip4 primary_ip = 0x0A000001U; + ip4 secondary_ip = 0xC0A80101U; + + setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip); + + ck_assert_uint_eq(wolfIP_route_for_ip(&s, primary_ip), TEST_PRIMARY_IF); + ck_assert_uint_eq(wolfIP_route_for_ip(&s, secondary_ip), TEST_SECOND_IF); +} +END_TEST + START_TEST(test_route_for_ip_gw_and_nonloop_fallback) { struct wolfIP s; @@ -4956,6 +6141,20 @@ START_TEST(test_forward_interface_variants) } END_TEST +START_TEST(test_forward_interface_skips_ipaddr_any) +{ + struct wolfIP s; + ip4 primary_ip = 0x0A000001U; + ip4 secondary_ip = 0xC0A80101U; + + setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip); + s.ipconf[TEST_SECOND_IF].ip = IPADDR_ANY; + s.ipconf[TEST_SECOND_IF].mask = 0xFFFFFF00U; + + ck_assert_int_eq((int)wolfIP_forward_interface(&s, TEST_PRIMARY_IF, 0xC0A80199U), -1); +} +END_TEST + START_TEST(test_forward_interface_dest_is_local_ip) { struct wolfIP s; @@ -6642,6 +7841,8 @@ START_TEST(test_tcp_ack_acks_data_and_sets_writable) ts->sock.tcp.state = TCP_ESTABLISHED; ts->sock.tcp.cwnd = TCP_MSS; ts->sock.tcp.ssthresh = TCP_MSS * 4; + /* Ensure cwnd growth is gated by bytes_in_flight and not rwnd-capped. */ + ts->sock.tcp.peer_rwnd = TCP_MSS * 8; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); memset(seg, 0, sizeof(seg_buf)); @@ -6655,6 +7856,9 @@ START_TEST(test_tcp_ack_acks_data_and_sets_writable) ck_assert_ptr_nonnull(desc); desc->flags |= PKT_FLAG_SENT; desc->time_sent = 10; + /* Simulate cwnd-limited flight and initialize snd_una. */ + ts->sock.tcp.bytes_in_flight = TCP_MSS; + ts->sock.tcp.snd_una = seq; memset(&ackseg, 0, sizeof(ackseg)); ackseg.ack = ee32(seq + sizeof(payload)); @@ -6662,7 +7866,8 @@ START_TEST(test_tcp_ack_acks_data_and_sets_writable) ackseg.flags = 0x10; tcp_ack(ts, &ackseg); - ck_assert_int_eq(fifo_len(&ts->sock.tcp.txbuf), 0); + /* FIFO should be empty after acked data is removed. */ + ck_assert_ptr_eq(fifo_peek(&ts->sock.tcp.txbuf), NULL); ck_assert_uint_eq(ts->events & CB_EVENT_WRITABLE, CB_EVENT_WRITABLE); ck_assert_uint_gt(ts->sock.tcp.cwnd, TCP_MSS); } @@ -6686,6 +7891,8 @@ START_TEST(test_tcp_ack_duplicate_resend_clears_sent) ts->sock.tcp.state = TCP_ESTABLISHED; ts->sock.tcp.cwnd = TCP_MSS * 4; ts->sock.tcp.ssthresh = TCP_MSS * 4; + /* Allow duplicate-ACK path without rwnd cap. */ + ts->sock.tcp.peer_rwnd = TCP_MSS * 8; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); memset(seg, 0, sizeof(seg_buf)); @@ -6910,12 +8117,18 @@ START_TEST(test_tcp_ack_duplicate_zero_len_segment_large_ack) desc = fifo_next(&ts->sock.tcp.txbuf, desc); ck_assert_ptr_nonnull(desc); desc->flags |= PKT_FLAG_SENT; + /* Force duplicate ACK handling with outstanding bytes. */ + ts->sock.tcp.bytes_in_flight = TCP_MSS * 2; + /* Treat this ACK as a duplicate (snd_una == ack). */ + ts->sock.tcp.snd_una = 0xF0000000U; memset(&ackseg, 0, sizeof(ackseg)); ackseg.ack = ee32(0xF0000000U); ackseg.hlen = TCP_HEADER_LEN << 2; ackseg.flags = 0x10; + /* Prime dup-ack counter so a single ACK triggers fast retransmit. */ + ts->sock.tcp.dup_acks = 2; tcp_ack(ts, &ackseg); ck_assert_uint_le(fifo_len(&ts->sock.tcp.txbuf), TXBUF_SIZE); ck_assert_uint_eq(ts->sock.tcp.ssthresh, TCP_MSS * 2); @@ -6940,6 +8153,8 @@ START_TEST(test_tcp_ack_duplicate_seq_match_large_seg_len) ts->S = &s; ts->sock.tcp.state = TCP_ESTABLISHED; ts->sock.tcp.cwnd = TCP_MSS * 4; + /* Allow duplicate-ACK path without rwnd cap. */ + ts->sock.tcp.peer_rwnd = TCP_MSS * 8; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); memset(&segbuf, 0, sizeof(segbuf)); @@ -6951,12 +8166,18 @@ START_TEST(test_tcp_ack_duplicate_seq_match_large_seg_len) desc = fifo_peek(&ts->sock.tcp.txbuf); ck_assert_ptr_nonnull(desc); desc->flags |= PKT_FLAG_SENT; + /* Force duplicate ACK handling with outstanding bytes. */ + ts->sock.tcp.bytes_in_flight = TCP_MSS * 2; + ts->sock.tcp.snd_una = seq; memset(&ackseg, 0, sizeof(ackseg)); ackseg.ack = ee32(seq); ackseg.hlen = TCP_HEADER_LEN << 2; ackseg.flags = 0x10; + /* Trigger fast retransmit by delivering three duplicate ACKs. */ + tcp_ack(ts, &ackseg); + tcp_ack(ts, &ackseg); tcp_ack(ts, &ackseg); ck_assert_int_ne(desc->flags & PKT_FLAG_SENT, PKT_FLAG_SENT); } @@ -7001,11 +8222,17 @@ START_TEST(test_tcp_ack_duplicate_clears_sent_flag) seg2->seq = ee32(seq2); ck_assert_int_eq(fifo_push(&ts->sock.tcp.txbuf, &segbuf2, sizeof(segbuf2)), 0); + /* Force duplicate ACK handling with outstanding bytes. */ + ts->sock.tcp.bytes_in_flight = TCP_MSS * 2; + ts->sock.tcp.snd_una = seq1; + memset(&ackseg, 0, sizeof(ackseg)); ackseg.ack = ee32(seq1); ackseg.hlen = TCP_HEADER_LEN << 2; ackseg.flags = 0x10; + /* Prime dup-ack counter so a single ACK triggers fast retransmit. */ + ts->sock.tcp.dup_acks = 2; tcp_ack(ts, &ackseg); desc = fifo_peek(&ts->sock.tcp.txbuf); ck_assert_ptr_nonnull(desc); @@ -7269,6 +8496,8 @@ START_TEST(test_tcp_ack_cwnd_count_wrap) ts->sock.tcp.cwnd = TCP_MSS * 4; ts->sock.tcp.ssthresh = TCP_MSS; ts->sock.tcp.cwnd_count = ts->sock.tcp.cwnd - 1; + /* Ensure cwnd growth path is taken and not rwnd-capped. */ + ts->sock.tcp.peer_rwnd = TCP_MSS * 8; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); memset(&segbuf, 0, sizeof(segbuf)); @@ -7280,6 +8509,10 @@ START_TEST(test_tcp_ack_cwnd_count_wrap) desc = fifo_peek(&ts->sock.tcp.txbuf); ck_assert_ptr_nonnull(desc); desc->flags |= PKT_FLAG_SENT; + /* Simulate cwnd-limited flight and initialize snd_una. */ + ts->sock.tcp.bytes_in_flight = ts->sock.tcp.cwnd; + /* Advance ACK by 1 byte to exercise cwnd_count wrap. */ + ts->sock.tcp.snd_una = 100; memset(&ackseg, 0, sizeof(ackseg)); ackseg.ack = ee32(101); @@ -7287,7 +8520,8 @@ START_TEST(test_tcp_ack_cwnd_count_wrap) ackseg.flags = 0x10; tcp_ack(ts, &ackseg); - ck_assert_uint_eq(ts->sock.tcp.cwnd_count, 0); + /* Expect cwnd_count to wrap to (cwnd_count + MSS - cwnd). */ + ck_assert_uint_eq(ts->sock.tcp.cwnd_count, (TCP_MSS - 1)); ck_assert_uint_eq(ts->sock.tcp.cwnd, (TCP_MSS * 5)); } END_TEST @@ -7310,6 +8544,8 @@ START_TEST(test_tcp_ack_updates_rtt_and_cwnd) ts->sock.tcp.state = TCP_ESTABLISHED; ts->sock.tcp.cwnd = TCP_MSS; ts->sock.tcp.ssthresh = TCP_MSS * 4; + /* Ensure cwnd growth path is taken and not rwnd-capped. */ + ts->sock.tcp.peer_rwnd = TCP_MSS * 8; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); s.last_tick = 1000; @@ -7327,6 +8563,9 @@ START_TEST(test_tcp_ack_updates_rtt_and_cwnd) desc = fifo_peek(&ts->sock.tcp.txbuf); ck_assert_ptr_nonnull(desc); desc->flags |= PKT_FLAG_SENT; + /* Simulate cwnd-limited flight and initialize snd_una. */ + ts->sock.tcp.bytes_in_flight = ts->sock.tcp.cwnd; + ts->sock.tcp.snd_una = 100; memset(&ackseg, 0, sizeof(ackseg)); ackseg.ack = ee32(101); @@ -7394,6 +8633,8 @@ START_TEST(test_tcp_ack_no_progress_when_ack_far_ahead) ts->proto = WI_IPPROTO_TCP; ts->S = &s; ts->sock.tcp.state = TCP_ESTABLISHED; + /* Allow duplicate-ACK path without rwnd cap. */ + ts->sock.tcp.peer_rwnd = TCP_MSS * 8; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); memset(&segbuf, 0, sizeof(segbuf)); @@ -7405,6 +8646,9 @@ START_TEST(test_tcp_ack_no_progress_when_ack_far_ahead) desc = fifo_peek(&ts->sock.tcp.txbuf); ck_assert_ptr_nonnull(desc); desc->flags |= PKT_FLAG_SENT; + /* Force duplicate ACK handling with outstanding bytes. */ + ts->sock.tcp.bytes_in_flight = TCP_MSS * 2; + ts->sock.tcp.snd_una = 500; memset(&ackseg, 0, sizeof(ackseg)); ackseg.ack = ee32(seq + 0x100000U); @@ -7473,6 +8717,8 @@ START_TEST(test_tcp_ack_duplicate_clears_sent_large_seg_len) ts->proto = WI_IPPROTO_TCP; ts->S = &s; ts->sock.tcp.state = TCP_ESTABLISHED; + /* Allow duplicate-ACK path without rwnd cap. */ + ts->sock.tcp.peer_rwnd = TCP_MSS * 8; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); memset(&segbuf, 0, sizeof(segbuf)); @@ -7484,12 +8730,18 @@ START_TEST(test_tcp_ack_duplicate_clears_sent_large_seg_len) desc = fifo_peek(&ts->sock.tcp.txbuf); ck_assert_ptr_nonnull(desc); desc->flags |= PKT_FLAG_SENT; + /* Force duplicate ACK handling with outstanding bytes. */ + ts->sock.tcp.bytes_in_flight = TCP_MSS * 2; + ts->sock.tcp.snd_una = 500; memset(&ackseg, 0, sizeof(ackseg)); ackseg.ack = ee32(500); ackseg.hlen = TCP_HEADER_LEN << 2; ackseg.flags = 0x10; + /* Trigger fast retransmit by delivering three duplicate ACKs. */ + tcp_ack(ts, &ackseg); + tcp_ack(ts, &ackseg); tcp_ack(ts, &ackseg); desc = fifo_peek(&ts->sock.tcp.txbuf); ck_assert_ptr_nonnull(desc); @@ -7550,6 +8802,8 @@ START_TEST(test_tcp_ack_duplicate_ssthresh_min) ts->S = &s; ts->sock.tcp.state = TCP_ESTABLISHED; ts->sock.tcp.cwnd = TCP_MSS; + /* Allow duplicate-ACK path without rwnd cap. */ + ts->sock.tcp.peer_rwnd = TCP_MSS * 8; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); memset(&segbuf, 0, sizeof(segbuf)); @@ -7561,6 +8815,9 @@ START_TEST(test_tcp_ack_duplicate_ssthresh_min) desc = fifo_peek(&ts->sock.tcp.txbuf); ck_assert_ptr_nonnull(desc); desc->flags |= PKT_FLAG_SENT; + /* Force duplicate ACK handling with outstanding bytes. */ + ts->sock.tcp.bytes_in_flight = TCP_MSS; + ts->sock.tcp.snd_una = 50; memset(&ackseg, 0, sizeof(ackseg)); ackseg.ack = ee32(50); @@ -8070,6 +9327,7 @@ START_TEST(test_fifo_space_wrap_sets_hwrap) size_t total = sizeof(struct pkt_desc) + LINK_MTU + 64; uint8_t *buf = malloc(total); uint32_t space; + uint32_t expected; ck_assert_ptr_nonnull(buf); fifo_init(&f, buf, (uint32_t)total); @@ -8078,9 +9336,9 @@ START_TEST(test_fifo_space_wrap_sets_hwrap) f.h_wrap = 0; space = fifo_space(&f); - ck_assert_uint_eq(f.head, 0); - ck_assert_uint_ne(f.h_wrap, 0); - ck_assert_uint_gt(space, 0); + expected = (uint32_t)total - (f.head - f.tail); + ck_assert_uint_eq(f.h_wrap, 0); + ck_assert_uint_eq(space, expected); free(buf); } @@ -8100,7 +9358,7 @@ START_TEST(test_fifo_space_wrap_returns_zero) f.h_wrap = 0; space = fifo_space(&f); - ck_assert_uint_eq(space, 0); + ck_assert_uint_eq(space, (uint32_t)total - (f.head - f.tail)); ck_assert_uint_eq(f.h_wrap, 0); free(buf); @@ -8118,13 +9376,16 @@ START_TEST(test_fifo_peek_wrap_to_start) ck_assert_ptr_nonnull(buf); fifo_init(&f, buf, (uint32_t)total); f.head = 8; - f.tail = 12; + f.tail = 4; + f.h_wrap = 8; desc = (struct pkt_desc *)buf; memset(desc, 0, sizeof(*desc)); desc->len = 1; desc->pos = 0; + /* When tail crosses h_wrap, peek should wrap to the start and return + * the first descriptor at offset 0. */ peeked = fifo_peek(&f); ck_assert_ptr_eq(peeked, desc); @@ -8161,6 +9422,7 @@ START_TEST(test_fifo_peek_aligns_tail) desc->len = 1; desc->pos = 4; + /* Peek aligns tail to 4-byte boundary before reading the descriptor. */ peeked = fifo_peek(&f); ck_assert_ptr_eq(peeked, desc); ck_assert_uint_eq(f.tail, 4); @@ -8174,14 +9436,18 @@ START_TEST(test_fifo_next_wraps_to_start) uint8_t *buf = malloc(total); struct pkt_desc *desc; struct pkt_desc *next; + uint32_t h_wrap = 64; + uint32_t len; ck_assert_ptr_nonnull(buf); fifo_init(&f, buf, (uint32_t)total); - desc = (struct pkt_desc *)(buf + 4); + desc = (struct pkt_desc *)buf; memset(desc, 0, sizeof(*desc)); - desc->pos = 4; - desc->len = 1; - f.head = 32; + desc->pos = 0; + len = h_wrap - desc->pos; + desc->len = len - sizeof(struct pkt_desc); + f.head = 128; + f.h_wrap = h_wrap; next = fifo_next(&f, desc); ck_assert_ptr_eq(next, (struct pkt_desc *)buf); @@ -8222,7 +9488,58 @@ START_TEST(test_fifo_space_with_hwrap) f.h_wrap = 300; space = fifo_space(&f); - ck_assert_uint_eq(space, f.size - (f.tail - f.head)); + ck_assert_uint_eq(space, f.tail - f.head); +} +END_TEST + +START_TEST(test_fifo_space_hwrap_head_hits_wrap) +{ + struct fifo f; + uint8_t buf[512]; + uint32_t space; + + fifo_init(&f, buf, sizeof(buf)); + f.head = 300; + f.tail = 200; + f.h_wrap = 300; + + space = fifo_space(&f); + ck_assert_uint_eq(space, 0); +} +END_TEST + +START_TEST(test_fifo_space_hwrap_head_ge_tail_returns_zero) +{ + struct fifo f; + uint8_t buf[512]; + uint32_t space; + + fifo_init(&f, buf, sizeof(buf)); + f.head = 220; + f.tail = 200; + f.h_wrap = 300; + + space = fifo_space(&f); + ck_assert_uint_eq(space, 0); +} +END_TEST + +START_TEST(test_fifo_push_no_contiguous_space_even_with_space) +{ + struct fifo f; + uint8_t buf[4096]; + uint8_t payload[1592]; + int ret; + + fifo_init(&f, buf, sizeof(buf)); + f.head = 2506; + f.tail = 1500; + f.h_wrap = 0; + + memset(payload, 0xCD, sizeof(payload)); + ret = fifo_push(&f, payload, sizeof(payload)); + ck_assert_int_eq(ret, -1); + ck_assert_uint_eq(f.h_wrap, 0); } END_TEST @@ -8280,13 +9597,15 @@ START_TEST(test_fifo_pop_wrap_to_start) ck_assert_ptr_nonnull(buf); fifo_init(&f, buf, (uint32_t)total); f.head = 8; - f.tail = 12; + f.tail = 4; + f.h_wrap = 8; desc = (struct pkt_desc *)buf; memset(desc, 0, sizeof(*desc)); desc->len = 1; desc->pos = 0; + /* Pop should wrap to start and return the descriptor at offset 0. */ popped = fifo_pop(&f); ck_assert_ptr_eq(popped, desc); @@ -8682,6 +10001,58 @@ START_TEST(test_timer_pop_reorders_heap) } END_TEST +START_TEST(test_timer_pop_right_child_swap) +{ + struct timers_binheap h; + struct wolfIP_timer popped; + + memset(&h, 0, sizeof(h)); + h.size = 4; + h.timers[0].expires = 10; + h.timers[1].expires = 40; + h.timers[2].expires = 20; + h.timers[3].expires = 30; + + popped = timers_binheap_pop(&h); + ck_assert_uint_eq(popped.expires, 10); + ck_assert_uint_eq(h.size, 3); + ck_assert_uint_eq(h.timers[0].expires, 20); +} +END_TEST + +START_TEST(test_timer_pop_break_when_root_small) +{ + struct timers_binheap h; + struct wolfIP_timer popped; + + memset(&h, 0, sizeof(h)); + h.size = 4; + h.timers[0].expires = 10; + h.timers[1].expires = 20; + h.timers[2].expires = 30; + h.timers[3].expires = 15; + + popped = timers_binheap_pop(&h); + ck_assert_uint_eq(popped.expires, 10); + ck_assert_uint_eq(h.size, 3); + ck_assert_uint_eq(h.timers[0].expires, 15); +} +END_TEST + +START_TEST(test_is_timer_expired_skips_zero_head) +{ + struct timers_binheap h; + + memset(&h, 0, sizeof(h)); + h.size = 2; + h.timers[0].expires = 0; + h.timers[1].expires = 50; + + ck_assert_int_eq(is_timer_expired(&h, 10), 0); + ck_assert_uint_eq(h.size, 0); +} +END_TEST + /* Arp suite */ START_TEST(test_arp_request_basic) @@ -9962,8 +11333,11 @@ Suite *wolf_suite(void) tcase_add_test(tc_core, test_fifo_space_head_lt_tail); suite_add_tcase(s, tc_core); tcase_add_test(tc_core, test_fifo_space_with_hwrap); + tcase_add_test(tc_core, test_fifo_space_hwrap_head_hits_wrap); + tcase_add_test(tc_core, test_fifo_space_hwrap_head_ge_tail_returns_zero); suite_add_tcase(s, tc_core); tcase_add_test(tc_core, test_fifo_next_hits_hwrap); + tcase_add_test(tc_core, test_fifo_push_no_contiguous_space_even_with_space); suite_add_tcase(s, tc_core); tcase_add_test(tc_core, test_fifo_len_tail_gt_head_no_hwrap); suite_add_tcase(s, tc_core); @@ -10031,6 +11405,12 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_timer_pop_reorders_heap); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_timer_pop_right_child_swap); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_timer_pop_break_when_root_small); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_is_timer_expired_skips_zero_head); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_wolfip_getdev_ex_api); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_wolfip_ll_at_and_ipconf_at_invalid); @@ -10063,6 +11443,8 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_filter_socket_event_unknown_proto); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_filter_socket_event_proto_variants); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_filter_setters_and_get_mask); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_socket_errors); @@ -10085,10 +11467,26 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_bind_wrong_family); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_bind_invalid_fd); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_bind_tcp_state_not_closed); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_bind_tcp_filter_blocks); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_bind_udp_src_port_nonzero); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_bind_udp_filter_blocks); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_bind_icmp_success); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_wrong_family); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_accept_error_paths); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_accept_non_tcp_socket_sets_addrlen); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_accept_null_addr_with_addrlen); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_recvfrom_short_addrlen); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_recvfrom_udp_short_addrlen); @@ -10099,6 +11497,8 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_bad_addrlen); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_tcp_bad_addrlen); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_invalid_args); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_invalid_tcp_fd); @@ -10115,18 +11515,40 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_udp_bound_local_ip_no_match); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_udp_bound_local_ip_match); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_icmp_sets_local_ip_from_conf); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_icmp_wrong_family); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_icmp_local_ip_pre_set); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_icmp_conf_null); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_icmp_falls_back_to_primary); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_icmp_primary_ip_any); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_icmp_primary_ip_fallback); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_tcp_established_returns_zero); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_tcp_bound_local_ip_match); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_tcp_bound_local_ip_no_match); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_tcp_filter_blocks); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_tcp_local_ip_from_conf); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_tcp_local_ip_from_primary); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_tcp_primary_ip_fallback); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_tcp_conf_null_primary_null); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_tcp_state_not_closed); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_accept_negative_fd); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_accept_invalid_tcp_fd); @@ -10159,10 +11581,14 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_recvfrom_invalid_socket_ids); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_recvfrom_non_socket); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_recvfrom_icmp_success); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_opts_unknown_level); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_opts_sol_ip_unknown_optname); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_setsockopt_recvttl); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_setsockopt_invalid_socket); @@ -10181,6 +11607,8 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_get_recv_ttl_disabled); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_get_recv_ttl_null); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_tcp_states); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_listen_errors); @@ -10189,6 +11617,12 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_getsockname_errors); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_getsockname_null_addr); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_getsockname_invalid_socket_ids); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_getsockname_icmp_success); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_register_callback_variants); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_udp_bound_ip_not_local); @@ -10227,13 +11661,31 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_sendto_invalid_socket_ids); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_sendto_non_socket_returns_minus_one); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_sendto_udp_remote_ip_zero); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_sendto_udp_primary_ip_fallback); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_sendto_udp_zero_port_in_addr); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_sendto_udp_src_port_low_adjusts); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_sendto_udp_local_ip_conf_null); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_sendto_udp_local_ip_from_primary); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_sendto_tcp_txbuf_full); suite_add_tcase(s, tc_utils); - tcase_add_test(tc_utils, test_sock_sendto_tcp_partial_send); + tcase_add_test(tc_utils, test_sock_sendto_tcp_partial_send_only); + tcase_add_test(tc_utils, test_sock_sendto_tcp_multiple_segments_flags); + tcase_add_test(tc_utils, test_sock_sendto_udp_fifo_push_fails_returns_eagain); + tcase_add_test(tc_utils, test_sock_sendto_icmp_fifo_push_fails_returns_eagain); + tcase_add_test(tc_utils, test_sock_sendto_icmp_src_port_zero_random_zero_sets_one); + tcase_add_test(tc_utils, test_sock_sendto_icmp_non_echo_no_set_id); + tcase_add_test(tc_utils, test_sock_sendto_icmp_local_ip_from_primary); + tcase_add_test(tc_utils, test_sock_sendto_icmp_local_ip_pre_set); + tcase_add_test(tc_utils, test_sock_sendto_icmp_conf_null_primary_null); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_recvfrom_icmp_paths); suite_add_tcase(s, tc_utils); @@ -10327,6 +11779,14 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_close_udp_icmp); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_close_invalid_fds); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_close_tcp_fin_wait_1); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_close_tcp_other_state_closes); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_close_tcp_closed_returns_minus_one); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_tcp_syn_sent_to_established); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_tcp_input_syn_sent_unexpected_flags); @@ -10562,6 +12022,8 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_proto); tcase_add_test(tc_proto, test_route_for_ip_variants); suite_add_tcase(s, tc_proto); + tcase_add_test(tc_proto, test_route_for_ip_dest_matches_iface_ip); + suite_add_tcase(s, tc_proto); tcase_add_test(tc_proto, test_route_for_ip_no_primary_index); suite_add_tcase(s, tc_proto); tcase_add_test(tc_proto, test_route_for_ip_null_stack); @@ -10572,6 +12034,8 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_proto); tcase_add_test(tc_proto, test_forward_interface_variants); suite_add_tcase(s, tc_proto); + tcase_add_test(tc_proto, test_forward_interface_skips_ipaddr_any); + suite_add_tcase(s, tc_proto); tcase_add_test(tc_proto, test_forward_interface_dest_is_local_ip); suite_add_tcase(s, tc_proto); tcase_add_test(tc_proto, test_forward_interface_short_circuit_cases); diff --git a/src/wolfip.c b/src/wolfip.c index 647a8e7..721a750 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -79,9 +79,12 @@ struct wolfIP_icmp_packet; #define TCP_OPTION_MSS 0x02 #define TCP_OPTION_MSS_LEN 4 +#define TCP_OPTION_WS 0x03 +#define TCP_OPTION_WS_LEN 3 #define TCP_OPTION_TS 0x08 #define TCP_OPTION_TS_LEN 10 #define TCP_OPTIONS_LEN 12 +#define TCP_SYN_OPTIONS_LEN 20 #define TCP_OPTION_NOP 0x01 #define TCP_OPTION_EOO 0x00 @@ -104,6 +107,8 @@ struct wolfIP_icmp_packet; #define WI_IP_MTU 1500 #define TCP_MSS (WI_IP_MTU - (IP_HEADER_LEN + TCP_HEADER_LEN)) +/* Arbitrary upper limit to avoid monopolizing the CPU during poll loops. */ +#define WOLFIP_POLL_BUDGET 128 /* Macros */ #define IS_IP_BCAST(ip) (ip == 0xFFFFFFFF) @@ -126,6 +131,34 @@ struct fifo { uint8_t *data; }; +static inline uint32_t fifo_align_head_pos(uint32_t head, uint32_t size) +{ + if (head % 4) + head += 4 - (head % 4); + if (head >= size) + head = 0; + return head; +} + +static inline void fifo_align_tail(struct fifo *f) +{ + if (f->tail % 4) + f->tail += 4 - (f->tail % 4); + if (f->h_wrap && f->tail >= f->h_wrap) { + f->tail = 0; + f->h_wrap = 0; + } + if (f->h_wrap && (f->tail < f->h_wrap) && + ((f->tail + sizeof(struct pkt_desc)) > f->h_wrap)) { + f->tail = 0; + f->h_wrap = 0; + } + if (f->tail >= f->size) + f->tail %= f->size; + if ((f->tail + sizeof(struct pkt_desc)) > f->size) + f->tail = 0; +} + /* TCP TX is a circular buffer and contains an array of full packets */ /* TCP RX only contains application data */ @@ -148,49 +181,30 @@ static void fifo_init(struct fifo *f, uint8_t *data, uint32_t size) /* Return the number of bytes available */ static uint32_t fifo_space(struct fifo *f) { - int ret = 0; - if (f->head == f->tail) { - f->head = 0; - f->tail = 0; + if (f->head == f->tail) return f->size; + if (f->h_wrap) { + if (f->head < f->tail) + return f->tail - f->head; + return 0; } - if (f->tail == f->h_wrap) { - f->tail = 0; - f->h_wrap = 0; - } - if (f->h_wrap == 0) { - if (f->head >= f->tail) { - ret = f->size - (f->head - f->tail); - } else { - ret = f->tail - f->head; - } - /* Take into account the wraparound to always keep the segment contiguous */ - if ((f->size - f->head) < (sizeof(struct pkt_desc) + LINK_MTU)) { - if (f->tail > (sizeof(struct pkt_desc) + LINK_MTU)) { - f->h_wrap = f->head; - f->head = 0; - return f->tail - f->head; - } else return 0; - } - } else { - ret = f->size - (f->tail - f->head); - } - return ret; + if (f->head >= f->tail) + return f->size - (f->head - f->tail); + return f->tail - f->head; } /* Check the descriptor of the next packet */ static struct pkt_desc *fifo_peek(struct fifo *f) { - if (f->tail == f->h_wrap) { - f->tail = 0; - f->h_wrap = 0; - } if (f->tail == f->head) return NULL; - while (f->tail % 4) - f->tail++; - if ((f->head < f->tail) && ((f->tail + sizeof(struct pkt_desc) + LINK_MTU > f->size))) - f->tail = 0; + /* Advance tail only to skip alignment/wrap padding, not real packet data. + * This is safe because padding bytes are not part of any pkt_desc payload. + * We do this right before reading the next descriptor so callers always + * see a valid, aligned pkt_desc without dequeuing a packet. */ + fifo_align_tail(f); + if (f->tail == f->head) + return NULL; return (struct pkt_desc *)((uint8_t *)f->data + f->tail); } @@ -205,22 +219,17 @@ static struct pkt_desc *fifo_next(struct fifo *f, struct pkt_desc *desc) return NULL; while ((desc->pos + len) % 4) len++; - if ((desc->pos + len + sizeof(struct pkt_desc) + LINK_MTU ) >= f->size) + if ((desc->pos + len) >= f->size || (desc->pos + len) == f->h_wrap) desc = (struct pkt_desc *)((uint8_t *)f->data); else desc = (struct pkt_desc *)((uint8_t *)f->data + desc->pos + len); - if ((desc->pos + len) == f->h_wrap) { - desc = (struct pkt_desc *)((uint8_t *)f->data); - } return desc; } /* Return the number of bytes used */ static uint32_t fifo_len(struct fifo *f) { - while (f->tail % 4) - f->tail++; - f->tail %= f->size; + fifo_align_tail(f); if (f->tail == f->head) return 0; if (f->tail > f->head) { @@ -237,18 +246,55 @@ static uint32_t fifo_len(struct fifo *f) static int fifo_push(struct fifo *f, void *data, uint32_t len) { struct pkt_desc desc; + uint32_t needed = sizeof(struct pkt_desc) + len; + uint32_t head = f->head; + uint32_t tail = f->tail; + uint32_t h_wrap = f->h_wrap; memset(&desc, 0, sizeof(struct pkt_desc)); /* Ensure 4-byte alignment in the buffer */ - if (f->head % 4) - f->head += 4 - (f->head % 4); - if (fifo_space(f) < (sizeof(struct pkt_desc) + len)) - return -1; - desc.pos = f->head; + head = fifo_align_head_pos(head, f->size); + { + uint32_t space; + if (head == tail) + space = f->size; + else if (h_wrap) { + if (head < tail) + space = tail - head; + else + space = 0; + } else if (head >= tail) + space = f->size - (head - tail); + else + space = tail - head; + if (space < needed) + return -1; + } + if (h_wrap && head == h_wrap) + head = 0; + if (h_wrap == 0 && head >= tail) { + uint32_t end_space = f->size - head; + if (end_space < needed) { + if (tail <= needed) + return -1; + h_wrap = head; + head = 0; + } + } + if (h_wrap) { + if (head + needed > tail) + return -1; + } else { + if (head + needed > f->size) + return -1; + } + desc.pos = head; desc.len = len; - memcpy((uint8_t *)f->data + f->head, &desc, sizeof(struct pkt_desc)); - f->head += sizeof(struct pkt_desc); - memcpy((uint8_t *)f->data + f->head, data, len); - f->head += len; + memcpy((uint8_t *)f->data + head, &desc, sizeof(struct pkt_desc)); + head += sizeof(struct pkt_desc); + memcpy((uint8_t *)f->data + head, data, len); + head += len; + f->head = head; + f->h_wrap = h_wrap; return 0; } @@ -258,16 +304,17 @@ static struct pkt_desc *fifo_pop(struct fifo *f) struct pkt_desc *desc; if (f->tail == f->head) return NULL; - while (f->tail % 4) - f->tail++; - f->tail %= f->size; + fifo_align_tail(f); if (f->tail == f->head) return NULL; - if ((f->head < f->tail) && ((f->tail + sizeof(struct pkt_desc) + LINK_MTU > f->size))) - f->tail = 0; desc = (struct pkt_desc *)((uint8_t *)f->data + f->tail); f->tail += sizeof(struct pkt_desc) + desc->len; - f->tail %= f->size; + if (f->h_wrap && f->tail >= f->h_wrap) { + f->tail -= f->h_wrap; + f->h_wrap = 0; + } + if (f->tail >= f->size) + f->tail %= f->size; return desc; } @@ -409,6 +456,12 @@ struct PACKED tcp_opt_mss { uint16_t mss; }; +struct PACKED tcp_opt_ws { + /* Window Scale option (3 bytes) */ + uint8_t opt, len; + uint8_t shift; +}; + /* UDP datagram */ struct PACKED wolfIP_udp_datagram { struct wolfIP_ip_packet ip; @@ -736,8 +789,11 @@ enum tcp_state { struct tcpsocket { enum tcp_state state; uint32_t last_ts, rtt, rto, cwnd, cwnd_count, ssthresh, tmr_rto, rto_backoff, - seq, ack, last_ack, last; + seq, ack, last_ack, last, bytes_in_flight, snd_una; + uint8_t dup_acks; ip4 local_ip, remote_ip; + uint32_t peer_rwnd; + uint8_t snd_wscale, rcv_wscale, ws_enabled, ws_offer; struct fifo txbuf; struct queue rxbuf; }; @@ -1328,6 +1384,22 @@ static struct tsocket *tcp_new_socket(struct wolfIP *s) t->sock.tcp.ssthresh = 64 * TCP_MSS; t->sock.tcp.rtt = 0; t->sock.tcp.rto_backoff = 0; + t->sock.tcp.bytes_in_flight = 0; + t->sock.tcp.snd_una = t->sock.tcp.seq; + t->sock.tcp.dup_acks = 0; + t->sock.tcp.peer_rwnd = 0xFFFF; + t->sock.tcp.snd_wscale = 0; + t->sock.tcp.ws_enabled = 0; + { + uint32_t space = RXBUF_SIZE; + uint8_t shift = 0; + while (shift < 14 && (space >> shift) > 0xFFFF) + shift++; + t->sock.tcp.rcv_wscale = shift; + } + /* We always include WS in the initial SYN (shift may be 0), so + * mark that we offered it to accept the peer's WS in SYN-ACK. */ + t->sock.tcp.ws_offer = 1; queue_init(&t->sock.tcp.rxbuf, t->rxmem, RXBUF_SIZE, 0); fifo_init(&t->sock.tcp.txbuf, t->txmem, TXBUF_SIZE); @@ -1337,6 +1409,49 @@ static struct tsocket *tcp_new_socket(struct wolfIP *s) return NULL; } +static uint16_t tcp_adv_win(const struct tsocket *t) +{ + uint32_t space = queue_space((struct queue *)&t->sock.tcp.rxbuf); + uint8_t shift = t->sock.tcp.ws_enabled ? t->sock.tcp.rcv_wscale : 0; + uint32_t win = space >> shift; + if (win > 0xFFFF) + win = 0xFFFF; + return (uint16_t)win; +} + +static int tcp_process_ws(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) +{ + const uint8_t *opt = (const uint8_t *)tcp->data; + uint32_t opt_len = (uint32_t)((tcp->hlen >> 2) - TCP_HEADER_LEN); + int found = 0; + while (opt_len > 0) { + if (*opt == TCP_OPTION_NOP) { + opt++; + opt_len--; + continue; + } else if (*opt == TCP_OPTION_EOO) { + break; + } + if (opt_len < 2) + break; + if (opt[1] < 2 || opt[1] > opt_len) + break; + if (*opt == TCP_OPTION_WS && opt[1] == TCP_OPTION_WS_LEN) { + uint8_t shift; + if (opt_len < TCP_OPTION_WS_LEN) + break; + shift = opt[2]; + if (shift > 14) + shift = 14; + t->sock.tcp.snd_wscale = shift; + found = 1; + } + opt_len -= opt[1]; + opt += opt[1]; + } + return found; +} + static void tcp_send_empty(struct tsocket *t, uint8_t flags) { struct wolfIP_tcp_seg *tcp; @@ -1350,7 +1465,7 @@ static void tcp_send_empty(struct tsocket *t, uint8_t flags) tcp->ack = ee32(t->sock.tcp.ack); tcp->hlen = ((20 + TCP_OPTIONS_LEN) << 2) & 0xF0; tcp->flags = flags; - tcp->win = ee16((uint16_t)queue_space(&t->sock.tcp.rxbuf)); + tcp->win = ee16(tcp_adv_win(t)); tcp->csum = 0; tcp->urg = 0; ts = (struct tcp_opt_ts *)tcp->data; @@ -1380,31 +1495,56 @@ static void tcp_send_syn(struct tsocket *t, uint8_t flags) struct wolfIP_tcp_seg *tcp; struct tcp_opt_ts *ts; struct tcp_opt_mss *mss; - uint8_t buffer[sizeof(struct wolfIP_tcp_seg) + TCP_OPTIONS_LEN + TCP_OPTION_MSS_LEN]; + struct tcp_opt_ws *ws; + uint8_t *opt; + uint8_t buffer[sizeof(struct wolfIP_tcp_seg) + TCP_SYN_OPTIONS_LEN]; + uint8_t include_ws = 0; + uint8_t opt_len = TCP_OPTIONS_LEN + TCP_OPTION_MSS_LEN; tcp = (struct wolfIP_tcp_seg *)buffer; memset(tcp, 0, sizeof(buffer)); + if (flags & 0x02) { + if ((flags & 0x10) != 0) { + /* SYN-ACK: include WS only when enabled on this socket. */ + include_ws = t->sock.tcp.ws_enabled; + } else { + /* Initial SYN: always include WS to allow peer scaling. */ + include_ws = 1; + } + } + if (include_ws) + opt_len = TCP_SYN_OPTIONS_LEN; tcp->src_port = ee16(t->src_port); tcp->dst_port = ee16(t->dst_port); tcp->seq = ee32(t->sock.tcp.seq); tcp->ack = ee32(t->sock.tcp.ack); - tcp->hlen = ((20 + TCP_OPTIONS_LEN + TCP_OPTION_MSS_LEN) << 2) & 0xF0; + tcp->hlen = ((20 + opt_len) << 2) & 0xF0; tcp->flags = flags; - tcp->win = ee16((uint16_t)queue_space(&t->sock.tcp.rxbuf)); + tcp->win = ee16(tcp_adv_win(t)); tcp->csum = 0; tcp->urg = 0; - ts = (struct tcp_opt_ts *)tcp->data; + opt = tcp->data; + ts = (struct tcp_opt_ts *)opt; ts->opt = TCP_OPTION_TS; ts->len = TCP_OPTION_TS_LEN; ts->val = ee32(t->S->last_tick & 0xFFFFFFFFU); ts->ecr = t->sock.tcp.last_ts; - ts->pad = 0x01; - ts->eoo = 0x01; - mss = (struct tcp_opt_mss *)((uint8_t *)tcp->data + sizeof(struct tcp_opt_ts)); + ts->pad = TCP_OPTION_NOP; + ts->eoo = TCP_OPTION_NOP; + opt += sizeof(*ts); + mss = (struct tcp_opt_mss *)opt; mss->opt = TCP_OPTION_MSS; mss->len = TCP_OPTION_MSS_LEN; mss->mss = ee16(TCP_MSS); - fifo_push(&t->sock.tcp.txbuf, tcp, sizeof(struct wolfIP_tcp_seg) + \ - TCP_OPTIONS_LEN + TCP_OPTION_MSS_LEN); + opt += sizeof(*mss); + if (include_ws) { + ws = (struct tcp_opt_ws *)opt; + ws->opt = TCP_OPTION_WS; + ws->len = TCP_OPTION_WS_LEN; + ws->shift = t->sock.tcp.rcv_wscale; + opt += sizeof(*ws); + *opt++ = TCP_OPTION_NOP; + } + fifo_push(&t->sock.tcp.txbuf, tcp, sizeof(struct wolfIP_tcp_seg) + opt_len); } /* Add a segment to the rx buffer for the application to consume */ @@ -1649,12 +1789,22 @@ static int tcp_process_ts(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) #define SEQ_DIFF(a,b) ((a - b) > 0x7FFFFFFF) ? (b - a) : (a - b) +/* Return true if a <= b + * Take into account wrapping. + */ +static inline int tcp_seq_leq(uint32_t a, uint32_t b) +{ + return (int32_t)(a - b) <= 0; +} + /* Receive an ack */ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) { uint32_t ack = ee32(tcp->ack); struct pkt_desc *desc; int ack_count = 0; + uint32_t inflight_pre = t->sock.tcp.bytes_in_flight; + uint32_t acked_bytes = 0; desc = fifo_peek(&t->sock.tcp.txbuf); while ((desc) && (desc->flags & PKT_FLAG_SENT)) { struct wolfIP_tcp_seg *seg = (struct wolfIP_tcp_seg *)(t->txmem + desc->pos + sizeof(*desc)); @@ -1673,14 +1823,25 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) return; } } - if (SEQ_DIFF(ee32(seg->seq) + seg_len, ack) < fifo_len(&t->sock.tcp.txbuf)) { + if (tcp_seq_leq(ee32(seg->seq) + seg_len, ack)) { desc->flags |= PKT_FLAG_ACKED; + desc->flags &= ~PKT_FLAG_SENT; desc = fifo_next(&t->sock.tcp.txbuf, desc); ack_count++; } else { break; } } + if (t->sock.tcp.snd_una != ack) { + uint32_t delta = ack - t->sock.tcp.snd_una; + if (delta >= t->sock.tcp.bytes_in_flight) + t->sock.tcp.bytes_in_flight = 0; + else + t->sock.tcp.bytes_in_flight -= delta; + t->sock.tcp.snd_una = ack; + t->sock.tcp.dup_acks = 0; + acked_bytes = delta; + } if (ack_count > 0) { struct pkt_desc *fresh_desc = NULL; struct wolfIP_tcp_seg *seg; @@ -1702,21 +1863,31 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) t->sock.tcp.rtt = (7 * (t->sock.tcp.rtt << 3)) + (rtt << 3); } } - /* Update cwnd */ - if (t->sock.tcp.cwnd < t->sock.tcp.ssthresh) { - t->sock.tcp.cwnd += TCP_MSS; - } else { - t->sock.tcp.cwnd_count++; - if (t->sock.tcp.cwnd_count == t->sock.tcp.cwnd) { - t->sock.tcp.cwnd_count = 0; + /* Update cwnd only if we were cwnd-limited. */ + if (t->sock.tcp.cwnd <= (inflight_pre + acked_bytes)) { + if (t->sock.tcp.cwnd < t->sock.tcp.ssthresh) { t->sock.tcp.cwnd += TCP_MSS; + } else { + t->sock.tcp.cwnd_count += TCP_MSS; + if (t->sock.tcp.cwnd_count >= t->sock.tcp.cwnd) { + t->sock.tcp.cwnd_count -= t->sock.tcp.cwnd; + t->sock.tcp.cwnd += TCP_MSS; + } } } if (fifo_space(&t->sock.tcp.txbuf) > 0) t->events |= CB_EVENT_WRITABLE; } } else { - /* Duplicate ack */ + /* Duplicate ack (no advance in snd_una). */ + if (ack != t->sock.tcp.snd_una) + return; + if (inflight_pre == 0) + return; + if (t->sock.tcp.dup_acks < 3) + t->sock.tcp.dup_acks++; + if (t->sock.tcp.dup_acks < 3) + return; t->sock.tcp.ssthresh = t->sock.tcp.cwnd / 2; if (t->sock.tcp.ssthresh < 2 * TCP_MSS) { t->sock.tcp.ssthresh = 2 * TCP_MSS; @@ -1778,6 +1949,29 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, struct wolfIP_tcp_s return; } tcplen = iplen - (IP_HEADER_LEN + (tcp->hlen >> 2)); + if (tcp->flags & 0x02) { + int ws_found = tcp_process_ws(t, tcp); + /* Window scale is negotiated only during SYN/SYN-ACK. */ + if (t->sock.tcp.state == TCP_LISTEN) { + /* Server side: enable if peer offered WS. */ + t->sock.tcp.ws_enabled = ws_found ? 1 : 0; + if (!ws_found) + t->sock.tcp.snd_wscale = 0; + } else if (t->sock.tcp.state == TCP_SYN_SENT) { + /* Client side: only accept WS if we offered it. */ + if (t->sock.tcp.ws_offer && ws_found) { + t->sock.tcp.ws_enabled = 1; + } else { + t->sock.tcp.ws_enabled = 0; + t->sock.tcp.snd_wscale = 0; + } + } + } + { + uint16_t raw_win = ee16(tcp->win); + uint8_t ws_shift = t->sock.tcp.ws_enabled ? t->sock.tcp.snd_wscale : 0; + t->sock.tcp.peer_rwnd = (uint32_t)raw_win << ws_shift; + } /* Check if RST */ if (tcp->flags & 0x04) { if (t->sock.tcp.state == TCP_LISTEN) { @@ -1849,6 +2043,7 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, struct wolfIP_tcp_s t->sock.tcp.state = TCP_ESTABLISHED; t->sock.tcp.ack = ee32(tcp->seq) + 1; t->sock.tcp.seq = ee32(tcp->ack); + t->sock.tcp.snd_una = t->sock.tcp.seq; t->events |= CB_EVENT_WRITABLE; tcp_process_ts(t, tcp); tcp_send_ack(t); @@ -1861,6 +2056,7 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, struct wolfIP_tcp_s t->sock.tcp.state = TCP_ESTABLISHED; t->sock.tcp.ack = ee32(tcp->seq); t->sock.tcp.seq = ee32(tcp->ack); + t->sock.tcp.snd_una = t->sock.tcp.seq; t->events |= CB_EVENT_WRITABLE; } } else if (t->sock.tcp.state == TCP_LAST_ACK) { @@ -1914,6 +2110,10 @@ static void tcp_rto_cb(void *arg) } desc = fifo_next(&ts->sock.tcp.txbuf, desc); } + if (pending) { + /* RTO implies all in-flight data is considered lost. */ + ts->sock.tcp.bytes_in_flight = 0; + } if (ts->sock.tcp.tmr_rto != NO_TIMER) { timer_binheap_cancel(&ts->S->timers, ts->sock.tcp.tmr_rto); @@ -2087,6 +2287,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.snd_una = ts->sock.tcp.seq; if (wolfIP_filter_notify_socket_event( WOLFIP_FILT_CONNECTING, s, ts, ts->local_ip, ts->src_port, ts->remote_ip, ts->dst_port) != 0) { @@ -2111,7 +2312,7 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add if (sockfd < 0) return -WOLFIP_EINVAL; - if (addr && addrlen) + if (addrlen) *addrlen = sizeof(struct wolfIP_sockaddr_in); if (IS_SOCKET_TCP(sockfd)) { @@ -2138,6 +2339,11 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add newts->sock.tcp.ack = ts->sock.tcp.ack; newts->sock.tcp.seq = ts->sock.tcp.seq; newts->sock.tcp.last_ts = ts->sock.tcp.last_ts; + newts->sock.tcp.peer_rwnd = ts->sock.tcp.peer_rwnd; + newts->sock.tcp.snd_wscale = ts->sock.tcp.snd_wscale; + newts->sock.tcp.rcv_wscale = ts->sock.tcp.rcv_wscale; + newts->sock.tcp.ws_enabled = ts->sock.tcp.ws_enabled; + newts->sock.tcp.ws_offer = ts->sock.tcp.ws_offer; newts->sock.tcp.state = TCP_ESTABLISHED; /* Send SYN-ACK to accept connection. * Send the syn-ack from the newly established socket: @@ -2206,8 +2412,13 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len uint32_t payload_len = len - sent; if (payload_len > (TCP_MSS - TCP_OPTIONS_LEN)) payload_len = (TCP_MSS - TCP_OPTIONS_LEN); - if (fifo_space(&ts->sock.tcp.txbuf) < payload_len + sizeof(struct pkt_desc) + IP_HEADER_LEN + TCP_HEADER_LEN + TCP_OPTIONS_LEN) { - break; + { + uint32_t need = payload_len + sizeof(struct pkt_desc) + IP_HEADER_LEN + + TCP_HEADER_LEN + TCP_OPTIONS_LEN; + uint32_t space = fifo_space(&ts->sock.tcp.txbuf); + if (space < need) { + break; + } } memset(tcp, 0, sizeof(struct wolfIP_tcp_seg)); tcp->src_port = ee16(ts->src_port); @@ -2216,7 +2427,7 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len tcp->ack = ee32(ts->sock.tcp.ack); tcp->hlen = (TCP_HEADER_LEN + TCP_OPTIONS_LEN) << 2; tcp->flags = 0x10 | ((sent == 0)? 0x08 : 0); /* ACK; PSH only on first */ - tcp->win = ee16(queue_space(&ts->sock.tcp.rxbuf)); + tcp->win = ee16(tcp_adv_win(ts)); tcp->csum = 0; tcp->urg = 0; tsopt->opt = TCP_OPTION_TS; @@ -2226,13 +2437,16 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len tsopt->pad = 0x01; tsopt->eoo = 0x00; memcpy((uint8_t *)tcp->data + TCP_OPTIONS_LEN, (const uint8_t *)buf + sent, payload_len); - fifo_push(&ts->sock.tcp.txbuf, tcp, sizeof(struct wolfIP_tcp_seg) + TCP_OPTIONS_LEN + payload_len); + if (fifo_push(&ts->sock.tcp.txbuf, tcp, + sizeof(struct wolfIP_tcp_seg) + TCP_OPTIONS_LEN + payload_len) < 0) { + break; + } sent += payload_len; ts->sock.tcp.seq += payload_len; } - if (sent == 0) + if (sent == 0) { return -WOLFIP_EAGAIN; - else + } else return sent; } else if (IS_SOCKET_UDP(sockfd)) { const struct wolfIP_sockaddr_in *sin = (const struct wolfIP_sockaddr_in *)dest_addr; @@ -2255,8 +2469,9 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len return -1; if (len > WI_IP_MTU - IP_HEADER_LEN - UDP_HEADER_LEN) return -1; /* Fragmentation not supported */ - if (fifo_space(&ts->sock.udp.txbuf) < len) + if (fifo_space(&ts->sock.udp.txbuf) < len) { return -WOLFIP_EAGAIN; + } if (ts->src_port == 0) { ts->src_port = (uint16_t)(wolfIP_getrandom() & 0xFFFF); if (ts->src_port < 1024) @@ -2280,7 +2495,8 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len udp->len = ee16(len + UDP_HEADER_LEN); udp->csum = 0; memcpy(udp->data, buf, len); - fifo_push(&ts->sock.udp.txbuf, udp, sizeof(struct wolfIP_udp_datagram) + len); + if (fifo_push(&ts->sock.udp.txbuf, udp, sizeof(struct wolfIP_udp_datagram) + len) < 0) + return -WOLFIP_EAGAIN; return len; } else if (IS_SOCKET_ICMP(sockfd)) { const struct wolfIP_sockaddr_in *sin = (const struct wolfIP_sockaddr_in *)dest_addr; @@ -2299,8 +2515,9 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len return -1; if (payload_len < ICMP_HEADER_LEN || payload_len > (WI_IP_MTU - IP_HEADER_LEN)) return -WOLFIP_EINVAL; - if (fifo_space(&ts->sock.udp.txbuf) < payload_len) + if (fifo_space(&ts->sock.udp.txbuf) < payload_len) { return -WOLFIP_EAGAIN; + } if (ts->src_port == 0) { ts->src_port = (uint16_t)(wolfIP_getrandom() & 0xFFFF); if (ts->src_port == 0) @@ -2323,7 +2540,8 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len icmp_set_echo_id(icmp, ts->src_port); icmp->csum = 0; icmp->csum = ee16(icmp_checksum(icmp, (uint16_t)payload_len)); - fifo_push(&ts->sock.udp.txbuf, icmp, sizeof(struct wolfIP_ip_packet) + payload_len); + if (fifo_push(&ts->sock.udp.txbuf, icmp, sizeof(struct wolfIP_ip_packet) + payload_len) < 0) + return -WOLFIP_EAGAIN; return (int)payload_len; } else return -1; } @@ -3403,7 +3621,7 @@ static inline void ip_recv(struct wolfIP *s, unsigned int if_idx, struct wolfIP_ #endif if (wolfIP_filter_notify_ip(WOLFIP_FILT_RECEIVING, s, if_idx, ip, len) != 0) return; -#if WOLFIP_ENABLE_FORWARDING + #if WOLFIP_ENABLE_FORWARDING if (ip->ver_ihl == 0x45) { ip4 dest = ee32(ip->dst); int is_local = 0; @@ -3830,6 +4048,7 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) /* Step 1: Poll the device */ for (if_idx = 0; if_idx < s->if_count; if_idx++) { struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, if_idx); + int budget = WOLFIP_POLL_BUDGET; if (!ll || !ll->poll) continue; do { @@ -3837,8 +4056,9 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) if (len > 0) { /* Process packet */ wolfIP_recv_on(s, if_idx, buf, len); + budget--; } - } while (len > 0); + } while (len > 0 && budget > 0); } /* Step 2: Handle timers */ while(is_timer_expired(&s->timers, now)) { @@ -3871,7 +4091,7 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) /* Step 4: attempt to write any pending data */ for (i = 0; i < MAX_TCPSOCKETS; i++) { struct tsocket *ts = &s->tcpsockets[i]; - uint32_t in_flight = 0; + uint32_t in_flight = ts->sock.tcp.bytes_in_flight; uint32_t size = 0; struct pkt_desc *desc; struct wolfIP_tcp_seg *tcp; @@ -3880,7 +4100,6 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) unsigned int tx_if = wolfIP_socket_if_idx(ts); tcp = (struct wolfIP_tcp_seg *)(ts->txmem + desc->pos + sizeof(*desc)); if (desc->flags & PKT_FLAG_SENT) { - in_flight += ee16(tcp->ip.len) - (IP_HEADER_LEN + (tcp->hlen >> 2)); desc = fifo_next(&ts->sock.tcp.txbuf, desc); continue; } else { @@ -3897,7 +4116,11 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) break; } #endif - if (in_flight <= ts->sock.tcp.cwnd) { + { + uint32_t snd_wnd = ts->sock.tcp.cwnd; + if (ts->sock.tcp.peer_rwnd < snd_wnd) + snd_wnd = ts->sock.tcp.peer_rwnd; + if (in_flight < snd_wnd) { struct wolfIP_timer new_tmr = {}; size = desc->len - ETH_HEADER_LEN; tcp = (struct wolfIP_tcp_seg *)(ts->txmem + desc->pos + sizeof(*desc)); @@ -3912,15 +4135,18 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) /* Refresh ack counter */ ts->sock.tcp.last_ack = ts->sock.tcp.ack; tcp->ack = ee32(ts->sock.tcp.ack); - tcp->win = ee16(queue_space(&ts->sock.tcp.rxbuf)); + tcp->win = ee16(tcp_adv_win(ts)); ip_output_add_header(ts, (struct wolfIP_ip_packet *)tcp, WI_IPPROTO_TCP, size); - if (wolfIP_filter_notify_tcp(WOLFIP_FILT_SENDING, ts->S, tx_if, tcp, desc->len) != 0) + if (wolfIP_filter_notify_tcp(WOLFIP_FILT_SENDING, ts->S, tx_if, tcp, desc->len) != 0) { break; - if (wolfIP_filter_notify_ip(WOLFIP_FILT_SENDING, ts->S, tx_if, &tcp->ip, desc->len) != 0) + } + if (wolfIP_filter_notify_ip(WOLFIP_FILT_SENDING, ts->S, tx_if, &tcp->ip, desc->len) != 0) { break; + } #ifdef ETHERNET - if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, ts->S, tx_if, &tcp->ip.eth, desc->len) != 0) + if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, ts->S, tx_if, &tcp->ip.eth, desc->len) != 0) { break; + } #endif { struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, tx_if); @@ -3943,11 +4169,13 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) new_tmr.arg = ts; ts->sock.tcp.tmr_rto = timers_binheap_insert(&s->timers, new_tmr); in_flight += payload_len; + ts->sock.tcp.bytes_in_flight += payload_len; desc = fifo_next(&ts->sock.tcp.txbuf, desc); } } else { break; } + } } } }