mirror of
https://github.com/apache/nuttx.git
synced 2026-06-02 09:38:37 +08:00
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:
@@ -77,11 +77,6 @@ Usage
|
|||||||
Enable NAT function on a network device, on which the outbound packets
|
Enable NAT function on a network device, on which the outbound packets
|
||||||
will be masqueraded.
|
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
|
:return: Zero is returned if NAT function is successfully enabled on
|
||||||
the device; A negated errno value is returned if failed.
|
the device; A negated errno value is returned if failed.
|
||||||
|
|
||||||
|
|||||||
+18
-24
@@ -171,9 +171,12 @@ ipv4_nat_inbound_tcp(FAR struct ipv4_hdr_s *ipv4,
|
|||||||
enum nat_manip_type_e manip_type)
|
enum nat_manip_type_e manip_type)
|
||||||
{
|
{
|
||||||
FAR struct tcp_hdr_s *tcp = L4_HDR(ipv4);
|
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 uint16_t *external_port = MANIP_PORT(tcp, manip_type);
|
||||||
FAR struct ipv4_nat_entry *entry =
|
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)
|
if (!entry)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -217,10 +220,13 @@ ipv4_nat_inbound_udp(FAR struct ipv4_hdr_s *ipv4,
|
|||||||
enum nat_manip_type_e manip_type)
|
enum nat_manip_type_e manip_type)
|
||||||
{
|
{
|
||||||
FAR struct udp_hdr_s *udp = L4_HDR(ipv4);
|
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 *external_port = MANIP_PORT(udp, manip_type);
|
||||||
FAR uint16_t *udpchksum;
|
FAR uint16_t *udpchksum;
|
||||||
FAR struct ipv4_nat_entry *entry =
|
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)
|
if (!entry)
|
||||||
{
|
{
|
||||||
@@ -264,13 +270,17 @@ ipv4_nat_inbound_icmp(FAR struct ipv4_hdr_s *ipv4,
|
|||||||
enum nat_manip_type_e manip_type)
|
enum nat_manip_type_e manip_type)
|
||||||
{
|
{
|
||||||
FAR struct icmp_hdr_s *icmp = L4_HDR(ipv4);
|
FAR struct icmp_hdr_s *icmp = L4_HDR(ipv4);
|
||||||
|
FAR uint16_t *external_ip;
|
||||||
FAR struct ipv4_nat_entry *entry;
|
FAR struct ipv4_nat_entry *entry;
|
||||||
|
|
||||||
switch (icmp->type)
|
switch (icmp->type)
|
||||||
{
|
{
|
||||||
case ICMP_ECHO_REQUEST:
|
case ICMP_ECHO_REQUEST:
|
||||||
case ICMP_ECHO_REPLY:
|
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)
|
if (!entry)
|
||||||
{
|
{
|
||||||
return NULL;
|
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_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;
|
return entry;
|
||||||
}
|
}
|
||||||
@@ -444,7 +454,7 @@ ipv4_nat_outbound_udp(FAR struct net_driver_s *dev,
|
|||||||
udpchksum = udp->udpchksum != 0 ? &udp->udpchksum : NULL;
|
udpchksum = udp->udpchksum != 0 ? &udp->udpchksum : NULL;
|
||||||
|
|
||||||
ipv4_nat_port_adjust(udpchksum, local_port, entry->external_port);
|
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;
|
return entry;
|
||||||
}
|
}
|
||||||
@@ -499,7 +509,7 @@ ipv4_nat_outbound_icmp(FAR struct net_driver_s *dev,
|
|||||||
|
|
||||||
ipv4_nat_port_adjust(&icmp->icmpchksum,
|
ipv4_nat_port_adjust(&icmp->icmpchksum,
|
||||||
&icmp->id, entry->external_port);
|
&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;
|
return entry;
|
||||||
|
|
||||||
case ICMP_DEST_UNREACHABLE:
|
case ICMP_DEST_UNREACHABLE:
|
||||||
@@ -547,7 +557,7 @@ ipv4_nat_outbound_icmp(FAR struct net_driver_s *dev,
|
|||||||
|
|
||||||
/* Adjust outer IP */
|
/* 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
|
/* Recalculate ICMP checksum, we only need to re-calc data in L4
|
||||||
* header, because the inner IPv4 header's checksum is updated,
|
* 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;
|
* Zero is returned if NAT function is successfully enabled on the device;
|
||||||
* A negated errno value is returned if failed.
|
* 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)
|
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)
|
bool ipv4_nat_port_inuse(uint8_t protocol, in_addr_t ip, uint16_t port)
|
||||||
{
|
{
|
||||||
FAR struct ipv4_nat_entry *entry =
|
FAR struct ipv4_nat_entry *entry =
|
||||||
ipv4_nat_inbound_entry_find(protocol, port, false);
|
ipv4_nat_inbound_entry_find(protocol, ip, 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);
|
|
||||||
|
|
||||||
return entry != NULL;
|
return entry != NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-12
@@ -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)
|
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:
|
* Input Parameters:
|
||||||
* protocol - The L4 protocol of the packet.
|
* protocol - The L4 protocol of the packet.
|
||||||
|
* external_ip - The external ip of the packet.
|
||||||
* external_port - The external port of the packet.
|
* external_port - The external port 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.
|
||||||
@@ -299,7 +302,8 @@ static void ipv4_nat_entry_refresh(FAR struct ipv4_nat_entry *entry)
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
static FAR struct ipv4_nat_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)
|
in_addr_t local_ip, uint16_t local_port)
|
||||||
{
|
{
|
||||||
FAR struct ipv4_nat_entry *entry =
|
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->protocol = protocol;
|
||||||
|
entry->external_ip = external_ip;
|
||||||
entry->external_port = external_port;
|
entry->external_port = external_port;
|
||||||
entry->local_ip = local_ip;
|
entry->local_ip = local_ip;
|
||||||
entry->local_port = local_port;
|
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);
|
ipv4_nat_entry_refresh(entry);
|
||||||
|
|
||||||
hashtable_add(g_table_inbound, &entry->hash_inbound,
|
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,
|
hashtable_add(g_table_outbound, &entry->hash_outbound,
|
||||||
ipv4_nat_outbound_key(local_ip, local_port, protocol));
|
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);
|
entry->external_port);
|
||||||
|
|
||||||
hashtable_delete(g_table_inbound, &entry->hash_inbound,
|
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));
|
entry->protocol));
|
||||||
hashtable_delete(g_table_outbound, &entry->hash_outbound,
|
hashtable_delete(g_table_outbound, &entry->hash_outbound,
|
||||||
ipv4_nat_outbound_key(entry->local_ip, entry->local_port,
|
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:
|
* Input Parameters:
|
||||||
* protocol - The L4 protocol of the packet.
|
* 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.
|
* external_port - The external port of the packet.
|
||||||
* refresh - Whether to refresh the selected entry.
|
* 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 *
|
FAR struct ipv4_nat_entry *
|
||||||
ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port,
|
ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip,
|
||||||
bool refresh)
|
uint16_t external_port, bool refresh)
|
||||||
{
|
{
|
||||||
FAR hash_node_t *p;
|
FAR hash_node_t *p;
|
||||||
FAR hash_node_t *tmp;
|
FAR hash_node_t *tmp;
|
||||||
|
bool skip_ip = net_ipv4addr_cmp(external_ip, INADDR_ANY);
|
||||||
int32_t current_time = TICK2SEC(clock_systime_ticks());
|
int32_t current_time = TICK2SEC(clock_systime_ticks());
|
||||||
|
|
||||||
#if CONFIG_NET_NAT_ENTRY_RECLAIM_SEC > 0
|
#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
|
#endif
|
||||||
|
|
||||||
hashtable_for_every_possible_safe(g_table_inbound, p, tmp,
|
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 =
|
FAR struct ipv4_nat_entry *entry =
|
||||||
container_of(p, struct ipv4_nat_entry, hash_inbound);
|
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 &&
|
if (entry->protocol == protocol &&
|
||||||
|
(skip_ip || net_ipv4addr_cmp(entry->external_ip, external_ip)) &&
|
||||||
entry->external_port == external_port)
|
entry->external_port == external_port)
|
||||||
{
|
{
|
||||||
if (refresh)
|
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 "
|
if (refresh) /* false = a test of whether entry exists, no need to warn */
|
||||||
"proto=%d, external_port=%d\n", protocol, external_port);
|
{
|
||||||
|
nwarn("WARNING: Failed to find IPv4 inbound NAT entry for "
|
||||||
|
"proto=%d, external=%x:%d\n",
|
||||||
|
protocol, external_ip, external_port);
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,6 +524,7 @@ ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (entry->protocol == protocol &&
|
if (entry->protocol == protocol &&
|
||||||
|
net_ipv4addr_cmp(entry->external_ip, dev->d_ipaddr) &&
|
||||||
net_ipv4addr_cmp(entry->local_ip, local_ip) &&
|
net_ipv4addr_cmp(entry->local_ip, local_ip) &&
|
||||||
entry->local_port == local_port)
|
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 NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ipv4_nat_entry_create(protocol, external_port, local_ip,
|
return ipv4_nat_entry_create(protocol, dev->d_ipaddr, external_port,
|
||||||
local_port);
|
local_ip, local_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */
|
#endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */
|
||||||
|
|||||||
+6
-12
@@ -54,11 +54,12 @@ struct ipv4_nat_entry
|
|||||||
* local port> | | external port> peer port>
|
* local port> | | external port> peer port>
|
||||||
* |----------------|
|
* |----------------|
|
||||||
*
|
*
|
||||||
* Full cone NAT on single WAN only need to save local ip:port and external
|
* Full cone NAT only need to save local ip:port and external ip:port.
|
||||||
* port. For ICMP, save id in port field.
|
* For ICMP, save id in port field.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
in_addr_t local_ip; /* IP address of the local (private) host. */
|
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 local_port; /* Port of the local (private) host. */
|
||||||
uint16_t external_port; /* The external port of local (private) host. */
|
uint16_t external_port; /* The external port of local (private) host. */
|
||||||
uint8_t protocol; /* L4 protocol (TCP, UDP etc). */
|
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;
|
* Zero is returned if NAT function is successfully enabled on the device;
|
||||||
* A negated errno value is returned if failed.
|
* 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);
|
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:
|
* Input Parameters:
|
||||||
* protocol - The L4 protocol of the packet.
|
* 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.
|
* external_port - The external port of the packet.
|
||||||
* refresh - Whether to refresh the selected entry.
|
* 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 *
|
FAR struct ipv4_nat_entry *
|
||||||
ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port,
|
ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip,
|
||||||
bool refresh);
|
uint16_t external_port, bool refresh);
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: ipv4_nat_outbound_entry_find
|
* Name: ipv4_nat_outbound_entry_find
|
||||||
|
|||||||
Reference in New Issue
Block a user