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
@ -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 *tcp =
|
||||||
(FAR struct tcp_hdr_s *)((FAR uint8_t *)ipv4 + iphdrlen);
|
(FAR struct tcp_hdr_s *)((FAR uint8_t *)ipv4 + iphdrlen);
|
||||||
FAR struct ipv4_nat_entry *entry = ipv4_nat_outbound_entry_find(
|
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)
|
if (!entry)
|
||||||
{
|
{
|
||||||
/* Outbound entry creation failed, should have corresponding entry. */
|
/* Outbound entry creation failed, should have corresponding entry. */
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include <nuttx/queue.h>
|
#include <nuttx/queue.h>
|
||||||
|
|
||||||
#include "nat/nat.h"
|
#include "nat/nat.h"
|
||||||
|
#include "tcp/tcp.h"
|
||||||
|
|
||||||
#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4)
|
#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4)
|
||||||
|
|
||||||
@ -44,6 +45,46 @@ static dq_queue_t g_entries;
|
|||||||
* Private Functions
|
* 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
|
* 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.
|
* Select an available port number for TCP/UDP protocol, or id for ICMP.
|
||||||
*
|
*
|
||||||
* Input Parameters:
|
* Input Parameters:
|
||||||
|
* dev - The device on which the packet will be sent.
|
||||||
* protocol - The L4 protocol of the packet.
|
* protocol - The L4 protocol of the packet.
|
||||||
* local_port - The local port of the packet, as reference.
|
* 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.
|
switch (protocol)
|
||||||
* TODO: Shall we let the chosen port same as local_port if possible?
|
{
|
||||||
*/
|
#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
|
# 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;
|
return local_port;
|
||||||
}
|
}
|
||||||
@ -237,6 +317,7 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port,
|
|||||||
* entry does not exist.
|
* entry does not exist.
|
||||||
*
|
*
|
||||||
* Input Parameters:
|
* Input Parameters:
|
||||||
|
* dev - The device on which the packet will be sent.
|
||||||
* protocol - The L4 protocol of the packet.
|
* protocol - The L4 protocol of the packet.
|
||||||
* local_ip - The local ip of the packet.
|
* local_ip - The local ip of the packet.
|
||||||
* local_port - The local port 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 *
|
FAR struct ipv4_nat_entry *
|
||||||
ipv4_nat_outbound_entry_find(uint8_t protocol, in_addr_t local_ip,
|
ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
|
||||||
uint16_t local_port)
|
in_addr_t local_ip, uint16_t local_port)
|
||||||
{
|
{
|
||||||
FAR sq_entry_t *p;
|
FAR sq_entry_t *p;
|
||||||
FAR sq_entry_t *tmp;
|
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",
|
"proto=%d, local=%x:%d, try creating one.\n",
|
||||||
protocol, local_ip, local_port);
|
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)
|
if (!external_port)
|
||||||
{
|
{
|
||||||
nwarn("WARNING: Failed to find an available port!\n");
|
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.
|
* entry does not exist.
|
||||||
*
|
*
|
||||||
* Input Parameters:
|
* Input Parameters:
|
||||||
|
* dev - The device on which the packet will be sent.
|
||||||
* protocol - The L4 protocol of the packet.
|
* protocol - The L4 protocol of the packet.
|
||||||
* local_ip - The local ip of the packet.
|
* local_ip - The local ip of the packet.
|
||||||
* local_port - The local port 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 *
|
FAR struct ipv4_nat_entry *
|
||||||
ipv4_nat_outbound_entry_find(uint8_t protocol, in_addr_t local_ip,
|
ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
|
||||||
uint16_t local_port);
|
in_addr_t local_ip, uint16_t local_port);
|
||||||
|
|
||||||
#endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */
|
#endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */
|
||||||
#endif /* __NET_NAT_NAT_H */
|
#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_conn_s *tcp_alloc_accept(FAR struct net_driver_s *dev,
|
||||||
FAR struct tcp_hdr_s *tcp);
|
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
|
* Name: tcp_bind
|
||||||
*
|
*
|
||||||
|
@ -165,117 +165,6 @@ static FAR struct tcp_conn_s *
|
|||||||
return NULL;
|
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
|
* Name: tcp_ipv4_active
|
||||||
*
|
*
|
||||||
@ -586,6 +475,117 @@ FAR struct tcp_conn_s *tcp_alloc_conn(void)
|
|||||||
* Public Functions
|
* 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
|
* Name: tcp_initialize
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user