From a3a669a5f6bbd19b08d6a9e6183238a350040271 Mon Sep 17 00:00:00 2001 From: Zhe Weng Date: Thu, 3 Nov 2022 14:26:21 +0800 Subject: [PATCH] net/nat: Add UDP support Signed-off-by: Zhe Weng --- net/nat/Kconfig | 11 ++++ net/nat/ipv4_nat.c | 126 ++++++++++++++++++++++++++++++++++++++- net/nat/ipv4_nat_entry.c | 23 ++++++- net/udp/udp_conn.c | 17 +++++- 4 files changed, 171 insertions(+), 6 deletions(-) diff --git a/net/nat/Kconfig b/net/nat/Kconfig index baa961f775..9ca48a0382 100644 --- a/net/nat/Kconfig +++ b/net/nat/Kconfig @@ -20,6 +20,17 @@ config NET_NAT_TCP_EXPIRE_SEC Note: The default value 86400 is suggested by RFC2663, Section 2.6, Page 5. +config NET_NAT_UDP_EXPIRE_SEC + int "UDP NAT entry expiration seconds" + default 240 + depends on NET_NAT + ---help--- + The expiration time for idle UDP entry in NAT. + + Note: RFC2663 (Section 2.6, Page 5) suggests that non-TCP sessions + that have not been used for a couple of minutes can be assumed to be + terminated. + config NET_NAT_ICMP_EXPIRE_SEC int "ICMP NAT entry expiration seconds" default 60 diff --git a/net/nat/ipv4_nat.c b/net/nat/ipv4_nat.c index b24d60e73a..5cbdd73d21 100644 --- a/net/nat/ipv4_nat.c +++ b/net/nat/ipv4_nat.c @@ -31,6 +31,7 @@ #include #include +#include #include "nat/nat.h" #include "utils/utils.h" @@ -103,6 +104,65 @@ static int ipv4_nat_inbound_tcp(FAR struct ipv4_hdr_s *ipv4) } #endif +/**************************************************************************** + * Name: ipv4_nat_inbound_udp + * + * Description: + * Check if a received UDP 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 NAT device and is targeting at the address + * assigned to the device. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_UDP +static int ipv4_nat_inbound_udp(FAR struct ipv4_hdr_s *ipv4) +{ + uint16_t iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2; + FAR struct udp_hdr_s *udp = + (FAR struct udp_hdr_s *)((FAR uint8_t *)ipv4 + iphdrlen); + FAR struct ipv4_nat_entry *entry = + ipv4_nat_inbound_entry_find(IP_PROTO_UDP, udp->destport, true); + if (!entry) + { + /* Inbound without entry is OK (e.g. towards NuttX itself), skip NAT. */ + + return OK; + } + + /* Modify port and checksum. */ + + if (udp->udpchksum != 0) /* UDP checksum has special case 0 (no checksum) */ + { + chksum_adjust(udp->udpchksum, udp->destport, entry->local_port); + } + + udp->destport = entry->local_port; + + /* Modify address and checksum. */ + + if (udp->udpchksum != 0) /* UDP checksum has special case 0 (no checksum) */ + { + chksum_adjust(udp->udpchksum, ipv4->destipaddr, entry->local_ip); + } + + chksum_adjust(ipv4->ipchksum, ipv4->destipaddr, entry->local_ip); + net_ipv4addr_hdrcopy(ipv4->destipaddr, &entry->local_ip); + + return OK; +} +#endif + /**************************************************************************** * Name: ipv4_nat_inbound_icmp * @@ -213,6 +273,66 @@ static int ipv4_nat_outbound_tcp(FAR struct net_driver_s *dev, } #endif +/**************************************************************************** + * Name: ipv4_nat_outbound_udp + * + * Description: + * Check if we want to perform NAT with this outbound UDP 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_UDP +static int ipv4_nat_outbound_udp(FAR struct net_driver_s *dev, + FAR struct ipv4_hdr_s *ipv4) +{ + uint16_t iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2; + FAR struct udp_hdr_s *udp = + (FAR struct udp_hdr_s *)((FAR uint8_t *)ipv4 + iphdrlen); + FAR struct ipv4_nat_entry *entry = ipv4_nat_outbound_entry_find( + dev, IP_PROTO_UDP, net_ip4addr_conv32(ipv4->srcipaddr), udp->srcport); + if (!entry) + { + /* Outbound entry creation failed, should have corresponding entry. */ + + return -ENOMEM; + } + + /* Modify port and checksum. */ + + if (udp->udpchksum != 0) /* UDP checksum has special case 0 (no checksum) */ + { + chksum_adjust(udp->udpchksum, udp->srcport, entry->external_port); + } + + udp->srcport = entry->external_port; + + /* Modify address and checksum. */ + + if (udp->udpchksum != 0) /* UDP checksum has special case 0 (no checksum) */ + { + chksum_adjust(udp->udpchksum, ipv4->srcipaddr, dev->d_ipaddr); + } + + chksum_adjust(ipv4->ipchksum, ipv4->srcipaddr, dev->d_ipaddr); + net_ipv4addr_hdrcopy(ipv4->srcipaddr, &dev->d_ipaddr); + + return OK; +} +#endif + /**************************************************************************** * Name: ipv4_nat_outbound_icmp * @@ -379,7 +499,8 @@ int ipv4_nat_inbound(FAR struct net_driver_s *dev, #endif #ifdef CONFIG_NET_UDP -# warning Missing logic + case IP_PROTO_UDP: + return ipv4_nat_inbound_udp(ipv4); #endif #ifdef CONFIG_NET_ICMP @@ -430,7 +551,8 @@ int ipv4_nat_outbound(FAR struct net_driver_s *dev, #endif #ifdef CONFIG_NET_UDP -# warning Missing logic + case IP_PROTO_UDP: + return ipv4_nat_outbound_udp(dev, ipv4); #endif #ifdef CONFIG_NET_ICMP diff --git a/net/nat/ipv4_nat_entry.c b/net/nat/ipv4_nat_entry.c index 7869136e95..cbfc369fb7 100644 --- a/net/nat/ipv4_nat_entry.c +++ b/net/nat/ipv4_nat_entry.c @@ -33,6 +33,7 @@ #include "icmp/icmp.h" #include "nat/nat.h" #include "tcp/tcp.h" +#include "udp/udp.h" #if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4) @@ -145,7 +146,22 @@ static uint16_t ipv4_nat_select_port(FAR struct net_driver_s *dev, #endif #ifdef CONFIG_NET_UDP -# warning Missing logic + case IP_PROTO_UDP: + { +#ifndef CONFIG_NET_UDP_NO_STACK + union ip_binding_u u; + u.ipv4.laddr = dev->d_draddr; + u.ipv4.raddr = INADDR_ANY; + + /* TODO: Try keep origin port as possible. */ + + return HTONS(udp_select_port(PF_INET, &u)); +#else + return ipv4_nat_select_port_without_stack(IP_PROTO_UDP, + dev->d_draddr, + local_port); +#endif + } #endif #ifdef CONFIG_NET_ICMP @@ -212,7 +228,10 @@ static void ipv4_nat_entry_refresh(FAR struct ipv4_nat_entry *entry) #endif #ifdef CONFIG_NET_UDP -# warning Missing logic + case IP_PROTO_UDP: + entry->expire_time = TICK2SEC(clock_systime_ticks()) + + CONFIG_NET_NAT_UDP_EXPIRE_SEC; + break; #endif #ifdef CONFIG_NET_ICMP diff --git a/net/udp/udp_conn.c b/net/udp/udp_conn.c index 24f4085aaf..51c5bc86ee 100644 --- a/net/udp/udp_conn.c +++ b/net/udp/udp_conn.c @@ -64,6 +64,7 @@ #include #include "devif/devif.h" +#include "nat/nat.h" #include "netdev/netdev.h" #include "inet/inet.h" #include "udp/udp.h" @@ -533,7 +534,13 @@ uint16_t udp_select_port(uint8_t domain, FAR union ip_binding_u *u) g_last_udp_port = 4096; } } - while (udp_find_conn(domain, u, HTONS(g_last_udp_port)) != NULL); + while (udp_find_conn(domain, u, HTONS(g_last_udp_port)) != NULL +#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4) + || (domain == PF_INET && + ipv4_nat_port_inuse(IP_PROTO_UDP, u->ipv4.laddr, + HTONS(g_last_udp_port))) +#endif + ); /* Initialize and return the connection structure, bind it to the * port number @@ -816,7 +823,13 @@ int udp_bind(FAR struct udp_conn_s *conn, FAR const struct sockaddr *addr) * and port ? */ - if (udp_find_conn(conn->domain, &conn->u, portno) == NULL) + if (udp_find_conn(conn->domain, &conn->u, portno) == NULL +#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4) + && !(conn->domain == PF_INET && + ipv4_nat_port_inuse(IP_PROTO_UDP, conn->u.ipv4.laddr, + portno)) +#endif + ) { /* No.. then bind the socket to the port */