net/nat: Support isolation between multiple WAN devices by saving external ip

Signed-off-by: Zhe Weng <wengzhe@xiaomi.com>
This commit is contained in:
Zhe Weng 2022-12-21 15:11:28 +08:00 committed by Xiang Xiao
parent 0ceee72239
commit 902a6dcad2
4 changed files with 51 additions and 53 deletions

View File

@ -77,11 +77,6 @@ Usage
Enable NAT function on a network device, on which the outbound packets
will be masqueraded.
Note that NAT is currently designed to be enabled on single device, it
may work when enabled on multiple devices, but external ports will not
be isolated between devices, so an external port used on one NAT device
will also be used by same local ip:port on another NAT device.
:return: Zero is returned if NAT function is successfully enabled on
the device; A negated errno value is returned if failed.

View File

@ -171,9 +171,12 @@ ipv4_nat_inbound_tcp(FAR struct ipv4_hdr_s *ipv4,
enum nat_manip_type_e manip_type)
{
FAR struct tcp_hdr_s *tcp = L4_HDR(ipv4);
FAR uint16_t *external_ip = MANIP_IPADDR(ipv4, manip_type);
FAR uint16_t *external_port = MANIP_PORT(tcp, manip_type);
FAR struct ipv4_nat_entry *entry =
ipv4_nat_inbound_entry_find(IP_PROTO_TCP, *external_port, true);
ipv4_nat_inbound_entry_find(IP_PROTO_TCP,
net_ip4addr_conv32(external_ip),
*external_port, true);
if (!entry)
{
return NULL;
@ -217,10 +220,13 @@ ipv4_nat_inbound_udp(FAR struct ipv4_hdr_s *ipv4,
enum nat_manip_type_e manip_type)
{
FAR struct udp_hdr_s *udp = L4_HDR(ipv4);
FAR uint16_t *external_ip = MANIP_IPADDR(ipv4, manip_type);
FAR uint16_t *external_port = MANIP_PORT(udp, manip_type);
FAR uint16_t *udpchksum;
FAR struct ipv4_nat_entry *entry =
ipv4_nat_inbound_entry_find(IP_PROTO_UDP, *external_port, true);
ipv4_nat_inbound_entry_find(IP_PROTO_UDP,
net_ip4addr_conv32(external_ip),
*external_port, true);
if (!entry)
{
@ -264,13 +270,17 @@ ipv4_nat_inbound_icmp(FAR struct ipv4_hdr_s *ipv4,
enum nat_manip_type_e manip_type)
{
FAR struct icmp_hdr_s *icmp = L4_HDR(ipv4);
FAR uint16_t *external_ip;
FAR struct ipv4_nat_entry *entry;
switch (icmp->type)
{
case ICMP_ECHO_REQUEST:
case ICMP_ECHO_REPLY:
entry = ipv4_nat_inbound_entry_find(IP_PROTO_ICMP, icmp->id, true);
external_ip = MANIP_IPADDR(ipv4, manip_type);
entry = ipv4_nat_inbound_entry_find(IP_PROTO_ICMP,
net_ip4addr_conv32(external_ip),
icmp->id, true);
if (!entry)
{
return NULL;
@ -391,7 +401,7 @@ ipv4_nat_outbound_tcp(FAR struct net_driver_s *dev,
*/
ipv4_nat_port_adjust(&tcp->tcpchksum, local_port, entry->external_port);
ipv4_nat_ip_adjust(ipv4, &tcp->tcpchksum, dev->d_ipaddr, manip_type);
ipv4_nat_ip_adjust(ipv4, &tcp->tcpchksum, entry->external_ip, manip_type);
return entry;
}
@ -444,7 +454,7 @@ ipv4_nat_outbound_udp(FAR struct net_driver_s *dev,
udpchksum = udp->udpchksum != 0 ? &udp->udpchksum : NULL;
ipv4_nat_port_adjust(udpchksum, local_port, entry->external_port);
ipv4_nat_ip_adjust(ipv4, udpchksum, dev->d_ipaddr, manip_type);
ipv4_nat_ip_adjust(ipv4, udpchksum, entry->external_ip, manip_type);
return entry;
}
@ -499,7 +509,7 @@ ipv4_nat_outbound_icmp(FAR struct net_driver_s *dev,
ipv4_nat_port_adjust(&icmp->icmpchksum,
&icmp->id, entry->external_port);
ipv4_nat_ip_adjust(ipv4, NULL, dev->d_ipaddr, manip_type);
ipv4_nat_ip_adjust(ipv4, NULL, entry->external_ip, manip_type);
return entry;
case ICMP_DEST_UNREACHABLE:
@ -547,7 +557,7 @@ ipv4_nat_outbound_icmp(FAR struct net_driver_s *dev,
/* Adjust outer IP */
ipv4_nat_ip_adjust(ipv4, NULL, dev->d_ipaddr, manip_type);
ipv4_nat_ip_adjust(ipv4, NULL, entry->external_ip, manip_type);
/* Recalculate ICMP checksum, we only need to re-calc data in L4
* header, because the inner IPv4 header's checksum is updated,
@ -673,16 +683,6 @@ ipv4_nat_outbound_internal(FAR struct net_driver_s *dev,
* Zero is returned if NAT function is successfully enabled on the device;
* A negated errno value is returned if failed.
*
* Assumptions:
* NAT will only be enabled on at most one device.
*
* Limitations:
* External ports are not isolated between devices yet, so if NAT is
* enabled on more than one device, an external port used on one device
* will also be used by same local ip:port on another device.
*
* TODO:
* Support multiple NAT devices with isolated external port mapping.
****************************************************************************/
int ipv4_nat_enable(FAR struct net_driver_s *dev)
@ -831,13 +831,7 @@ int ipv4_nat_outbound(FAR struct net_driver_s *dev,
bool ipv4_nat_port_inuse(uint8_t protocol, in_addr_t ip, uint16_t port)
{
FAR struct ipv4_nat_entry *entry =
ipv4_nat_inbound_entry_find(protocol, port, false);
/* Not checking ip is enough for single NAT device, may save external_ip in
* entry for multiple device support in future.
*/
UNUSED(ip);
ipv4_nat_inbound_entry_find(protocol, ip, port, false);
return entry != NULL;
}

View File

@ -68,10 +68,12 @@ static DECLARE_HASHTABLE(g_table_outbound, CONFIG_NET_NAT_HASH_BITS);
*
****************************************************************************/
static inline uint32_t ipv4_nat_inbound_key(uint16_t external_port,
static inline uint32_t ipv4_nat_inbound_key(in_addr_t external_ip,
uint16_t external_port,
uint8_t protocol)
{
return ((uint32_t)protocol << 16) | external_port;
return NTOHL(external_ip) ^ /* external ip may different in higher bits. */
((uint32_t)protocol << 16) ^ external_port;
}
/****************************************************************************
@ -289,6 +291,7 @@ static void ipv4_nat_entry_refresh(FAR struct ipv4_nat_entry *entry)
*
* Input Parameters:
* protocol - The L4 protocol of the packet.
* external_ip - The external ip of the packet.
* external_port - The external port of the packet.
* local_ip - The local ip of the packet.
* local_port - The local port of the packet.
@ -299,7 +302,8 @@ static void ipv4_nat_entry_refresh(FAR struct ipv4_nat_entry *entry)
****************************************************************************/
static FAR struct ipv4_nat_entry *
ipv4_nat_entry_create(uint8_t protocol, uint16_t external_port,
ipv4_nat_entry_create(uint8_t protocol,
in_addr_t external_ip, uint16_t external_port,
in_addr_t local_ip, uint16_t local_port)
{
FAR struct ipv4_nat_entry *entry =
@ -311,6 +315,7 @@ ipv4_nat_entry_create(uint8_t protocol, uint16_t external_port,
}
entry->protocol = protocol;
entry->external_ip = external_ip;
entry->external_port = external_port;
entry->local_ip = local_ip;
entry->local_port = local_port;
@ -318,7 +323,7 @@ ipv4_nat_entry_create(uint8_t protocol, uint16_t external_port,
ipv4_nat_entry_refresh(entry);
hashtable_add(g_table_inbound, &entry->hash_inbound,
ipv4_nat_inbound_key(external_port, protocol));
ipv4_nat_inbound_key(external_ip, external_port, protocol));
hashtable_add(g_table_outbound, &entry->hash_outbound,
ipv4_nat_outbound_key(local_ip, local_port, protocol));
@ -343,7 +348,8 @@ static void ipv4_nat_entry_delete(FAR struct ipv4_nat_entry *entry)
entry->external_port);
hashtable_delete(g_table_inbound, &entry->hash_inbound,
ipv4_nat_inbound_key(entry->external_port,
ipv4_nat_inbound_key(entry->external_ip,
entry->external_port,
entry->protocol));
hashtable_delete(g_table_outbound, &entry->hash_outbound,
ipv4_nat_outbound_key(entry->local_ip, entry->local_port,
@ -412,6 +418,7 @@ static void ipv4_nat_reclaim_entry(int32_t current_time)
*
* Input Parameters:
* protocol - The L4 protocol of the packet.
* external_ip - The external ip of the packet, supports INADDR_ANY.
* external_port - The external port of the packet.
* refresh - Whether to refresh the selected entry.
*
@ -421,11 +428,12 @@ static void ipv4_nat_reclaim_entry(int32_t current_time)
****************************************************************************/
FAR struct ipv4_nat_entry *
ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port,
bool refresh)
ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip,
uint16_t external_port, bool refresh)
{
FAR hash_node_t *p;
FAR hash_node_t *tmp;
bool skip_ip = net_ipv4addr_cmp(external_ip, INADDR_ANY);
int32_t current_time = TICK2SEC(clock_systime_ticks());
#if CONFIG_NET_NAT_ENTRY_RECLAIM_SEC > 0
@ -433,7 +441,7 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port,
#endif
hashtable_for_every_possible_safe(g_table_inbound, p, tmp,
ipv4_nat_inbound_key(external_port, protocol))
ipv4_nat_inbound_key(external_ip, external_port, protocol))
{
FAR struct ipv4_nat_entry *entry =
container_of(p, struct ipv4_nat_entry, hash_inbound);
@ -447,6 +455,7 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port,
}
if (entry->protocol == protocol &&
(skip_ip || net_ipv4addr_cmp(entry->external_ip, external_ip)) &&
entry->external_port == external_port)
{
if (refresh)
@ -458,8 +467,13 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port,
}
}
nwarn("WARNING: Failed to find IPv4 inbound NAT entry for "
"proto=%d, external_port=%d\n", protocol, external_port);
if (refresh) /* false = a test of whether entry exists, no need to warn */
{
nwarn("WARNING: Failed to find IPv4 inbound NAT entry for "
"proto=%d, external=%x:%d\n",
protocol, external_ip, external_port);
}
return NULL;
}
@ -510,6 +524,7 @@ ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
}
if (entry->protocol == protocol &&
net_ipv4addr_cmp(entry->external_ip, dev->d_ipaddr) &&
net_ipv4addr_cmp(entry->local_ip, local_ip) &&
entry->local_port == local_port)
{
@ -536,8 +551,8 @@ ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
return NULL;
}
return ipv4_nat_entry_create(protocol, external_port, local_ip,
local_port);
return ipv4_nat_entry_create(protocol, dev->d_ipaddr, external_port,
local_ip, local_port);
}
#endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */

View File

@ -54,11 +54,12 @@ struct ipv4_nat_entry
* local port> | | external port> peer port>
* |----------------|
*
* Full cone NAT on single WAN only need to save local ip:port and external
* port. For ICMP, save id in port field.
* Full cone NAT only need to save local ip:port and external ip:port.
* For ICMP, save id in port field.
*/
in_addr_t local_ip; /* IP address of the local (private) host. */
in_addr_t external_ip; /* External IP address. */
uint16_t local_port; /* Port of the local (private) host. */
uint16_t external_port; /* The external port of local (private) host. */
uint8_t protocol; /* L4 protocol (TCP, UDP etc). */
@ -93,14 +94,6 @@ enum nat_manip_type_e
* Zero is returned if NAT function is successfully enabled on the device;
* A negated errno value is returned if failed.
*
* Assumptions:
* NAT will only be enabled on at most one device.
*
* Limitations:
* External ports are not isolated between devices yet, so if NAT is
* enabled on more than one device, an external port used on one device
* will also be used by same local ip:port on another device.
*
****************************************************************************/
int ipv4_nat_enable(FAR struct net_driver_s *dev);
@ -191,6 +184,7 @@ bool ipv4_nat_port_inuse(uint8_t protocol, in_addr_t ip, uint16_t port);
*
* Input Parameters:
* protocol - The L4 protocol of the packet.
* external_ip - The external ip of the packet, supports INADDR_ANY.
* external_port - The external port of the packet.
* refresh - Whether to refresh the selected entry.
*
@ -200,8 +194,8 @@ bool ipv4_nat_port_inuse(uint8_t protocol, in_addr_t ip, uint16_t port);
****************************************************************************/
FAR struct ipv4_nat_entry *
ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port,
bool refresh);
ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip,
uint16_t external_port, bool refresh);
/****************************************************************************
* Name: ipv4_nat_outbound_entry_find