mirror of
https://github.com/apache/nuttx.git
synced 2025-01-13 09:49:21 +08:00
net: select NAT external port by tcp_selectport for TCP
Signed-off-by: Zhe Weng <wengzhe@xiaomi.com>
This commit is contained in:
parent
8239ddeef4
commit
f498102512
5 changed files with 225 additions and 122 deletions
|
@ -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. */
|
||||
|
|
|
@ -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. */
|
||||
|
||||
# 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");
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue