net/nat: Support IPv6 Masquerading (NAT66)

Notes:
1. This version of NAT66 is a stateful one like NAT44, corresponding to Linux's MASQUERADE target of ip6tables.  We can support stateless NAT66 & NPTv6 later by slightly modify the address & port selection logic (maybe just match the rules and skip the entry find).
2. We're using same flag `IFF_NAT` for both NAT44 & NAT66 to make control easier.  Which means, if we enable NAT, both NAT44 & NAT66 will be enabled.  If we don't want one of them, we can just disable that one in Kconfig.
3. Maybe we can accelerate the checksum adjustment by pre-calculate a difference of checksum, and apply it to each packet, instead of calling `net_chksum_adjust` each time.  Just a thought, maybe do it later.
4. IP fragment segments on NAT66 connections are not supported yet.

Signed-off-by: Zhe Weng <wengzhe@xiaomi.com>
This commit is contained in:
Zhe Weng
2024-03-13 17:04:55 +08:00
committed by Xiang Xiao
parent 676826cb7c
commit f3b34c84c2
14 changed files with 1439 additions and 97 deletions
+5 -1
View File
@@ -24,10 +24,14 @@ if(CONFIG_NET_NAT)
list(APPEND SRCS nat.c)
if(CONFIG_NET_IPv4)
if(CONFIG_NET_NAT44)
list(APPEND SRCS ipv4_nat.c ipv4_nat_entry.c)
endif()
if(CONFIG_NET_NAT66)
list(APPEND SRCS ipv6_nat.c ipv6_nat_entry.c)
endif()
target_sources(net PRIVATE ${SRCS})
endif()
+45 -7
View File
@@ -6,27 +6,58 @@
config NET_NAT
bool "Network Address Translation (NAT)"
default n
depends on NET_IPFORWARD && IOB_BUFSIZE >= 68
depends on NET_IPFORWARD
---help---
Enable or disable Network Address Translation (NAT) function.
Note: When forwarding IPv4 packet and applying NAT, NAT may be
applied directly on a single I/O buffer containing L3 packet header,
and NAT may need a continuous buffer of at least 68 Bytes
(IPv4 20B + ICMP 8B + IPv4 20B + TCP 20B).
(IPv4 20B + ICMP 8B + IPv4 20B + TCP 20B). And 108 Bytes for IPv6.
config NET_NAT44
bool "IPv4-to-IPv4 NAT (NAT44)"
default y
depends on NET_IPv4 && NET_NAT
depends on IOB_BUFSIZE >= 68
choice
prompt "NAT Type"
default NET_NAT_FULL_CONE
depends on NET_NAT
prompt "NAT44 Type"
default NET_NAT44_FULL_CONE
depends on NET_NAT44
config NET_NAT_FULL_CONE
config NET_NAT44_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
config NET_NAT44_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_NAT66
bool "IPv6-to-IPv6 NAT (NAT66)"
default y
depends on NET_IPv6 && NET_NAT
depends on IOB_BUFSIZE >= 108
choice
prompt "NAT66 Type"
default NET_NAT66_FULL_CONE
depends on NET_NAT66
config NET_NAT66_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_NAT66_SYMMETRIC
bool "Symmetric NAT"
---help---
Symmetric NAT will be safer than Full Cone NAT, be more difficult
@@ -73,6 +104,13 @@ config NET_NAT_ICMP_EXPIRE_SEC
Note: The default value 60 is suggested by RFC5508, Section 3.2,
Page 8.
config NET_NAT_ICMPv6_EXPIRE_SEC
int "ICMPv6 NAT entry expiration seconds"
default 60
depends on NET_NAT
---help---
The expiration time for idle ICMPv6 entry in NAT.
config NET_NAT_ENTRY_RECLAIM_SEC
int "The time to auto reclaim all expired entries"
default 3600
+5 -1
View File
@@ -24,10 +24,14 @@ ifeq ($(CONFIG_NET_NAT),y)
NET_CSRCS += nat.c
ifeq ($(CONFIG_NET_IPv4),y)
ifeq ($(CONFIG_NET_NAT44),y)
NET_CSRCS += ipv4_nat.c ipv4_nat_entry.c
endif
ifeq ($(CONFIG_NET_NAT66),y)
NET_CSRCS += ipv6_nat.c ipv6_nat_entry.c
endif
# Include NAT build support
DEPPATH += --dep-path nat
+6 -13
View File
@@ -37,7 +37,7 @@
#include "nat/nat.h"
#include "utils/utils.h"
#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4)
#ifdef CONFIG_NET_NAT44
/****************************************************************************
* Pre-processor Definitions
@@ -732,15 +732,10 @@ ipv4_nat_outbound_internal(FAR struct net_driver_s *dev,
* dev - The device on which the packet is received.
* 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.
*
****************************************************************************/
int ipv4_nat_inbound(FAR struct net_driver_s *dev,
FAR struct ipv4_hdr_s *ipv4)
void ipv4_nat_inbound(FAR struct net_driver_s *dev,
FAR struct ipv4_hdr_s *ipv4)
{
/* We only process packets from NAT device and targeting at the address
* assigned to the device.
@@ -755,11 +750,9 @@ int ipv4_nat_inbound(FAR struct net_driver_s *dev,
{
/* Inbound without entry is OK (e.g. towards NuttX itself), skip. */
return OK;
return;
}
}
return OK;
}
/****************************************************************************
@@ -801,11 +794,11 @@ int ipv4_nat_outbound(FAR struct net_driver_s *dev,
{
/* Outbound entry creation failed, should have entry. */
return -ENOMEM;
return -ENOENT;
}
}
return OK;
}
#endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */
#endif /* CONFIG_NET_NAT44 */
+23 -25
View File
@@ -34,14 +34,14 @@
#include "nat/nat.h"
#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4)
#ifdef CONFIG_NET_NAT44
/****************************************************************************
* Private Data
****************************************************************************/
static DECLARE_HASHTABLE(g_table_inbound, CONFIG_NET_NAT_HASH_BITS);
static DECLARE_HASHTABLE(g_table_outbound, CONFIG_NET_NAT_HASH_BITS);
static DECLARE_HASHTABLE(g_nat44_inbound, CONFIG_NET_NAT_HASH_BITS);
static DECLARE_HASHTABLE(g_nat44_outbound, CONFIG_NET_NAT_HASH_BITS);
/****************************************************************************
* Private Functions
@@ -134,16 +134,16 @@ 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
#ifdef CONFIG_NET_NAT44_SYMMETRIC
entry->peer_ip = peer_ip;
entry->peer_port = peer_port;
#endif
ipv4_nat_entry_refresh(entry);
hashtable_add(g_table_inbound, &entry->hash_inbound,
hashtable_add(g_nat44_inbound, &entry->hash_inbound,
ipv4_nat_inbound_key(external_ip, external_port, protocol));
hashtable_add(g_table_outbound, &entry->hash_outbound,
hashtable_add(g_nat44_outbound, &entry->hash_outbound,
ipv4_nat_outbound_key(local_ip, local_port, protocol));
return entry;
@@ -162,16 +162,16 @@ ipv4_nat_entry_create(uint8_t protocol,
static void ipv4_nat_entry_delete(FAR struct ipv4_nat_entry *entry)
{
ninfo("INFO: Removing NAT entry proto=%" PRIu8
ninfo("INFO: Removing NAT44 entry proto=%" PRIu8
", local=%" PRIx32 ":%" PRIu16 ", external=:%" PRIu16 "\n",
entry->protocol, entry->local_ip, entry->local_port,
entry->external_port);
hashtable_delete(g_table_inbound, &entry->hash_inbound,
hashtable_delete(g_nat44_inbound, &entry->hash_inbound,
ipv4_nat_inbound_key(entry->external_ip,
entry->external_port,
entry->protocol));
hashtable_delete(g_table_outbound, &entry->hash_outbound,
hashtable_delete(g_nat44_outbound, &entry->hash_outbound,
ipv4_nat_outbound_key(entry->local_ip,
entry->local_port,
entry->protocol));
@@ -207,9 +207,9 @@ static void ipv4_nat_reclaim_entry(int32_t current_time)
int count = 0;
int i;
ninfo("INFO: Reclaiming all expired NAT entries.\n");
ninfo("INFO: Reclaiming all expired NAT44 entries.\n");
hashtable_for_every_safe(g_table_inbound, p, tmp, i)
hashtable_for_every_safe(g_nat44_inbound, p, tmp, i)
{
FAR struct ipv4_nat_entry *entry =
container_of(p, struct ipv4_nat_entry, hash_inbound);
@@ -221,10 +221,12 @@ static void ipv4_nat_reclaim_entry(int32_t current_time)
}
}
ninfo("INFO: %d expired NAT entries reclaimed.\n", count);
ninfo("INFO: %d expired NAT44 entries reclaimed.\n", count);
next_reclaim_time = current_time + CONFIG_NET_NAT_ENTRY_RECLAIM_SEC;
}
}
#else
# define ipv4_nat_reclaim_entry(t)
#endif
/****************************************************************************
@@ -239,7 +241,7 @@ static void ipv4_nat_reclaim_entry(int32_t current_time)
* any device.
*
* Input Parameters:
* dev - The device on which NAT entries will be cleared.
* dev - The device on which NAT entries will be cleared.
*
* Assumptions:
* NAT is initialized.
@@ -252,9 +254,9 @@ void ipv4_nat_entry_clear(FAR struct net_driver_s *dev)
FAR hash_node_t *tmp;
int i;
ninfo("INFO: Clearing all NAT entries for %s\n", dev->d_ifname);
ninfo("INFO: Clearing all NAT44 entries for %s\n", dev->d_ifname);
hashtable_for_every_safe(g_table_inbound, p, tmp, i)
hashtable_for_every_safe(g_nat44_inbound, p, tmp, i)
{
FAR struct ipv4_nat_entry *entry =
container_of(p, struct ipv4_nat_entry, hash_inbound);
@@ -293,16 +295,14 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip,
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
#ifdef CONFIG_NET_NAT44_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
ipv4_nat_reclaim_entry(current_time);
#endif
hashtable_for_every_possible_safe(g_table_inbound, p, tmp,
hashtable_for_every_possible_safe(g_nat44_inbound, p, tmp,
ipv4_nat_inbound_key(external_ip, external_port, protocol))
{
FAR struct ipv4_nat_entry *entry =
@@ -319,7 +319,7 @@ 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
#ifdef CONFIG_NET_NAT_SYMMETRIC
#ifdef CONFIG_NET_NAT44_SYMMETRIC
&& (skip_peer || (net_ipv4addr_cmp(entry->peer_ip, peer_ip) &&
entry->peer_port == peer_port))
#endif
@@ -376,11 +376,9 @@ ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
uint16_t external_port;
int32_t current_time = TICK2SEC(clock_systime_ticks());
#if CONFIG_NET_NAT_ENTRY_RECLAIM_SEC > 0
ipv4_nat_reclaim_entry(current_time);
#endif
hashtable_for_every_possible_safe(g_table_outbound, p, tmp,
hashtable_for_every_possible_safe(g_nat44_outbound, p, tmp,
ipv4_nat_outbound_key(local_ip, local_port, protocol))
{
FAR struct ipv4_nat_entry *entry =
@@ -398,7 +396,7 @@ ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
net_ipv4addr_cmp(entry->external_ip, dev->d_ipaddr) &&
net_ipv4addr_cmp(entry->local_ip, local_ip) &&
entry->local_port == local_port
#ifdef CONFIG_NET_NAT_SYMMETRIC
#ifdef CONFIG_NET_NAT44_SYMMETRIC
&& net_ipv4addr_cmp(entry->peer_ip, peer_ip) &&
entry->peer_port == peer_port
#endif
@@ -432,4 +430,4 @@ ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
local_ip, local_port, peer_ip, peer_port);
}
#endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */
#endif /* CONFIG_NET_NAT44 */
+692
View File
File diff suppressed because it is too large Load Diff
+437
View File
@@ -0,0 +1,437 @@
/****************************************************************************
* net/nat/ipv6_nat_entry.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <debug.h>
#include <stdint.h>
#include <nuttx/clock.h>
#include <nuttx/hashtable.h>
#include <nuttx/kmalloc.h>
#include <nuttx/nuttx.h>
#include "inet/inet.h"
#include "nat/nat.h"
#ifdef CONFIG_NET_NAT66
/****************************************************************************
* Private Data
****************************************************************************/
static DECLARE_HASHTABLE(g_nat66_inbound, CONFIG_NET_NAT_HASH_BITS);
static DECLARE_HASHTABLE(g_nat66_outbound, CONFIG_NET_NAT_HASH_BITS);
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: ipv6_nat_hash_key
*
* Description:
* Create a hash key for NAT66.
*
****************************************************************************/
static inline uint32_t ipv6_nat_hash_key(const net_ipv6addr_t ip,
uint16_t port, uint8_t protocol)
{
uint32_t key = (((uint32_t)ip[0] << 16) | ip[1]) ^
(((uint32_t)ip[2] << 16) | ip[3]) ^
(((uint32_t)ip[4] << 16) | ip[5]) ^
(((uint32_t)ip[6] << 16) | ip[7]);
return key ^ ((uint32_t)protocol << 16) ^ port;
}
/****************************************************************************
* Name: ipv6_nat_entry_refresh
*
* Description:
* Refresh a NAT entry, update its expiration time.
*
* Input Parameters:
* entry - The entry to refresh.
*
****************************************************************************/
static void ipv6_nat_entry_refresh(FAR struct ipv6_nat_entry *entry)
{
entry->expire_time = nat_expire_time(entry->protocol);
}
/****************************************************************************
* Name: ipv6_nat_entry_create
*
* Description:
* Create a NAT entry and insert into entry list.
*
* Input Parameters:
* protocol - The L4 protocol of the packet.
* external_ip - The external ip of the packet.
* 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
*
****************************************************************************/
static FAR struct ipv6_nat_entry *
ipv6_nat_entry_create(uint8_t protocol, const net_ipv6addr_t external_ip,
uint16_t external_port, const net_ipv6addr_t local_ip,
uint16_t local_port, const net_ipv6addr_t peer_ip,
uint16_t peer_port)
{
FAR struct ipv6_nat_entry *entry =
kmm_malloc(sizeof(struct ipv6_nat_entry));
if (entry == NULL)
{
nwarn("WARNING: Failed to allocate IPv6 NAT entry\n");
return NULL;
}
entry->protocol = protocol;
entry->external_port = external_port;
entry->local_port = local_port;
#ifdef CONFIG_NET_NAT66_SYMMETRIC
entry->peer_port = peer_port;
#endif
net_ipv6addr_copy(entry->external_ip, external_ip);
net_ipv6addr_copy(entry->local_ip, local_ip);
#ifdef CONFIG_NET_NAT66_SYMMETRIC
net_ipv6addr_copy(entry->peer_ip, peer_ip);
#endif
ipv6_nat_entry_refresh(entry);
hashtable_add(g_nat66_inbound, &entry->hash_inbound,
ipv6_nat_hash_key(external_ip, external_port, protocol));
hashtable_add(g_nat66_outbound, &entry->hash_outbound,
ipv6_nat_hash_key(local_ip, local_port, protocol));
return entry;
}
/****************************************************************************
* Name: ipv6_nat_entry_delete
*
* Description:
* Delete a NAT entry and remove from entry list.
*
* Input Parameters:
* entry - The entry to remove.
*
****************************************************************************/
static void ipv6_nat_entry_delete(FAR struct ipv6_nat_entry *entry)
{
ninfo("INFO: Removing NAT66 entry proto=%" PRIu8
", local=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%" PRIu16
", external=:%" PRIu16 "\n",
entry->protocol, entry->local_ip[0], entry->local_ip[1],
entry->local_ip[2], entry->local_ip[3], entry->local_ip[4],
entry->local_ip[5], entry->local_ip[6], entry->local_ip[7],
entry->local_port, entry->external_port);
hashtable_delete(g_nat66_inbound, &entry->hash_inbound,
ipv6_nat_hash_key(entry->external_ip,
entry->external_port,
entry->protocol));
hashtable_delete(g_nat66_outbound, &entry->hash_outbound,
ipv6_nat_hash_key(entry->local_ip,
entry->local_port,
entry->protocol));
kmm_free(entry);
}
/****************************************************************************
* Name: ipv6_nat_reclaim_entry
*
* Description:
* Try reclaim all expired NAT entries.
* Only works after every CONFIG_NET_NAT_ENTRY_RECLAIM_SEC (low frequency).
*
* Although expired entries will be automatically reclaimed when matching
* inbound/outbound entries, there might be some situations that entries
* will be kept in memory, e.g. big hashtable with only a few connections.
*
* Assumptions:
* NAT is initialized.
*
****************************************************************************/
#if CONFIG_NET_NAT_ENTRY_RECLAIM_SEC > 0
static void ipv6_nat_reclaim_entry(int32_t current_time)
{
static int32_t next_reclaim_time = CONFIG_NET_NAT_ENTRY_RECLAIM_SEC;
if (next_reclaim_time - current_time <= 0)
{
FAR hash_node_t *p;
FAR hash_node_t *tmp;
int count = 0;
int i;
ninfo("INFO: Reclaiming all expired NAT66 entries.\n");
hashtable_for_every_safe(g_nat66_inbound, p, tmp, i)
{
FAR struct ipv6_nat_entry *entry =
container_of(p, struct ipv6_nat_entry, hash_inbound);
if (entry->expire_time - current_time <= 0)
{
ipv6_nat_entry_delete(entry);
count++;
}
}
ninfo("INFO: %d expired NAT66 entries reclaimed.\n", count);
next_reclaim_time = current_time + CONFIG_NET_NAT_ENTRY_RECLAIM_SEC;
}
}
#else
# define ipv6_nat_reclaim_entry(t)
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: ipv6_nat_entry_clear
*
* Description:
* Clear all entries related to dev. Called when NAT will be disabled on
* any device.
*
* Input Parameters:
* dev - The device on which NAT entries will be cleared.
*
* Assumptions:
* NAT is initialized.
*
****************************************************************************/
void ipv6_nat_entry_clear(FAR struct net_driver_s *dev)
{
FAR hash_node_t *p;
FAR hash_node_t *tmp;
int i;
ninfo("INFO: Clearing all NAT66 entries for %s\n", dev->d_ifname);
hashtable_for_every_safe(g_nat66_inbound, p, tmp, i)
{
FAR struct ipv6_nat_entry *entry =
container_of(p, struct ipv6_nat_entry, hash_inbound);
if (NETDEV_IS_MY_V6ADDR(dev, entry->external_ip))
{
ipv6_nat_entry_delete(entry);
}
}
}
/****************************************************************************
* Name: ipv6_nat_inbound_entry_find
*
* Description:
* Find the inbound entry in NAT entry list.
*
* Input Parameters:
* 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:
* Pointer to entry on success; null on failure
*
****************************************************************************/
FAR struct ipv6_nat_entry *
ipv6_nat_inbound_entry_find(uint8_t protocol,
const net_ipv6addr_t external_ip,
uint16_t external_port,
const net_ipv6addr_t peer_ip,
uint16_t peer_port, bool refresh)
{
FAR hash_node_t *p;
FAR hash_node_t *tmp;
bool skip_ip = net_ipv6addr_cmp(external_ip, g_ipv6_unspecaddr);
#ifdef CONFIG_NET_NAT66_SYMMETRIC
bool skip_peer = net_ipv6addr_cmp(peer_ip, g_ipv6_unspecaddr);
#endif
int32_t current_time = TICK2SEC(clock_systime_ticks());
ipv6_nat_reclaim_entry(current_time);
hashtable_for_every_possible_safe(g_nat66_inbound, p, tmp,
ipv6_nat_hash_key(external_ip, external_port, protocol))
{
FAR struct ipv6_nat_entry *entry =
container_of(p, struct ipv6_nat_entry, hash_inbound);
/* Remove expired entries. */
if (entry->expire_time - current_time <= 0)
{
ipv6_nat_entry_delete(entry);
continue;
}
if (entry->protocol == protocol &&
(skip_ip || net_ipv6addr_cmp(entry->external_ip, external_ip)) &&
entry->external_port == external_port
#ifdef CONFIG_NET_NAT66_SYMMETRIC
&& (skip_peer || (net_ipv6addr_cmp(entry->peer_ip, peer_ip) &&
entry->peer_port == peer_port))
#endif
)
{
if (refresh)
{
ipv6_nat_entry_refresh(entry);
}
return entry;
}
}
if (refresh) /* false = a test of whether entry exists, no need to warn */
{
nwarn("WARNING: Failed to find IPv6 inbound NAT entry for proto="
"%" PRIu8 ",external=[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]:"
"%" PRIu16 "\n",
protocol, external_ip[0], external_ip[1], external_ip[2],
external_ip[3], external_ip[4], external_ip[5], external_ip[6],
external_ip[7], external_port);
}
return NULL;
}
/****************************************************************************
* Name: ipv6_nat_outbound_entry_find
*
* Description:
* Find the outbound entry in NAT entry list. Create one if corresponding
* entry does not exist.
*
* Input Parameters:
* dev - The device on which the packet will be sent.
* 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:
* Pointer to entry on success; null on failure
*
****************************************************************************/
FAR struct ipv6_nat_entry *
ipv6_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
const net_ipv6addr_t local_ip,
uint16_t local_port,
const net_ipv6addr_t peer_ip,
uint16_t peer_port, bool try_create)
{
FAR hash_node_t *p;
FAR hash_node_t *tmp;
FAR union ip_addr_u *external_ip;
uint16_t external_port;
int32_t current_time = TICK2SEC(clock_systime_ticks());
ipv6_nat_reclaim_entry(current_time);
hashtable_for_every_possible_safe(g_nat66_outbound, p, tmp,
ipv6_nat_hash_key(local_ip, local_port, protocol))
{
FAR struct ipv6_nat_entry *entry =
container_of(p, struct ipv6_nat_entry, hash_outbound);
/* Remove expired entries. */
if (entry->expire_time - current_time <= 0)
{
ipv6_nat_entry_delete(entry);
continue;
}
if (entry->protocol == protocol &&
NETDEV_IS_MY_V6ADDR(dev, entry->external_ip) &&
net_ipv6addr_cmp(entry->local_ip, local_ip) &&
entry->local_port == local_port
#ifdef CONFIG_NET_NAT66_SYMMETRIC
&& net_ipv6addr_cmp(entry->peer_ip, peer_ip) &&
entry->peer_port == peer_port
#endif
)
{
ipv6_nat_entry_refresh(entry);
return entry;
}
}
if (!try_create)
{
return NULL;
}
/* Failed to find the entry, create one. */
ninfo("INFO: Failed to find IPv6 outbound NAT entry for proto=%" PRIu8
", local=[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]:%" PRIu16
", try create one.\n",
protocol, local_ip[0], local_ip[1], local_ip[2], local_ip[3],
local_ip[4], local_ip[5], local_ip[6], local_ip[7], local_port);
external_ip = (FAR union ip_addr_u *)netdev_ipv6_srcaddr(dev, peer_ip);
external_port = nat_port_select(dev, PF_INET6, protocol,
external_ip, local_port);
if (!external_port)
{
nwarn("WARNING: Failed to find an available port!\n");
return NULL;
}
return ipv6_nat_entry_create(protocol, external_ip->ipv6, external_port,
local_ip, local_port, peer_ip, peer_port);
}
#endif /* CONFIG_NET_NAT66 */
+69 -5
View File
@@ -27,6 +27,8 @@
#include <debug.h>
#include "icmp/icmp.h"
#include "icmpv6/icmpv6.h"
#include "inet/inet.h"
#include "nat/nat.h"
#include "tcp/tcp.h"
#include "udp/udp.h"
@@ -57,7 +59,8 @@
#if (defined(CONFIG_NET_TCP) && defined(CONFIG_NET_TCP_NO_STACK)) || \
(defined(CONFIG_NET_UDP) && defined(CONFIG_NET_UDP_NO_STACK)) || \
(defined(CONFIG_NET_ICMP) && !defined(CONFIG_NET_ICMP_SOCKET))
(defined(CONFIG_NET_ICMP) && !defined(CONFIG_NET_ICMP_SOCKET)) || \
(defined(CONFIG_NET_ICMPv6) && !defined(CONFIG_NET_ICMPv6_SOCKET))
static uint16_t nat_port_select_without_stack(
uint8_t domain, uint8_t protocol, FAR const union ip_addr_u *ip,
@@ -145,9 +148,12 @@ int nat_disable(FAR struct net_driver_s *dev)
/* Clear entries related to dev. */
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_NAT44
ipv4_nat_entry_clear(dev);
#endif
#ifdef CONFIG_NET_NAT66
ipv6_nat_entry_clear(dev);
#endif
IFF_CLR_NAT(dev->d_flags);
@@ -175,7 +181,7 @@ int nat_disable(FAR struct net_driver_s *dev)
bool nat_port_inuse(uint8_t domain, uint8_t protocol,
FAR const union ip_addr_u *ip, uint16_t port)
{
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_NAT44
if (domain == PF_INET)
{
return !!ipv4_nat_inbound_entry_find(protocol, ip->ipv4, port,
@@ -183,6 +189,14 @@ bool nat_port_inuse(uint8_t domain, uint8_t protocol,
}
#endif
#ifdef CONFIG_NET_NAT66
if (domain == PF_INET6)
{
return !!ipv6_nat_inbound_entry_find(protocol, ip->ipv6, port,
g_ipv6_unspecaddr, 0, false);
}
#endif
return false;
}
@@ -239,8 +253,25 @@ uint16_t nat_port_select(FAR struct net_driver_s *dev,
{
#ifndef CONFIG_NET_UDP_NO_STACK
union ip_binding_u u;
u.ipv4.laddr = external_ip->ipv4;
u.ipv4.raddr = INADDR_ANY;
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (domain == PF_INET)
#endif
{
u.ipv4.laddr = external_ip->ipv4;
u.ipv4.raddr = INADDR_ANY;
}
#endif
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
net_ipv6addr_copy(u.ipv6.laddr, external_ip->ipv6);
net_ipv6addr_copy(u.ipv6.raddr, g_ipv6_unspecaddr);
}
#endif
/* TODO: Try keep origin port as possible. */
@@ -278,6 +309,33 @@ uint16_t nat_port_select(FAR struct net_driver_s *dev,
#endif
}
#endif
#ifdef CONFIG_NET_ICMPv6
case IP_PROTO_ICMP6:
{
#ifdef CONFIG_NET_ICMPv6_SOCKET
uint16_t id = local_port;
uint16_t hid = NTOHS(id);
while (icmpv6_active(id) ||
nat_port_inuse(domain, IP_PROTO_ICMP6, external_ip, id))
{
++hid;
if (hid >= CONFIG_NET_DEFAULT_MAX_PORT ||
hid < CONFIG_NET_DEFAULT_MIN_PORT)
{
hid = CONFIG_NET_DEFAULT_MIN_PORT;
}
id = HTONS(hid);
}
return id;
#else
return nat_port_select_without_stack(domain, IP_PROTO_ICMP6,
external_ip, local_port);
#endif
}
#endif
}
/* Select original port for unsupported protocol. */
@@ -332,6 +390,12 @@ uint32_t nat_expire_time(uint8_t protocol)
CONFIG_NET_NAT_ICMP_EXPIRE_SEC;
#endif
#ifdef CONFIG_NET_ICMPv6
case IP_PROTO_ICMP6:
return TICK2SEC(clock_systime_ticks()) +
CONFIG_NET_NAT_ICMPv6_EXPIRE_SEC;
#endif
default:
nwarn("WARNING: Unsupported protocol %" PRIu8 "\n", protocol);
return 0;
+67 -24
View File
@@ -87,12 +87,12 @@ struct ipv4_nat_entry
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
#ifdef CONFIG_NET_NAT44_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
#ifdef CONFIG_NET_NAT44_SYMMETRIC
uint16_t peer_port; /* Peer port. */
#endif
uint8_t protocol; /* L4 protocol (TCP, UDP etc). */
@@ -100,6 +100,26 @@ struct ipv4_nat_entry
int32_t expire_time; /* The expiration time of this entry. */
};
struct ipv6_nat_entry
{
hash_node_t hash_inbound;
hash_node_t hash_outbound;
net_ipv6addr_t local_ip; /* IP address of the local host. */
net_ipv6addr_t external_ip; /* External IP address. */
#ifdef CONFIG_NET_NAT66_SYMMETRIC
net_ipv6addr_t peer_ip; /* Peer IP address. */
#endif
uint16_t local_port; /* Port of the local host. */
uint16_t external_port; /* The external port of local host. */
#ifdef CONFIG_NET_NAT66_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. */
};
/* NAT IP/Port manipulate type, to indicate whether to manipulate source or
* destination IP/Port in a packet.
*/
@@ -149,37 +169,36 @@ int nat_enable(FAR struct net_driver_s *dev);
int nat_disable(FAR struct net_driver_s *dev);
/****************************************************************************
* Name: ipv4_nat_inbound
* Name: ipv4/ipv6_nat_inbound
*
* Description:
* Check if a received packet belongs to a NAT entry. If so, translate it.
*
* Input Parameters:
* dev - The device on which the packet is received.
* 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.
* dev - The device on which the packet is received.
* ipv4/ipv6 - Points to the IP header with dev->d_buf.
*
****************************************************************************/
#ifdef CONFIG_NET_IPv4
int ipv4_nat_inbound(FAR struct net_driver_s *dev,
FAR struct ipv4_hdr_s *ipv4);
#ifdef CONFIG_NET_NAT44
void ipv4_nat_inbound(FAR struct net_driver_s *dev,
FAR struct ipv4_hdr_s *ipv4);
#endif
#ifdef CONFIG_NET_NAT66
void ipv6_nat_inbound(FAR struct net_driver_s *dev,
FAR struct ipv6_hdr_s *ipv6);
#endif
/****************************************************************************
* Name: ipv4_nat_outbound
* Name: ipv4/ipv6_nat_outbound
*
* Description:
* Check if we want to perform NAT with this outbound packet before sending
* it. If so, translate it.
*
* Input Parameters:
* dev - The device on which the packet will be sent.
* ipv4 - Points to the IPv4 header to be filled into dev->d_buf later.
* dev - The device on which the packet will be sent.
* ipv4/ipv6 - Points to the IP header to be filled into dev->d_buf later.
* manip_type - Whether local IP/Port is in source or destination.
*
* Returned Value:
@@ -189,11 +208,16 @@ int ipv4_nat_inbound(FAR struct net_driver_s *dev,
*
****************************************************************************/
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_NAT44
int ipv4_nat_outbound(FAR struct net_driver_s *dev,
FAR struct ipv4_hdr_s *ipv4,
enum nat_manip_type_e manip_type);
#endif
#ifdef CONFIG_NET_NAT66
int ipv6_nat_outbound(FAR struct net_driver_s *dev,
FAR struct ipv6_hdr_s *ipv6,
enum nat_manip_type_e manip_type);
#endif
/****************************************************************************
* Name: nat_port_inuse
@@ -255,26 +279,29 @@ uint16_t nat_port_select(FAR struct net_driver_s *dev,
uint32_t nat_expire_time(uint8_t protocol);
/****************************************************************************
* Name: ipv4_nat_entry_clear
* Name: ipv4/ipv6_nat_entry_clear
*
* Description:
* Clear all entries related to dev. Called when NAT will be disabled on
* any device.
*
* Input Parameters:
* dev - The device on which NAT entries will be cleared.
* dev - The device on which NAT entries will be cleared.
*
* Assumptions:
* NAT is initialized.
*
****************************************************************************/
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_NAT44
void ipv4_nat_entry_clear(FAR struct net_driver_s *dev);
#endif
#ifdef CONFIG_NET_NAT66
void ipv6_nat_entry_clear(FAR struct net_driver_s *dev);
#endif
/****************************************************************************
* Name: ipv4_nat_inbound_entry_find
* Name: ipv4/ipv6_nat_inbound_entry_find
*
* Description:
* Find the inbound entry in NAT entry list.
@@ -292,15 +319,23 @@ void ipv4_nat_entry_clear(FAR struct net_driver_s *dev);
*
****************************************************************************/
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_NAT44
FAR struct ipv4_nat_entry *
ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip,
uint16_t external_port, in_addr_t peer_ip,
uint16_t peer_port, bool refresh);
#endif
#ifdef CONFIG_NET_NAT66
FAR struct ipv6_nat_entry *
ipv6_nat_inbound_entry_find(uint8_t protocol,
const net_ipv6addr_t external_ip,
uint16_t external_port,
const net_ipv6addr_t peer_ip,
uint16_t peer_port, bool refresh);
#endif
/****************************************************************************
* Name: ipv4_nat_outbound_entry_find
* Name: ipv4/ipv6_nat_outbound_entry_find
*
* Description:
* Find the outbound entry in NAT entry list. Create one if corresponding
@@ -320,13 +355,21 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip,
*
****************************************************************************/
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_NAT44
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
#ifdef CONFIG_NET_NAT66
FAR struct ipv6_nat_entry *
ipv6_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
const net_ipv6addr_t local_ip,
uint16_t local_port,
const net_ipv6addr_t peer_ip,
uint16_t peer_port, bool try_create);
#endif
#endif /* CONFIG_NET_NAT */
#endif /* __NET_NAT_NAT_H */