net/nat: Add ICMP ECHO (REQUEST & REPLY) support

Support ICMP ECHO REQUEST & REPLY. Id of ICMP is processed like port of TCP in NAT. However, our ICMP stack doesn't have a method to manage id allocation like tcp_selectport(), the id is set by apps (like icmp_ping.c) without conflict avoidance, so not adding such conflict avoidance logic to ICMP stack when implementing NAT.

Signed-off-by: Zhe Weng <wengzhe@xiaomi.com>
This commit is contained in:
Zhe Weng 2022-10-31 11:33:06 +08:00 committed by Xiang Xiao
parent f498102512
commit 8d401db5b9
3 changed files with 173 additions and 6 deletions

View File

@ -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.

View File

@ -29,6 +29,7 @@
#include <stdint.h>
#include <sys/types.h>
#include <nuttx/net/icmp.h>
#include <nuttx/net/tcp.h>
#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
}
}

View File

@ -30,11 +30,21 @@
#include <nuttx/kmalloc.h>
#include <nuttx/queue.h>
#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
}
}