From f49810251266565c995ff173b6bd0189680b8ded Mon Sep 17 00:00:00 2001 From: Zhe Weng Date: Fri, 28 Oct 2022 16:08:36 +0800 Subject: [PATCH] net: select NAT external port by tcp_selectport for TCP Signed-off-by: Zhe Weng --- net/nat/ipv4_nat.c | 2 +- net/nat/ipv4_nat_entry.c | 97 +++++++++++++++-- net/nat/nat.h | 5 +- net/tcp/tcp.h | 21 ++++ net/tcp/tcp_conn.c | 222 +++++++++++++++++++-------------------- 5 files changed, 225 insertions(+), 122 deletions(-) diff --git a/net/nat/ipv4_nat.c b/net/nat/ipv4_nat.c index 12a4831585..9e57644b61 100644 --- a/net/nat/ipv4_nat.c +++ b/net/nat/ipv4_nat.c @@ -131,7 +131,7 @@ static int ipv4_nat_outbound_tcp(FAR struct net_driver_s *dev, FAR struct tcp_hdr_s *tcp = (FAR struct tcp_hdr_s *)((FAR uint8_t *)ipv4 + iphdrlen); FAR struct ipv4_nat_entry *entry = ipv4_nat_outbound_entry_find( - IP_PROTO_TCP, net_ip4addr_conv32(ipv4->srcipaddr), tcp->srcport); + dev, IP_PROTO_TCP, net_ip4addr_conv32(ipv4->srcipaddr), tcp->srcport); if (!entry) { /* Outbound entry creation failed, should have corresponding entry. */ diff --git a/net/nat/ipv4_nat_entry.c b/net/nat/ipv4_nat_entry.c index a5e4681111..b7f22d846e 100644 --- a/net/nat/ipv4_nat_entry.c +++ b/net/nat/ipv4_nat_entry.c @@ -31,6 +31,7 @@ #include #include "nat/nat.h" +#include "tcp/tcp.h" #if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4) @@ -44,6 +45,46 @@ static dq_queue_t g_entries; * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: ipv4_nat_select_port_without_stack + * + * Description: + * Select an available port number for TCP/UDP protocol, or id for ICMP. + * Used when corresponding stack is disabled. + * + * Input Parameters: + * protocol - The L4 protocol of the packet. + * ip - The IP bind with the port (in network byte order). + * portno - The local port (in network byte order), as reference. + * + * Returned Value: + * port number on success; 0 on failure + * + ****************************************************************************/ + +#if (defined(CONFIG_NET_TCP) && defined(CONFIG_NET_TCP_NO_STACK)) || \ + (defined(CONFIG_NET_UDP) && defined(CONFIG_NET_UDP_NO_STACK)) || \ + (defined(CONFIG_NET_ICMP) && !defined(CONFIG_NET_ICMP_SOCKET)) + +static uint16_t ipv4_nat_select_port_without_stack( + uint8_t protocol, in_addr_t ip, uint16_t portno) +{ + uint16_t hport = NTOHS(portno); + while (ipv4_nat_port_inuse(protocol, ip, portno)) + { + if (++hport >= 32000) /* TODO: Why we limit to 32000 in net stack? */ + { + hport = 4096; + } + + portno = HTONS(hport); + } + + return portno; +} + +#endif + /**************************************************************************** * Name: ipv4_nat_select_port * @@ -51,6 +92,7 @@ static dq_queue_t g_entries; * 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. * protocol - The L4 protocol of the packet. * local_port - The local port of the packet, as reference. * @@ -59,13 +101,51 @@ static dq_queue_t g_entries; * ****************************************************************************/ -static uint16_t ipv4_nat_select_port(uint8_t protocol, uint16_t local_port) +static uint16_t ipv4_nat_select_port(FAR struct net_driver_s *dev, + uint8_t protocol, + uint16_t local_port) { - /* TODO: Implement this, need to consider local ports and nat ports. - * TODO: Shall we let the chosen port same as local_port if possible? - */ + switch (protocol) + { +#ifdef CONFIG_NET_TCP + case IP_PROTO_TCP: + { +#ifndef CONFIG_NET_TCP_NO_STACK + /* Try to select local_port first. */ -# warning Missing logic + int ret = tcp_selectport(PF_INET, + (FAR const union ip_addr_u *)&dev->d_draddr, + local_port); + + /* If failed, try select another unused port. */ + + if (ret < 0) + { + ret = tcp_selectport(PF_INET, + (FAR const union ip_addr_u *)&dev->d_draddr, 0); + } + + return ret > 0 ? ret : 0; +#else + return ipv4_nat_select_port_without_stack(IP_PROTO_TCP, + dev->d_draddr, + local_port); +#endif + } +#endif + +#ifdef CONFIG_NET_UDP +# warning Missing logic +#endif + +#ifdef CONFIG_NET_ICMP +# warning Missing logic +#endif + } + + /* TODO: Currently select original port for unsupported protocol, maybe + * return zero to indicate failure. + */ return local_port; } @@ -237,6 +317,7 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port, * entry does not exist. * * Input Parameters: + * dev - The device on which the packet will be sent. * protocol - The L4 protocol of the packet. * local_ip - The local ip of the packet. * local_port - The local port of the packet. @@ -247,8 +328,8 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port, ****************************************************************************/ FAR struct ipv4_nat_entry * -ipv4_nat_outbound_entry_find(uint8_t protocol, in_addr_t local_ip, - uint16_t local_port) +ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol, + in_addr_t local_ip, uint16_t local_port) { FAR sq_entry_t *p; FAR sq_entry_t *tmp; @@ -283,7 +364,7 @@ ipv4_nat_outbound_entry_find(uint8_t protocol, in_addr_t local_ip, "proto=%d, local=%x:%d, try creating one.\n", protocol, local_ip, local_port); - uint16_t external_port = ipv4_nat_select_port(protocol, local_port); + uint16_t external_port = ipv4_nat_select_port(dev, protocol, local_port); if (!external_port) { nwarn("WARNING: Failed to find an available port!\n"); diff --git a/net/nat/nat.h b/net/nat/nat.h index a68d0af4db..70e2887a2b 100644 --- a/net/nat/nat.h +++ b/net/nat/nat.h @@ -204,6 +204,7 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port, * entry does not exist. * * Input Parameters: + * dev - The device on which the packet will be sent. * protocol - The L4 protocol of the packet. * local_ip - The local ip of the packet. * local_port - The local port of the packet. @@ -214,8 +215,8 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port, ****************************************************************************/ FAR struct ipv4_nat_entry * -ipv4_nat_outbound_entry_find(uint8_t protocol, in_addr_t local_ip, - uint16_t local_port); +ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol, + in_addr_t local_ip, uint16_t local_port); #endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */ #endif /* __NET_NAT_NAT_H */ diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h index 891c8fa856..a7af8d3cec 100644 --- a/net/tcp/tcp.h +++ b/net/tcp/tcp.h @@ -552,6 +552,27 @@ int tcp_remote_ipv6_device(FAR struct tcp_conn_s *conn); FAR struct tcp_conn_s *tcp_alloc_accept(FAR struct net_driver_s *dev, FAR struct tcp_hdr_s *tcp); +/**************************************************************************** + * Name: tcp_selectport + * + * Description: + * If the port number is zero; select an unused port for the connection. + * If the port number is non-zero, verify that no other connection has + * been created with this port number. + * + * Returned Value: + * Selected or verified port number in network order on success, a negated + * errno on failure. + * + * Assumptions: + * Interrupts are disabled + * + ****************************************************************************/ + +int tcp_selectport(uint8_t domain, + FAR const union ip_addr_u *ipaddr, + uint16_t portno); + /**************************************************************************** * Name: tcp_bind * diff --git a/net/tcp/tcp_conn.c b/net/tcp/tcp_conn.c index a4719cec59..ab42955afe 100644 --- a/net/tcp/tcp_conn.c +++ b/net/tcp/tcp_conn.c @@ -165,117 +165,6 @@ static FAR struct tcp_conn_s * return NULL; } -/**************************************************************************** - * Name: tcp_selectport - * - * Description: - * If the port number is zero; select an unused port for the connection. - * If the port number is non-zero, verify that no other connection has - * been created with this port number. - * - * Input Parameters: - * portno -- the selected port number in network order. Zero means no port - * selected. - * - * Returned Value: - * Selected or verified port number in network order on success, a negated - * errno on failure: - * - * EADDRINUSE - * The given address is already in use. - * EADDRNOTAVAIL - * Cannot assign requested address (unlikely) - * - * Assumptions: - * Interrupts are disabled - * - ****************************************************************************/ - -static int tcp_selectport(uint8_t domain, - FAR const union ip_addr_u *ipaddr, - uint16_t portno) -{ - static uint16_t g_last_tcp_port; - ssize_t ret; - - /* Generate port base dynamically */ - - if (g_last_tcp_port == 0) - { - ret = getrandom(&g_last_tcp_port, sizeof(uint16_t), 0); - if (ret < 0) - { - ret = getrandom(&g_last_tcp_port, sizeof(uint16_t), GRND_RANDOM); - } - - if (ret != sizeof(uint16_t)) - { - g_last_tcp_port = clock_systime_ticks() % 32000; - } - else - { - g_last_tcp_port = g_last_tcp_port % 32000; - } - - if (g_last_tcp_port < 4096) - { - g_last_tcp_port += 4096; - } - } - - if (portno == 0) - { - /* No local port assigned. Loop until we find a valid listen port - * number that is not being used by any other connection. NOTE the - * following loop is assumed to terminate but could not if all - * 32000-4096+1 ports are in used (unlikely). - */ - - do - { - /* Guess that the next available port number will be the one after - * the last port number assigned. Make sure that the port number - * is within range. - */ - - if (++g_last_tcp_port >= 32000) - { - g_last_tcp_port = 4096; - } - - portno = HTONS(g_last_tcp_port); - } - while (tcp_listener(domain, ipaddr, portno) -#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4) - || (domain == PF_INET && - ipv4_nat_port_inuse(IP_PROTO_TCP, ipaddr->ipv4, portno)) -#endif - ); - } - else - { - /* A port number has been supplied. Verify that no other TCP/IP - * connection is using this local port. - */ - - if (tcp_listener(domain, ipaddr, portno) -#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4) - || (domain == PF_INET && - ipv4_nat_port_inuse(IP_PROTO_TCP, ipaddr->ipv4, portno)) -#endif - ) - { - /* It is in use... return EADDRINUSE */ - - return -EADDRINUSE; - } - } - - /* Return the selected or verified port number (host byte order) */ - - return portno; -} - /**************************************************************************** * Name: tcp_ipv4_active * @@ -586,6 +475,117 @@ FAR struct tcp_conn_s *tcp_alloc_conn(void) * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: tcp_selectport + * + * Description: + * If the port number is zero; select an unused port for the connection. + * If the port number is non-zero, verify that no other connection has + * been created with this port number. + * + * Input Parameters: + * portno -- the selected port number in network order. Zero means no port + * selected. + * + * Returned Value: + * Selected or verified port number in network order on success, a negated + * errno on failure: + * + * EADDRINUSE + * The given address is already in use. + * EADDRNOTAVAIL + * Cannot assign requested address (unlikely) + * + * Assumptions: + * Interrupts are disabled + * + ****************************************************************************/ + +int tcp_selectport(uint8_t domain, + FAR const union ip_addr_u *ipaddr, + uint16_t portno) +{ + static uint16_t g_last_tcp_port; + ssize_t ret; + + /* Generate port base dynamically */ + + if (g_last_tcp_port == 0) + { + ret = getrandom(&g_last_tcp_port, sizeof(uint16_t), 0); + if (ret < 0) + { + ret = getrandom(&g_last_tcp_port, sizeof(uint16_t), GRND_RANDOM); + } + + if (ret != sizeof(uint16_t)) + { + g_last_tcp_port = clock_systime_ticks() % 32000; + } + else + { + g_last_tcp_port = g_last_tcp_port % 32000; + } + + if (g_last_tcp_port < 4096) + { + g_last_tcp_port += 4096; + } + } + + if (portno == 0) + { + /* No local port assigned. Loop until we find a valid listen port + * number that is not being used by any other connection. NOTE the + * following loop is assumed to terminate but could not if all + * 32000-4096+1 ports are in used (unlikely). + */ + + do + { + /* Guess that the next available port number will be the one after + * the last port number assigned. Make sure that the port number + * is within range. + */ + + if (++g_last_tcp_port >= 32000) + { + g_last_tcp_port = 4096; + } + + portno = HTONS(g_last_tcp_port); + } + while (tcp_listener(domain, ipaddr, portno) +#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4) + || (domain == PF_INET && + ipv4_nat_port_inuse(IP_PROTO_TCP, ipaddr->ipv4, portno)) +#endif + ); + } + else + { + /* A port number has been supplied. Verify that no other TCP/IP + * connection is using this local port. + */ + + if (tcp_listener(domain, ipaddr, portno) +#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4) + || (domain == PF_INET && + ipv4_nat_port_inuse(IP_PROTO_TCP, ipaddr->ipv4, portno)) +#endif + ) + { + /* It is in use... return EADDRINUSE */ + + return -EADDRINUSE; + } + } + + /* Return the selected or verified port number (host byte order) */ + + return portno; +} + /**************************************************************************** * Name: tcp_initialize *