You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2022/11/11 06:37:07 UTC
[incubator-nuttx] 06/07: net/nat: Add UDP support
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit a3a669a5f6bbd19b08d6a9e6183238a350040271
Author: Zhe Weng <we...@xiaomi.com>
AuthorDate: Thu Nov 3 14:26:21 2022 +0800
net/nat: Add UDP support
Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
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 <nuttx/net/icmp.h>
#include <nuttx/net/tcp.h>
+#include <nuttx/net/udp.h>
#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 <nuttx/net/udp.h>
#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 */