diff --git a/Documentation/components/net/nat.rst b/Documentation/components/net/nat.rst index 01215356b0f..773bb5bf1a9 100644 --- a/Documentation/components/net/nat.rst +++ b/Documentation/components/net/nat.rst @@ -2,7 +2,7 @@ Network Address Translation (NAT) ================================= -NuttX supports full cone NAT logic, which currently supports +NuttX supports full cone or symmetric NAT logic, which currently supports - TCP @@ -46,6 +46,12 @@ Configuration Options ``CONFIG_NET_NAT`` Enable or disable Network Address Translation (NAT) function. Depends on ``CONFIG_NET_IPFORWARD``. +``CONFIG_NET_NAT_FULL_CONE`` + Enable Full Cone NAT logic. Full Cone NAT is easier to traverse than + Symmetric NAT, and uses less resources than Symmetric NAT. +``CONFIG_NET_NAT_SYMMETRIC`` + Enable Symmetric NAT logic. Symmetric NAT will be safer than Full Cone NAT, + be more difficult to traverse, and has more entries which may lead to heavier load. ``CONFIG_NET_NAT_HASH_BITS`` The bits of the hashtable of NAT entries, hashtable has (1 << bits) buckets. ``CONFIG_NET_NAT_TCP_EXPIRE_SEC`` @@ -101,7 +107,8 @@ Validated on Ubuntu 22.04 x86_64 with NuttX SIM by following steps: # CONFIG_SIM_NET_BRIDGE is not set CONFIG_SIM_NETDEV_NUMBER=2 -2. Call ``ipv4_nat_enable`` on one dev on startup +2. Call ``ipv4_nat_enable`` on one dev on startup, or manually enable NAT + with ``iptables`` command (either may work). .. code-block:: c @@ -113,6 +120,10 @@ Validated on Ubuntu 22.04 x86_64 with NuttX SIM by following steps: ... } + .. code-block:: shell + + iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE + 3. Set IP Address for NuttX on startup .. code-block:: shell diff --git a/net/nat/Kconfig b/net/nat/Kconfig index 0adc09c5373..0ce9e71b1d1 100644 --- a/net/nat/Kconfig +++ b/net/nat/Kconfig @@ -15,6 +15,25 @@ config NET_NAT and NAT may need a continuous buffer of at least 68 Bytes (IPv4 20B + ICMP 8B + IPv4 20B + TCP 20B). +choice + prompt "NAT Type" + default NET_NAT_FULL_CONE + depends on NET_NAT + +config NET_NAT_FULL_CONE + bool "Full Cone NAT" + ---help--- + Full Cone NAT is easier to traverse than Symmetric NAT, and uses + less resources than Symmetric NAT. + +config NET_NAT_SYMMETRIC + bool "Symmetric NAT" + ---help--- + Symmetric NAT will be safer than Full Cone NAT, be more difficult + to traverse, and has more entries which may lead to heavier load. + +endchoice + config NET_NAT_HASH_BITS int "The bits of NAT entry hashtable" default 5 diff --git a/net/nat/ipv4_nat.c b/net/nat/ipv4_nat.c index b575a273c3d..a7d28c72fe1 100644 --- a/net/nat/ipv4_nat.c +++ b/net/nat/ipv4_nat.c @@ -58,6 +58,14 @@ #define MANIP_PORT(l4hdr,manip_type) \ ((manip_type) == NAT_MANIP_SRC ? &(l4hdr)->srcport : &(l4hdr)->destport) +/* Getting peer IP & Port (just other than MANIP) */ + +#define PEER_IPADDR(iphdr,manip_type) \ + ((manip_type) != NAT_MANIP_SRC ? (iphdr)->srcipaddr : (iphdr)->destipaddr) + +#define PEER_PORT(l4hdr,manip_type) \ + ((manip_type) != NAT_MANIP_SRC ? &(l4hdr)->srcport : &(l4hdr)->destport) + /* Getting L4 header from IPv4 header. */ #define L4_HDR(ipv4) \ @@ -204,10 +212,14 @@ ipv4_nat_inbound_tcp(FAR struct ipv4_hdr_s *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 *peer_ip = PEER_IPADDR(ipv4, manip_type); + FAR uint16_t *peer_port = PEER_PORT(tcp, manip_type); FAR struct ipv4_nat_entry *entry = ipv4_nat_inbound_entry_find(IP_PROTO_TCP, net_ip4addr_conv32(external_ip), - *external_port, true); + *external_port, + net_ip4addr_conv32(peer_ip), + *peer_port, true); if (!entry) { return NULL; @@ -253,11 +265,15 @@ ipv4_nat_inbound_udp(FAR struct ipv4_hdr_s *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 *peer_ip = PEER_IPADDR(ipv4, manip_type); + FAR uint16_t *peer_port = PEER_PORT(udp, manip_type); FAR uint16_t *udpchksum; FAR struct ipv4_nat_entry *entry = ipv4_nat_inbound_entry_find(IP_PROTO_UDP, net_ip4addr_conv32(external_ip), - *external_port, true); + *external_port, + net_ip4addr_conv32(peer_ip), + *peer_port, true); if (!entry) { @@ -302,6 +318,7 @@ ipv4_nat_inbound_icmp(FAR struct ipv4_hdr_s *ipv4, { FAR struct icmp_hdr_s *icmp = L4_HDR(ipv4); FAR uint16_t *external_ip; + FAR uint16_t *peer_ip; FAR struct ipv4_nat_entry *entry; switch (icmp->type) @@ -309,8 +326,11 @@ ipv4_nat_inbound_icmp(FAR struct ipv4_hdr_s *ipv4, case ICMP_ECHO_REQUEST: case ICMP_ECHO_REPLY: external_ip = MANIP_IPADDR(ipv4, manip_type); + peer_ip = PEER_IPADDR(ipv4, manip_type); entry = ipv4_nat_inbound_entry_find(IP_PROTO_ICMP, net_ip4addr_conv32(external_ip), + icmp->id, + net_ip4addr_conv32(peer_ip), icmp->id, true); if (!entry) { @@ -424,12 +444,15 @@ ipv4_nat_outbound_tcp(FAR struct net_driver_s *dev, FAR struct tcp_hdr_s *tcp = L4_HDR(ipv4); FAR uint16_t *local_ip = MANIP_IPADDR(ipv4, manip_type); FAR uint16_t *local_port = MANIP_PORT(tcp, manip_type); + FAR uint16_t *peer_ip = PEER_IPADDR(ipv4, manip_type); + FAR uint16_t *peer_port = PEER_PORT(tcp, manip_type); FAR struct ipv4_nat_entry *entry; /* Only create entry when it's the outermost packet (manip type is SRC). */ entry = ipv4_nat_outbound_entry_find(dev, IP_PROTO_TCP, net_ip4addr_conv32(local_ip), *local_port, + net_ip4addr_conv32(peer_ip), *peer_port, (manip_type == NAT_MANIP_SRC)); if (!entry) { @@ -477,6 +500,8 @@ ipv4_nat_outbound_udp(FAR struct net_driver_s *dev, FAR struct udp_hdr_s *udp = L4_HDR(ipv4); FAR uint16_t *local_ip = MANIP_IPADDR(ipv4, manip_type); FAR uint16_t *local_port = MANIP_PORT(udp, manip_type); + FAR uint16_t *peer_ip = PEER_IPADDR(ipv4, manip_type); + FAR uint16_t *peer_port = PEER_PORT(udp, manip_type); FAR uint16_t *udpchksum; FAR struct ipv4_nat_entry *entry; @@ -484,6 +509,7 @@ ipv4_nat_outbound_udp(FAR struct net_driver_s *dev, entry = ipv4_nat_outbound_entry_find(dev, IP_PROTO_UDP, net_ip4addr_conv32(local_ip), *local_port, + net_ip4addr_conv32(peer_ip), *peer_port, (manip_type == NAT_MANIP_SRC)); if (!entry) { @@ -529,6 +555,7 @@ ipv4_nat_outbound_icmp(FAR struct net_driver_s *dev, { FAR struct icmp_hdr_s *icmp = L4_HDR(ipv4); FAR uint16_t *local_ip = MANIP_IPADDR(ipv4, manip_type); + FAR uint16_t *peer_ip = PEER_IPADDR(ipv4, manip_type); FAR struct ipv4_nat_entry *entry; switch (icmp->type) @@ -542,6 +569,7 @@ ipv4_nat_outbound_icmp(FAR struct net_driver_s *dev, entry = ipv4_nat_outbound_entry_find(dev, IP_PROTO_ICMP, net_ip4addr_conv32(local_ip), icmp->id, + net_ip4addr_conv32(peer_ip), icmp->id, (manip_type == NAT_MANIP_SRC)); if (!entry) { @@ -894,7 +922,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, ip, port, false); + ipv4_nat_inbound_entry_find(protocol, ip, port, INADDR_ANY, 0, false); return entry != NULL; } diff --git a/net/nat/ipv4_nat_entry.c b/net/nat/ipv4_nat_entry.c index e32b3c73c92..29b6e1cde38 100644 --- a/net/nat/ipv4_nat_entry.c +++ b/net/nat/ipv4_nat_entry.c @@ -295,6 +295,8 @@ static void ipv4_nat_entry_refresh(FAR struct ipv4_nat_entry *entry) * external_port - The external port of the packet. * local_ip - The local ip of the packet. * local_port - The local port of the packet. + * peer_ip - The peer ip of the packet. + * peer_port - The peer port of the packet. * * Returned Value: * Pointer to entry on success; null on failure @@ -304,7 +306,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, 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, + in_addr_t peer_ip, uint16_t peer_port) { FAR struct ipv4_nat_entry *entry = kmm_malloc(sizeof(struct ipv4_nat_entry)); @@ -319,6 +322,10 @@ ipv4_nat_entry_create(uint8_t protocol, entry->external_port = external_port; entry->local_ip = local_ip; entry->local_port = local_port; +#ifdef CONFIG_NET_NAT_SYMMETRIC + entry->peer_ip = peer_ip; + entry->peer_port = peer_port; +#endif ipv4_nat_entry_refresh(entry); @@ -456,6 +463,8 @@ void ipv4_nat_entry_clear(FAR struct net_driver_s *dev) * 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. + * peer_ip - The peer ip of the packet. + * peer_port - The peer port of the packet. * refresh - Whether to refresh the selected entry. * * Returned Value: @@ -465,11 +474,15 @@ void ipv4_nat_entry_clear(FAR struct net_driver_s *dev) FAR struct ipv4_nat_entry * ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip, - uint16_t external_port, bool refresh) + uint16_t external_port, in_addr_t peer_ip, + uint16_t peer_port, bool refresh) { FAR hash_node_t *p; FAR hash_node_t *tmp; bool skip_ip = net_ipv4addr_cmp(external_ip, INADDR_ANY); +#ifdef CONFIG_NET_NAT_SYMMETRIC + bool skip_peer = net_ipv4addr_cmp(peer_ip, INADDR_ANY); +#endif int32_t current_time = TICK2SEC(clock_systime_ticks()); #if CONFIG_NET_NAT_ENTRY_RECLAIM_SEC > 0 @@ -492,7 +505,12 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip, if (entry->protocol == protocol && (skip_ip || net_ipv4addr_cmp(entry->external_ip, external_ip)) && - entry->external_port == external_port) + entry->external_port == external_port +#ifdef CONFIG_NET_NAT_SYMMETRIC + && (skip_peer || (net_ipv4addr_cmp(entry->peer_ip, peer_ip) && + entry->peer_port == peer_port)) +#endif + ) { if (refresh) { @@ -525,6 +543,8 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip, * protocol - The L4 protocol of the packet. * local_ip - The local ip of the packet. * local_port - The local port of the packet. + * peer_ip - The peer ip of the packet. + * peer_port - The peer port of the packet. * try_create - Try create the entry if no entry found. * * Returned Value: @@ -535,6 +555,7 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip, FAR struct ipv4_nat_entry * ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol, in_addr_t local_ip, uint16_t local_port, + in_addr_t peer_ip, uint16_t peer_port, bool try_create) { FAR hash_node_t *p; @@ -562,7 +583,12 @@ 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) + entry->local_port == local_port +#ifdef CONFIG_NET_NAT_SYMMETRIC + && net_ipv4addr_cmp(entry->peer_ip, peer_ip) && + entry->peer_port == peer_port +#endif + ) { ipv4_nat_entry_refresh(entry); return entry; @@ -588,7 +614,7 @@ ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol, } return ipv4_nat_entry_create(protocol, dev->d_ipaddr, external_port, - local_ip, local_port); + local_ip, local_port, peer_ip, peer_port); } #endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */ diff --git a/net/nat/nat.h b/net/nat/nat.h index 4c70ceeb95e..6273ecc1318 100644 --- a/net/nat/nat.h +++ b/net/nat/nat.h @@ -55,13 +55,20 @@ struct ipv4_nat_entry * |----------------| * * Full cone NAT only need to save local ip:port and external ip:port. + * Symmetric NAT need to save peer ip:port as well. * 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. */ +#ifdef CONFIG_NET_NAT_SYMMETRIC + in_addr_t peer_ip; /* Peer IP address. */ +#endif uint16_t local_port; /* Port of the local (private) host. */ uint16_t external_port; /* The external port of local (private) host. */ +#ifdef CONFIG_NET_NAT_SYMMETRIC + uint16_t peer_port; /* Peer port. */ +#endif uint8_t protocol; /* L4 protocol (TCP, UDP etc). */ int32_t expire_time; /* The expiration time of this entry. */ @@ -203,6 +210,8 @@ void ipv4_nat_entry_clear(FAR struct net_driver_s *dev); * 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. + * peer_ip - The peer ip of the packet. + * peer_port - The peer port of the packet. * refresh - Whether to refresh the selected entry. * * Returned Value: @@ -212,7 +221,8 @@ void ipv4_nat_entry_clear(FAR struct net_driver_s *dev); FAR struct ipv4_nat_entry * ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip, - uint16_t external_port, bool refresh); + uint16_t external_port, in_addr_t peer_ip, + uint16_t peer_port, bool refresh); /**************************************************************************** * Name: ipv4_nat_outbound_entry_find @@ -226,6 +236,8 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip, * protocol - The L4 protocol of the packet. * local_ip - The local ip of the packet. * local_port - The local port of the packet. + * peer_ip - The peer ip of the packet. + * peer_port - The peer port of the packet. * try_create - Try create the entry if no entry found. * * Returned Value: @@ -236,6 +248,7 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip, FAR struct ipv4_nat_entry * ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol, in_addr_t local_ip, uint16_t local_port, + in_addr_t peer_ip, uint16_t peer_port, bool try_create); #endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */