From 14d7e108b4f550eb60cd77f32c99eb9a5b29b24c Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Sun, 16 Nov 2025 13:44:26 +0100 Subject: [PATCH 1/2] Fix socket local_ip selection with multi_if --- src/test/unit/unit.c | 207 +++++++++++++++++++++++++++++++++++++++++++ src/wolfip.c | 109 +++++++++++++++++------ 2 files changed, 289 insertions(+), 27 deletions(-) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 5efbb7d..80d3a21 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -102,6 +102,56 @@ static void reset_heap(void) { heap.size = 0; } +static void setup_stack_with_two_ifaces(struct wolfIP *s, ip4 primary_ip, ip4 secondary_ip) +{ + wolfIP_init(s); + mock_link_init(s); + mock_link_init_idx(s, TEST_SECOND_IF, NULL); + wolfIP_ipconfig_set(s, primary_ip, 0xFFFFFF00U, 0); + wolfIP_ipconfig_set_ex(s, TEST_SECOND_IF, secondary_ip, 0xFFFFFF00U, 0); +} + +static void inject_tcp_syn(struct wolfIP *s, unsigned int if_idx, ip4 dst_ip, uint16_t dst_port) +{ + struct wolfIP_tcp_seg syn; + struct wolfIP_ll_dev *ll = wolfIP_getdev_ex(s, if_idx); + union transport_pseudo_header ph; + static const uint8_t src_mac[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60}; + + ck_assert_ptr_nonnull(ll); + memset(&syn, 0, sizeof(syn)); + memcpy(syn.ip.eth.dst, ll->mac, 6); + memcpy(syn.ip.eth.src, src_mac, 6); + syn.ip.eth.type = ee16(ETH_TYPE_IP); + syn.ip.ver_ihl = 0x45; + syn.ip.ttl = 64; + syn.ip.proto = WI_IPPROTO_TCP; + syn.ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN); + syn.ip.src = ee32(0x0A0000A1U); + syn.ip.dst = ee32(dst_ip); + syn.ip.csum = 0; + iphdr_set_checksum(&syn.ip); + + syn.src_port = ee16(40000); + syn.dst_port = ee16(dst_port); + syn.seq = ee32(1); + syn.ack = 0; + syn.hlen = TCP_HEADER_LEN << 2; + syn.flags = 0x02; + syn.win = ee16(65535); + syn.csum = 0; + syn.urg = 0; + + memset(&ph, 0, sizeof(ph)); + ph.ph.src = syn.ip.src; + ph.ph.dst = syn.ip.dst; + ph.ph.proto = WI_IPPROTO_TCP; + ph.ph.len = ee16(TCP_HEADER_LEN); + syn.csum = ee16(transport_checksum(&ph, &syn.src_port)); + + tcp_input(s, if_idx, &syn, sizeof(struct wolfIP_eth_frame) + IP_HEADER_LEN + TCP_HEADER_LEN); +} + START_TEST(test_fifo_init) { @@ -934,6 +984,155 @@ START_TEST(test_loopback_dest_not_forwarded) } END_TEST +START_TEST(test_tcp_listen_rejects_wrong_interface) +{ + struct wolfIP s; + const ip4 primary_ip = 0xC0A80001U; + const ip4 secondary_ip = 0xC0A80101U; + const uint16_t listen_port = 12345; + int listen_fd; + struct wolfIP_sockaddr_in addr; + struct tsocket *listener; + + setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip); + + listen_fd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, 0); + ck_assert_int_ge(listen_fd, 0); + listener = &s.tcpsockets[listen_fd & ~MARK_TCP_SOCKET]; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = ee16(listen_port); + addr.sin_addr.s_addr = ee32(primary_ip); + ck_assert_int_eq(wolfIP_sock_bind(&s, listen_fd, (struct wolfIP_sockaddr *)&addr, sizeof(addr)), 0); + ck_assert_int_eq(wolfIP_sock_listen(&s, listen_fd, 1), 0); + + inject_tcp_syn(&s, TEST_SECOND_IF, secondary_ip, listen_port); + + ck_assert_int_eq(listener->sock.tcp.state, TCP_LISTEN); + ck_assert_int_eq(listener->events & CB_EVENT_READABLE, 0); +} +END_TEST + +START_TEST(test_tcp_listen_accepts_bound_interface) +{ + struct wolfIP s; + const ip4 primary_ip = 0xC0A80002U; + const ip4 secondary_ip = 0xC0A80101U; + const uint16_t listen_port = 23456; + int listen_fd; + int client_fd; + struct wolfIP_sockaddr_in addr; + struct tsocket *listener; + struct tsocket *client; + + setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip); + + listen_fd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, 0); + ck_assert_int_ge(listen_fd, 0); + listener = &s.tcpsockets[listen_fd & ~MARK_TCP_SOCKET]; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = ee16(listen_port); + addr.sin_addr.s_addr = ee32(secondary_ip); + ck_assert_int_eq(wolfIP_sock_bind(&s, listen_fd, (struct wolfIP_sockaddr *)&addr, sizeof(addr)), 0); + ck_assert_int_eq(wolfIP_sock_listen(&s, listen_fd, 1), 0); + + inject_tcp_syn(&s, TEST_SECOND_IF, secondary_ip, listen_port); + + ck_assert_int_eq(listener->sock.tcp.state, TCP_SYN_RCVD); + ck_assert_uint_eq(listener->local_ip, secondary_ip); + ck_assert_uint_eq(listener->if_idx, TEST_SECOND_IF); + + client_fd = wolfIP_sock_accept(&s, listen_fd, NULL, NULL); + ck_assert_int_ge(client_fd, 0); + client = &s.tcpsockets[client_fd & ~MARK_TCP_SOCKET]; + ck_assert_uint_eq(client->local_ip, secondary_ip); + ck_assert_uint_eq(client->bound_local_ip, secondary_ip); + ck_assert_int_eq(client->sock.tcp.state, TCP_ESTABLISHED); +} +END_TEST + +START_TEST(test_tcp_listen_accepts_any_interface) +{ + struct wolfIP s; + const ip4 primary_ip = 0xC0A80005U; + const ip4 secondary_ip = 0xC0A80105U; + const uint16_t listen_port = 34567; + int listen_fd; + int client_fd; + struct wolfIP_sockaddr_in addr; + struct tsocket *listener; + struct tsocket *client; + + setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip); + + listen_fd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, 0); + ck_assert_int_ge(listen_fd, 0); + listener = &s.tcpsockets[listen_fd & ~MARK_TCP_SOCKET]; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = ee16(listen_port); + addr.sin_addr.s_addr = ee32(IPADDR_ANY); + ck_assert_int_eq(wolfIP_sock_bind(&s, listen_fd, (struct wolfIP_sockaddr *)&addr, sizeof(addr)), 0); + ck_assert_int_eq(wolfIP_sock_listen(&s, listen_fd, 1), 0); + + inject_tcp_syn(&s, TEST_SECOND_IF, secondary_ip, listen_port); + + ck_assert_int_eq(listener->sock.tcp.state, TCP_SYN_RCVD); + ck_assert_uint_eq(listener->local_ip, secondary_ip); + ck_assert_uint_eq(listener->if_idx, TEST_SECOND_IF); + + client_fd = wolfIP_sock_accept(&s, listen_fd, NULL, NULL); + ck_assert_int_ge(client_fd, 0); + client = &s.tcpsockets[client_fd & ~MARK_TCP_SOCKET]; + ck_assert_uint_eq(client->local_ip, secondary_ip); + ck_assert_int_eq(client->sock.tcp.state, TCP_ESTABLISHED); +} +END_TEST + +START_TEST(test_sock_connect_selects_local_ip_multi_if) +{ + struct wolfIP s; + const ip4 primary_ip = 0xC0A80009U; + const ip4 secondary_ip = 0xC0A80109U; + const ip4 remote_primary = 0xC0A800AAU; + const ip4 remote_secondary = 0xC0A801A1U; + int udp_fd; + int tcp_fd; + struct wolfIP_sockaddr_in dst; + struct tsocket *ts; + int ret; + + setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip); + + udp_fd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, 0); + ck_assert_int_ge(udp_fd, 0); + memset(&dst, 0, sizeof(dst)); + dst.sin_family = AF_INET; + dst.sin_port = ee16(5555); + dst.sin_addr.s_addr = ee32(remote_secondary); + ck_assert_int_eq(wolfIP_sock_connect(&s, udp_fd, (struct wolfIP_sockaddr *)&dst, sizeof(dst)), 0); + ts = &s.udpsockets[udp_fd & ~MARK_UDP_SOCKET]; + ck_assert_uint_eq(ts->local_ip, secondary_ip); + ck_assert_uint_eq(ts->if_idx, TEST_SECOND_IF); + + tcp_fd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, 0); + ck_assert_int_ge(tcp_fd, 0); + memset(&dst, 0, sizeof(dst)); + dst.sin_family = AF_INET; + dst.sin_port = ee16(8080); + dst.sin_addr.s_addr = ee32(remote_primary); + ret = wolfIP_sock_connect(&s, tcp_fd, (struct wolfIP_sockaddr *)&dst, sizeof(dst)); + ck_assert_int_eq(ret, -WOLFIP_EAGAIN); + ts = &s.tcpsockets[tcp_fd & ~MARK_TCP_SOCKET]; + ck_assert_uint_eq(ts->local_ip, primary_ip); + ck_assert_uint_eq(ts->if_idx, TEST_PRIMARY_IF); +} +END_TEST + // Test for `transport_checksum` calculation START_TEST(test_transport_checksum) { @@ -1136,6 +1335,14 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_proto); tcase_add_test(tc_proto, test_loopback_dest_not_forwarded); suite_add_tcase(s, tc_proto); + tcase_add_test(tc_proto, test_tcp_listen_rejects_wrong_interface); + suite_add_tcase(s, tc_proto); + tcase_add_test(tc_proto, test_tcp_listen_accepts_bound_interface); + suite_add_tcase(s, tc_proto); + tcase_add_test(tc_proto, test_tcp_listen_accepts_any_interface); + suite_add_tcase(s, tc_proto); + tcase_add_test(tc_proto, test_sock_connect_selects_local_ip_multi_if); + suite_add_tcase(s, tc_proto); tcase_add_test(tc_utils, test_transport_checksum); suite_add_tcase(s, tc_proto); diff --git a/src/wolfip.c b/src/wolfip.c index abd8c5d..66c8f4a 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -733,6 +733,7 @@ struct tsocket { } sock; uint16_t proto, events; ip4 local_ip, remote_ip; + ip4 bound_local_ip; uint16_t src_port, dst_port; struct wolfIP *S; #ifdef ETHERNET @@ -1652,8 +1653,6 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) /* Preselect socket, parse options, manage handshakes, pass to application */ static void tcp_input(struct wolfIP *S, unsigned int if_idx, struct wolfIP_tcp_seg *tcp, uint32_t frame_len) { - struct ipconf *conf = wolfIP_ipconf_at(S, if_idx); - ip4 local_ip = conf ? conf->ip : IPADDR_ANY; int i; if (wolfIP_filter_notify_tcp(WOLFIP_FILT_RECEIVING, S, if_idx, tcp, frame_len) != 0) return; @@ -1661,8 +1660,7 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, struct wolfIP_tcp_s uint32_t tcplen; uint32_t iplen; struct tsocket *t = &S->tcpsockets[i]; - if (t->src_port == ee16(tcp->dst_port) && - t->local_ip == ee32(tcp->ip.dst) && t->remote_ip != local_ip) { + if (t->src_port == ee16(tcp->dst_port)) { t->if_idx = (uint8_t)if_idx; /* TCP segment sanity checks */ iplen = ee16(tcp->ip.len); @@ -1671,7 +1669,7 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, struct wolfIP_tcp_s } if (t->sock.tcp.state > TCP_LISTEN) { - if (t->dst_port != ee16(tcp->src_port) || t->remote_ip != ee32(tcp->ip.src)) { + if (t->dst_port != ee16(tcp->src_port)) { /* Not the right socket */ continue; } @@ -1712,6 +1710,21 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, struct wolfIP_tcp_s /* Check if SYN */ if (tcp->flags & 0x02) { if (t->sock.tcp.state == TCP_LISTEN) { + ip4 syn_dst = ee32(tcp->ip.dst); + int dst_match = 0; + unsigned int dst_if; + + if (syn_dst == IPADDR_ANY) + continue; + if (t->bound_local_ip != IPADDR_ANY && t->bound_local_ip != syn_dst) + continue; + + dst_if = wolfIP_if_for_local_ip(S, syn_dst, &dst_match); + if (!dst_match) + continue; + + t->local_ip = syn_dst; + t->if_idx = (uint8_t)dst_if; t->sock.tcp.state = TCP_SYN_RCVD; t->sock.tcp.ack = ee32(tcp->seq) + 1; t->sock.tcp.seq = wolfIP_getrandom(); @@ -1852,15 +1865,23 @@ int wolfIP_sock_connect(struct wolfIP *s, int sockfd, const struct wolfIP_sockad ts = &s->udpsockets[sockfd & ~MARK_UDP_SOCKET]; ts->dst_port = ee16(sin->sin_port); ts->remote_ip = ee32(sin->sin_addr.s_addr); - if_idx = wolfIP_route_for_ip(s, ts->remote_ip); - conf = wolfIP_ipconf_at(s, if_idx); - ts->if_idx = (uint8_t)if_idx; - if (ts->local_ip == 0 && conf && conf->ip != IPADDR_ANY) - ts->local_ip = conf->ip; - else if (ts->local_ip == 0) { - struct ipconf *primary = wolfIP_primary_ipconf(s); - if (primary && primary->ip != IPADDR_ANY) - ts->local_ip = primary->ip; + if (ts->bound_local_ip != IPADDR_ANY) { + int bound_match = 0; + unsigned int bound_if = wolfIP_if_for_local_ip(s, ts->bound_local_ip, &bound_match); + if (!bound_match) + return -WOLFIP_EINVAL; + ts->if_idx = (uint8_t)bound_if; + ts->local_ip = ts->bound_local_ip; + } else { + if_idx = wolfIP_route_for_ip(s, ts->remote_ip); + conf = wolfIP_ipconf_at(s, if_idx); + ts->if_idx = (uint8_t)if_idx; + if (conf && conf->ip != IPADDR_ANY) + ts->local_ip = conf->ip; + else { + struct ipconf *primary = wolfIP_primary_ipconf(s); + ts->local_ip = (primary && primary->ip != IPADDR_ANY) ? primary->ip : IPADDR_ANY; + } } return 0; } @@ -1881,17 +1902,26 @@ int wolfIP_sock_connect(struct wolfIP *s, int sockfd, const struct wolfIP_sockad struct ipconf *conf; ts->sock.tcp.state = TCP_SYN_SENT; ts->remote_ip = ee32(sin->sin_addr.s_addr); - if_idx = wolfIP_route_for_ip(s, ts->remote_ip); - conf = wolfIP_ipconf_at(s, if_idx); - ts->if_idx = (uint8_t)if_idx; - if (conf && conf->ip != IPADDR_ANY) - ts->local_ip = conf->ip; - else { - struct ipconf *primary = wolfIP_primary_ipconf(s); - if (primary && primary->ip != IPADDR_ANY) - ts->local_ip = primary->ip; - else - ts->local_ip = 0; + if (ts->bound_local_ip != IPADDR_ANY) { + int bound_match = 0; + unsigned int bound_if = wolfIP_if_for_local_ip(s, ts->bound_local_ip, &bound_match); + if (!bound_match) + return -WOLFIP_EINVAL; + ts->if_idx = (uint8_t)bound_if; + ts->local_ip = ts->bound_local_ip; + } else { + if_idx = wolfIP_route_for_ip(s, ts->remote_ip); + conf = wolfIP_ipconf_at(s, if_idx); + ts->if_idx = (uint8_t)if_idx; + if (conf && conf->ip != IPADDR_ANY) + ts->local_ip = conf->ip; + else { + struct ipconf *primary = wolfIP_primary_ipconf(s); + if (primary && primary->ip != IPADDR_ANY) + ts->local_ip = primary->ip; + else + ts->local_ip = IPADDR_ANY; + } } if (!ts->src_port) ts->src_port = (uint16_t)(wolfIP_getrandom() & 0xFFFF); @@ -1933,6 +1963,9 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add return -1; if (ts->sock.tcp.state == TCP_SYN_RCVD) { + ip4 conn_local = ts->local_ip; + uint8_t conn_if = ts->if_idx; + tcp_send_synack(ts); newts = tcp_new_socket(s); if (!newts) @@ -1941,8 +1974,9 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add newts->events |= CB_EVENT_WRITABLE; newts->callback = ts->callback; newts->callback_arg = ts->callback_arg; - newts->local_ip = ts->local_ip; - newts->if_idx = ts->if_idx; + newts->local_ip = conn_local; + newts->bound_local_ip = conn_local; + newts->if_idx = conn_if; newts->remote_ip = ts->remote_ip; newts->src_port = ts->src_port; newts->dst_port = ts->dst_port; @@ -1956,6 +1990,15 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add } ts->sock.tcp.state = TCP_LISTEN; ts->sock.tcp.seq = wolfIP_getrandom(); + if (ts->bound_local_ip != IPADDR_ANY) { + int bound_match = 0; + unsigned int bound_if = wolfIP_if_for_local_ip(s, ts->bound_local_ip, &bound_match); + ts->if_idx = bound_match ? (uint8_t)bound_if : ts->if_idx; + ts->local_ip = ts->bound_local_ip; + } else { + ts->local_ip = IPADDR_ANY; + ts->if_idx = 0; + } if (wolfIP_filter_notify_socket_event( WOLFIP_FILT_ACCEPTING, s, newts, newts->local_ip, newts->src_port, newts->remote_ip, newts->dst_port) != 0) { @@ -2287,6 +2330,14 @@ int wolfIP_sock_bind(struct wolfIP *s, int sockfd, const struct wolfIP_sockaddr } ts->src_port = new_port; } + ts->if_idx = (bind_ip != IPADDR_ANY) ? (uint8_t)if_idx : 0U; + if (bind_ip != IPADDR_ANY) { + ts->local_ip = bind_ip; + } else { + ts->local_ip = IPADDR_ANY; + } + ts->bound_local_ip = ts->local_ip; + ts->src_port = ee16(sin->sin_port); return 0; } else if (sockfd & MARK_UDP_SOCKET) { if ((sockfd & ~MARK_UDP_SOCKET) >= MAX_UDPSOCKETS) @@ -2319,6 +2370,8 @@ int wolfIP_sock_bind(struct wolfIP *s, int sockfd, const struct wolfIP_sockaddr return -1; } } + ts->bound_local_ip = ts->local_ip; + ts->src_port = ee16(sin->sin_port); return 0; } else return -1; @@ -2922,6 +2975,7 @@ struct wolfIP_ll_dev *wolfIP_getdev_ex(struct wolfIP *s, unsigned int if_idx) return wolfIP_ll_at(s, if_idx); } +#ifndef WOLFIP_NOSTATIC static struct wolfIP wolfIP_static; void wolfIP_init_static(struct wolfIP **s) { @@ -2930,6 +2984,7 @@ void wolfIP_init_static(struct wolfIP **s) wolfIP_init(&wolfIP_static); *s = &wolfIP_static; } +#endif size_t wolfIP_instance_size(void) { From 1e2d1cbc08b0dcbda6078acd279bbdda03beaac2 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Sun, 16 Nov 2025 15:46:59 +0100 Subject: [PATCH 2/2] Actually assign correct address to socket upon accept --- src/wolfip.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wolfip.c b/src/wolfip.c index 66c8f4a..e691b57 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -1996,8 +1996,8 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add ts->if_idx = bound_match ? (uint8_t)bound_if : ts->if_idx; ts->local_ip = ts->bound_local_ip; } else { - ts->local_ip = IPADDR_ANY; - ts->if_idx = 0; + ts->local_ip = conn_local; + ts->if_idx = conn_if; } if (wolfIP_filter_notify_socket_event( WOLFIP_FILT_ACCEPTING, s, newts,