You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2022/11/11 06:37:05 UTC
[incubator-nuttx] 04/07: net: select NAT external port by tcp_selectport for TCP
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit f49810251266565c995ff173b6bd0189680b8ded
Author: Zhe Weng <we...@xiaomi.com>
AuthorDate: Fri Oct 28 16:08:36 2022 +0800
net: select NAT external port by tcp_selectport for TCP
Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
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 <nuttx/queue.h>
#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. */
+
+ 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
-# warning Missing logic
+#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
*