From d619ee6541efb523b5955b0c493244e355bd3afa Mon Sep 17 00:00:00 2001 From: Shunchao Hu Date: Mon, 27 Apr 2026 16:33:07 +0800 Subject: [PATCH] net/nat: Let ICMP echo id zero be a valid NAT id. Make nat_port_select() return an error code and report the selected external port through an output parameter. This separates selection failure from the valid ICMP echo identifier value zero, so outbound NAT entry creation no longer rejects ICMP echo requests that use id zero. Signed-off-by: Shunchao Hu --- net/nat/ipv4_nat_entry.c | 8 ++-- net/nat/ipv6_nat_entry.c | 7 ++-- net/nat/nat.c | 83 +++++++++++++++++++++++++--------------- net/nat/nat.h | 22 ++++++----- 4 files changed, 74 insertions(+), 46 deletions(-) diff --git a/net/nat/ipv4_nat_entry.c b/net/nat/ipv4_nat_entry.c index 0d2645243b9..189143cc6ee 100644 --- a/net/nat/ipv4_nat_entry.c +++ b/net/nat/ipv4_nat_entry.c @@ -411,6 +411,7 @@ ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol, FAR hash_node_t *tmp; uint16_t external_port; int32_t current_time = TICK2SEC(clock_systime_ticks()); + int ret; ipv4_nat_reclaim_entry(current_time); @@ -454,9 +455,10 @@ ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol, "proto=%" PRIu8 ", local=%" PRIx32 ":%" PRIu16 ", try create one.\n", protocol, local_ip, local_port); - external_port = nat_port_select(dev, PF_INET, protocol, - (FAR union ip_addr_u *)&dev->d_ipaddr, local_port); - if (!external_port) + ret = nat_port_select(dev, PF_INET, protocol, + (FAR union ip_addr_u *)&dev->d_ipaddr, local_port, + &external_port); + if (ret < 0) { nwarn("WARNING: Failed to find an available port!\n"); return NULL; diff --git a/net/nat/ipv6_nat_entry.c b/net/nat/ipv6_nat_entry.c index 1f8229dae89..6f6c6309327 100644 --- a/net/nat/ipv6_nat_entry.c +++ b/net/nat/ipv6_nat_entry.c @@ -411,6 +411,7 @@ ipv6_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol, FAR union ip_addr_u *external_ip; uint16_t external_port; int32_t current_time = TICK2SEC(clock_systime_ticks()); + int ret; ipv6_nat_reclaim_entry(current_time); @@ -457,10 +458,10 @@ ipv6_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol, local_ip[4], local_ip[5], local_ip[6], local_ip[7], local_port); external_ip = (FAR union ip_addr_u *)netdev_ipv6_srcaddr(dev, peer_ip); - external_port = nat_port_select(dev, PF_INET6, protocol, - external_ip, local_port); + ret = nat_port_select(dev, PF_INET6, protocol, external_ip, local_port, + &external_port); - if (!external_port) + if (ret < 0) { nwarn("WARNING: Failed to find an available port!\n"); return NULL; diff --git a/net/nat/nat.c b/net/nat/nat.c index 9ff052a1c5d..ff2ea994954 100644 --- a/net/nat/nat.c +++ b/net/nat/nat.c @@ -56,13 +56,14 @@ static rmutex_t g_nat_lock = NXRMUTEX_INITIALIZER; * Used when corresponding stack is disabled. * * Input Parameters: - * domain - The domain of the packet. - * protocol - The L4 protocol of the packet. - * ip - The IP bind with the port (in network byte order). - * local_port - The local port (in network byte order), as reference. + * domain - The domain of the packet. + * protocol - The L4 protocol of the packet. + * ip - The IP bind with the port (in network byte order). + * local_port - The local port (in network byte order), as reference. + * external_port - The selected external port (in network byte order). * * Returned Value: - * port number on success; 0 on failure + * 0 on success; a negated errno on failure. * ****************************************************************************/ @@ -71,12 +72,15 @@ static rmutex_t g_nat_lock = NXRMUTEX_INITIALIZER; (defined(CONFIG_NET_ICMP) && !defined(CONFIG_NET_ICMP_SOCKET)) || \ (defined(CONFIG_NET_ICMPv6) && !defined(CONFIG_NET_ICMPv6_SOCKET)) -static uint16_t nat_port_select_without_stack( +static int nat_port_select_without_stack( uint8_t domain, uint8_t protocol, FAR const union ip_addr_u *ip, - uint16_t local_port) + uint16_t local_port, FAR uint16_t *external_port) { uint16_t portno = local_port; uint16_t hport = NTOHS(portno); + + DEBUGASSERT(external_port != NULL); + while (nat_port_inuse(domain, protocol, ip, portno)) { NET_PORT_NEXT_NH(portno, hport); @@ -84,11 +88,12 @@ static uint16_t nat_port_select_without_stack( { /* We have looped back, failed. */ - return 0; + return -EADDRINUSE; } } - return portno; + *external_port = portno; + return OK; } #endif @@ -216,22 +221,26 @@ bool nat_port_inuse(uint8_t domain, uint8_t protocol, * Select an available port number for TCP/UDP protocol, or id for ICMP. * * Input Parameters: - * dev - The device on which the packet will be sent. - * domain - The domain of the packet. - * protocol - The L4 protocol of the packet. - * external_ip - The external IP bind with the port. - * local_port - The local port of the packet, as reference. + * dev - The device on which the packet will be sent. + * domain - The domain of the packet. + * protocol - The L4 protocol of the packet. + * external_ip - The external IP bind with the port. + * local_port - The local port of the packet, as reference. + * external_port - The selected external port. * * Returned Value: - * External port number on success; 0 on failure + * 0 on success; a negated errno on failure. * ****************************************************************************/ -uint16_t nat_port_select(FAR struct net_driver_s *dev, - uint8_t domain, uint8_t protocol, - FAR const union ip_addr_u *external_ip, - uint16_t local_port) +int nat_port_select(FAR struct net_driver_s *dev, + uint8_t domain, uint8_t protocol, + FAR const union ip_addr_u *external_ip, + uint16_t local_port, + FAR uint16_t *external_port) { + DEBUGASSERT(external_port != NULL); + switch (protocol) { #ifdef CONFIG_NET_TCP @@ -249,10 +258,17 @@ uint16_t nat_port_select(FAR struct net_driver_s *dev, ret = tcp_selectport(domain, external_ip, 0); } - return ret > 0 ? ret : 0; + if (ret < 0) + { + return ret; + } + + *external_port = ret; + return OK; #else return nat_port_select_without_stack(domain, IP_PROTO_TCP, - external_ip, local_port); + external_ip, local_port, + external_port); #endif } #endif @@ -284,10 +300,12 @@ uint16_t nat_port_select(FAR struct net_driver_s *dev, /* TODO: Try keep origin port as possible. */ - return HTONS(udp_select_port(domain, &u)); + *external_port = HTONS(udp_select_port(domain, &u)); + return *external_port == 0 ? -EADDRINUSE : OK; #else return nat_port_select_without_stack(domain, IP_PROTO_UDP, - external_ip, local_port); + external_ip, local_port, + external_port); #endif } #endif @@ -306,14 +324,16 @@ uint16_t nat_port_select(FAR struct net_driver_s *dev, { /* We have looped back, failed. */ - return 0; + return -EADDRINUSE; } } - return id; + *external_port = id; + return OK; #else return nat_port_select_without_stack(domain, IP_PROTO_ICMP, - external_ip, local_port); + external_ip, local_port, + external_port); #endif } #endif @@ -332,14 +352,16 @@ uint16_t nat_port_select(FAR struct net_driver_s *dev, { /* We have looped back, failed. */ - return 0; + return -EADDRINUSE; } } - return id; + *external_port = id; + return OK; #else return nat_port_select_without_stack(domain, IP_PROTO_ICMP6, - external_ip, local_port); + external_ip, local_port, + external_port); #endif } #endif @@ -347,7 +369,8 @@ uint16_t nat_port_select(FAR struct net_driver_s *dev, /* Select original port for unsupported protocol. */ - return local_port; + *external_port = local_port; + return OK; } /**************************************************************************** diff --git a/net/nat/nat.h b/net/nat/nat.h index 7ed03b37151..f328347b635 100644 --- a/net/nat/nat.h +++ b/net/nat/nat.h @@ -256,21 +256,23 @@ bool nat_port_inuse(uint8_t domain, uint8_t protocol, * Select an available port number for TCP/UDP protocol, or id for ICMP. * * Input Parameters: - * dev - The device on which the packet will be sent. - * domain - The domain of the packet. - * protocol - The L4 protocol of the packet. - * external_ip - The external IP bind with the port. - * local_port - The local port of the packet, as reference. + * dev - The device on which the packet will be sent. + * domain - The domain of the packet. + * protocol - The L4 protocol of the packet. + * external_ip - The external IP bind with the port. + * local_port - The local port of the packet, as reference. + * external_port - The selected external port. * * Returned Value: - * External port number on success; 0 on failure + * 0 on success; a negated errno on failure. * ****************************************************************************/ -uint16_t nat_port_select(FAR struct net_driver_s *dev, - uint8_t domain, uint8_t protocol, - FAR const union ip_addr_u *external_ip, - uint16_t local_port); +int nat_port_select(FAR struct net_driver_s *dev, + uint8_t domain, uint8_t protocol, + FAR const union ip_addr_u *external_ip, + uint16_t local_port, + FAR uint16_t *external_port); /**************************************************************************** * Name: nat_expire_time