diff --git a/Documentation/reference/os/nat.rst b/Documentation/reference/os/nat.rst index 1a524ca536..01215356b0 100644 --- a/Documentation/reference/os/nat.rst +++ b/Documentation/reference/os/nat.rst @@ -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. diff --git a/net/nat/ipv4_nat.c b/net/nat/ipv4_nat.c index cc6f7b7fd7..93709b705f 100644 --- a/net/nat/ipv4_nat.c +++ b/net/nat/ipv4_nat.c @@ -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; } diff --git a/net/nat/ipv4_nat_entry.c b/net/nat/ipv4_nat_entry.c index 8ec7cf65e6..e9e8e5e484 100644 --- a/net/nat/ipv4_nat_entry.c +++ b/net/nat/ipv4_nat_entry.c @@ -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 */ diff --git a/net/nat/nat.h b/net/nat/nat.h index c8b2d5c128..ffedf5c1dc 100644 --- a/net/nat/nat.h +++ b/net/nat/nat.h @@ -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