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:
parent
0ceee72239
commit
902a6dcad2
@ -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.
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user