diff --git a/net/nat/Kconfig b/net/nat/Kconfig index 670a2f2d286..baa961f7753 100644 --- a/net/nat/Kconfig +++ b/net/nat/Kconfig @@ -19,3 +19,13 @@ config NET_NAT_TCP_EXPIRE_SEC Note: The default value 86400 is suggested by RFC2663, Section 2.6, Page 5. + +config NET_NAT_ICMP_EXPIRE_SEC + int "ICMP NAT entry expiration seconds" + default 60 + depends on NET_NAT + ---help--- + The expiration time for idle ICMP entry in NAT. + + Note: The default value 60 is suggested by RFC5508, Section 3.2, + Page 8. diff --git a/net/nat/ipv4_nat.c b/net/nat/ipv4_nat.c index 9e57644b610..b24d60e73a3 100644 --- a/net/nat/ipv4_nat.c +++ b/net/nat/ipv4_nat.c @@ -29,6 +29,7 @@ #include #include +#include #include #include "nat/nat.h" @@ -102,6 +103,64 @@ static int ipv4_nat_inbound_tcp(FAR struct ipv4_hdr_s *ipv4) } #endif +/**************************************************************************** + * Name: ipv4_nat_inbound_icmp + * + * Description: + * Check if a received ICMP packet belongs to a NAT entry. If so, translate + * it. + * + * Input Parameters: + * ipv4 - Points to the IPv4 header with dev->d_buf. + * + * Returned Value: + * Zero is returned if NAT is successfully applied, or is not enabled for + * this packet; + * A negated errno value is returned if error occured. + * + * Assumptions: + * Packet is received on g_dev and is targeting at the address assigned to + * g_dev. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMP +static int ipv4_nat_inbound_icmp(FAR struct ipv4_hdr_s *ipv4) +{ + uint16_t iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2; + FAR struct icmp_hdr_s *icmp = + (FAR struct icmp_hdr_s *)((FAR uint8_t *)ipv4 + iphdrlen); + FAR struct ipv4_nat_entry *entry; + + switch (icmp->type) + { + /* TODO: Support other ICMP types. */ + + case ICMP_ECHO_REQUEST: + case ICMP_ECHO_REPLY: + entry = ipv4_nat_inbound_entry_find(IP_PROTO_ICMP, icmp->id, true); + if (!entry) + { + /* Inbound without entry is OK, skip NAT. */ + + return OK; + } + + /* Modify id and checksum. */ + + chksum_adjust(icmp->icmpchksum, icmp->id, entry->local_port); + icmp->id = entry->local_port; + + /* Modify address and checksum. */ + + chksum_adjust(ipv4->ipchksum, ipv4->destipaddr, entry->local_ip); + net_ipv4addr_hdrcopy(ipv4->destipaddr, &entry->local_ip); + } + + return OK; +} +#endif + /**************************************************************************** * Name: ipv4_nat_outbound_tcp * @@ -154,6 +213,67 @@ static int ipv4_nat_outbound_tcp(FAR struct net_driver_s *dev, } #endif +/**************************************************************************** + * Name: ipv4_nat_outbound_icmp + * + * Description: + * Check if we want to perform NAT with this outbound ICMP packet before + * sending it. If so, translate it. + * + * Input Parameters: + * dev - The device to sent the packet. + * ipv4 - Points to the IPv4 header to be filled into dev->d_buf later. + * + * Returned Value: + * Zero is returned if NAT is successfully applied, or is not enabled for + * this packet; + * A negated errno value is returned if error occured. + * + * Assumptions: + * Packet will be sent on NAT device. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMP +static int ipv4_nat_outbound_icmp(FAR struct net_driver_s *dev, + FAR struct ipv4_hdr_s *ipv4) +{ + uint16_t iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2; + FAR struct icmp_hdr_s *icmp = + (FAR struct icmp_hdr_s *)((FAR uint8_t *)ipv4 + iphdrlen); + FAR struct ipv4_nat_entry *entry; + + switch (icmp->type) + { + /* TODO: Support other ICMP types. */ + + case ICMP_ECHO_REQUEST: + case ICMP_ECHO_REPLY: + entry = ipv4_nat_outbound_entry_find( + dev, IP_PROTO_ICMP, net_ip4addr_conv32(ipv4->srcipaddr), + icmp->id); + if (!entry) + { + /* Outbound entry creation failed. */ + + return -ENOMEM; + } + + /* Modify id and checksum. */ + + chksum_adjust(icmp->icmpchksum, icmp->id, entry->external_port); + icmp->id = entry->external_port; + + /* Modify address and checksum. */ + + chksum_adjust(ipv4->ipchksum, ipv4->srcipaddr, dev->d_ipaddr); + net_ipv4addr_hdrcopy(ipv4->srcipaddr, &dev->d_ipaddr); + } + + return OK; +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -263,7 +383,8 @@ int ipv4_nat_inbound(FAR struct net_driver_s *dev, #endif #ifdef CONFIG_NET_ICMP -# warning Missing logic + case IP_PROTO_ICMP: + return ipv4_nat_inbound_icmp(ipv4); #endif } } @@ -313,7 +434,8 @@ int ipv4_nat_outbound(FAR struct net_driver_s *dev, #endif #ifdef CONFIG_NET_ICMP -# warning Missing logic + case IP_PROTO_ICMP: + return ipv4_nat_outbound_icmp(dev, ipv4); #endif } } diff --git a/net/nat/ipv4_nat_entry.c b/net/nat/ipv4_nat_entry.c index b7f22d846e1..7869136e959 100644 --- a/net/nat/ipv4_nat_entry.c +++ b/net/nat/ipv4_nat_entry.c @@ -30,11 +30,21 @@ #include #include +#include "icmp/icmp.h" #include "nat/nat.h" #include "tcp/tcp.h" #if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4) +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* TODO: Why we limit to 32000 in net stack? */ + +#define NAT_PORT_REASSIGN_MAX 32000 +#define NAT_PORT_REASSIGN_MIN 4096 + /**************************************************************************** * Private Data ****************************************************************************/ @@ -72,9 +82,9 @@ static uint16_t ipv4_nat_select_port_without_stack( uint16_t hport = NTOHS(portno); while (ipv4_nat_port_inuse(protocol, ip, portno)) { - if (++hport >= 32000) /* TODO: Why we limit to 32000 in net stack? */ + if (++hport >= NAT_PORT_REASSIGN_MAX) { - hport = 4096; + hport = NAT_PORT_REASSIGN_MIN; } portno = HTONS(hport); @@ -139,7 +149,29 @@ static uint16_t ipv4_nat_select_port(FAR struct net_driver_s *dev, #endif #ifdef CONFIG_NET_ICMP -# warning Missing logic + case IP_PROTO_ICMP: + { +#ifdef CONFIG_NET_ICMP_SOCKET + uint16_t id = local_port; + uint16_t hid = NTOHS(id); + while (icmp_findconn(dev, id) || + ipv4_nat_port_inuse(IP_PROTO_ICMP, dev->d_draddr, id)) + { + if (++hid >= NAT_PORT_REASSIGN_MAX) + { + hid = NAT_PORT_REASSIGN_MIN; + } + + id = HTONS(hid); + } + + return id; +#else + return ipv4_nat_select_port_without_stack(IP_PROTO_ICMP, + dev->d_draddr, + local_port); +#endif + } #endif } @@ -184,7 +216,10 @@ static void ipv4_nat_entry_refresh(FAR struct ipv4_nat_entry *entry) #endif #ifdef CONFIG_NET_ICMP -# warning Missing logic + case IP_PROTO_ICMP: + entry->expire_time = TICK2SEC(clock_systime_ticks()) + + CONFIG_NET_NAT_ICMP_EXPIRE_SEC; + break; #endif } }