mirror of
https://github.com/apache/nuttx.git
synced 2025-01-13 09:49:21 +08:00
net/nat: Use hashtable to optimize performance
Performance tested on simulator: Before optimization: -25% bandwidth @2k entries, -64% @10k entries hashtable size= 2(1bit): -24% bandwidth @2k entries, -65% @10k entries hashtable size= 4(2bits):-15% bandwidth @2k entries, -51% @10k entries hashtable size= 32(5bits): -3% bandwidth @2k entries, -14% @10k entries hashtable size=256(8bits): -1% bandwidth @2k entries, -3% @10k entries Note: Tested on worst performance, the earliest entry will be the worst. Signed-off-by: Zhe Weng <wengzhe@xiaomi.com>
This commit is contained in:
parent
573c79fd56
commit
879c337e30
3 changed files with 75 additions and 24 deletions
|
@ -15,6 +15,14 @@ config NET_NAT
|
|||
and NAT may need a continuous buffer of at least 68 Bytes
|
||||
(IPv4 20B + ICMP 8B + IPv4 20B + TCP 20B).
|
||||
|
||||
config NET_NAT_HASH_BITS
|
||||
int "The bits of NAT entry hashtable"
|
||||
default 5
|
||||
range 1 10
|
||||
depends on NET_NAT
|
||||
---help---
|
||||
The hashtable of NAT entries will have (1 << bits) buckets.
|
||||
|
||||
config NET_NAT_TCP_EXPIRE_SEC
|
||||
int "TCP NAT entry expiration seconds"
|
||||
default 86400
|
||||
|
|
|
@ -25,9 +25,12 @@
|
|||
#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 <nuttx/queue.h>
|
||||
|
||||
#include "icmp/icmp.h"
|
||||
|
@ -50,12 +53,43 @@
|
|||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static dq_queue_t g_entries;
|
||||
static DECLARE_HASHTABLE(g_table_inbound, CONFIG_NET_NAT_HASH_BITS);
|
||||
static DECLARE_HASHTABLE(g_table_outbound, CONFIG_NET_NAT_HASH_BITS);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv4_nat_inbound_key
|
||||
*
|
||||
* Description:
|
||||
* Create an inbound hash key for NAT.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline uint32_t ipv4_nat_inbound_key(uint16_t external_port,
|
||||
uint8_t protocol)
|
||||
{
|
||||
return ((uint32_t)protocol << 16) | external_port;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv4_nat_outbound_key
|
||||
*
|
||||
* Description:
|
||||
* Create an outbound hash key for NAT.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline uint32_t ipv4_nat_outbound_key(in_addr_t local_ip,
|
||||
uint16_t local_port,
|
||||
uint8_t protocol)
|
||||
{
|
||||
return NTOHL(local_ip) ^ /* NTOHL makes sure difference is in lower bits. */
|
||||
((uint32_t)protocol << 8) ^ ((uint32_t)local_port << 16);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv4_nat_select_port_without_stack
|
||||
*
|
||||
|
@ -211,6 +245,10 @@ static uint16_t ipv4_nat_select_port(FAR struct net_driver_s *dev,
|
|||
|
||||
static void ipv4_nat_entry_refresh(FAR struct ipv4_nat_entry *entry)
|
||||
{
|
||||
/* Note: May add logic here to move recent node to head side if each chain
|
||||
* in hashtable is still too long (with long expire time).
|
||||
*/
|
||||
|
||||
switch (entry->protocol)
|
||||
{
|
||||
#ifdef CONFIG_NET_TCP
|
||||
|
@ -279,7 +317,11 @@ ipv4_nat_entry_create(uint8_t protocol, uint16_t external_port,
|
|||
|
||||
ipv4_nat_entry_refresh(entry);
|
||||
|
||||
dq_addfirst((FAR dq_entry_t *)entry, &g_entries);
|
||||
hashtable_add(g_table_inbound, &entry->hash_inbound,
|
||||
ipv4_nat_inbound_key(external_port, protocol));
|
||||
hashtable_add(g_table_outbound, &entry->hash_outbound,
|
||||
ipv4_nat_outbound_key(local_ip, local_port, protocol));
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
@ -294,13 +336,19 @@ ipv4_nat_entry_create(uint8_t protocol, uint16_t external_port,
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
void ipv4_nat_entry_delete(FAR struct ipv4_nat_entry *entry)
|
||||
static void ipv4_nat_entry_delete(FAR struct ipv4_nat_entry *entry)
|
||||
{
|
||||
ninfo("INFO: Removing NAT entry proto=%d, local=%x:%d, external=:%d\n",
|
||||
entry->protocol, entry->local_ip, entry->local_port,
|
||||
entry->external_port);
|
||||
|
||||
dq_rem((FAR dq_entry_t *)entry, &g_entries);
|
||||
hashtable_delete(g_table_inbound, &entry->hash_inbound,
|
||||
ipv4_nat_inbound_key(entry->external_port,
|
||||
entry->protocol));
|
||||
hashtable_delete(g_table_outbound, &entry->hash_outbound,
|
||||
ipv4_nat_outbound_key(entry->local_ip, entry->local_port,
|
||||
entry->protocol));
|
||||
|
||||
kmm_free(entry);
|
||||
}
|
||||
|
||||
|
@ -328,13 +376,15 @@ FAR struct ipv4_nat_entry *
|
|||
ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port,
|
||||
bool refresh)
|
||||
{
|
||||
FAR sq_entry_t *p;
|
||||
FAR sq_entry_t *tmp;
|
||||
FAR hash_node_t *p;
|
||||
FAR hash_node_t *tmp;
|
||||
uint32_t current_time = TICK2SEC(clock_systime_ticks());
|
||||
|
||||
sq_for_every_safe((FAR sq_queue_t *)&g_entries, p, tmp)
|
||||
hashtable_for_every_possible_safe(g_table_inbound, p, tmp,
|
||||
ipv4_nat_inbound_key(external_port, protocol))
|
||||
{
|
||||
FAR struct ipv4_nat_entry *entry = (FAR struct ipv4_nat_entry *)p;
|
||||
FAR struct ipv4_nat_entry *entry =
|
||||
container_of(p, struct ipv4_nat_entry, hash_inbound);
|
||||
|
||||
/* Remove expired entries. */
|
||||
|
||||
|
@ -347,8 +397,6 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port,
|
|||
if (entry->protocol == protocol &&
|
||||
entry->external_port == external_port)
|
||||
{
|
||||
/* TODO: Use hash table, or move recent node to head. */
|
||||
|
||||
if (refresh)
|
||||
{
|
||||
ipv4_nat_entry_refresh(entry);
|
||||
|
@ -387,13 +435,15 @@ ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
|
|||
in_addr_t local_ip, uint16_t local_port,
|
||||
bool try_create)
|
||||
{
|
||||
FAR sq_entry_t *p;
|
||||
FAR sq_entry_t *tmp;
|
||||
FAR hash_node_t *p;
|
||||
FAR hash_node_t *tmp;
|
||||
uint32_t current_time = TICK2SEC(clock_systime_ticks());
|
||||
|
||||
sq_for_every_safe((FAR sq_queue_t *)&g_entries, p, tmp)
|
||||
hashtable_for_every_possible_safe(g_table_outbound, p, tmp,
|
||||
ipv4_nat_outbound_key(local_ip, local_port, protocol))
|
||||
{
|
||||
FAR struct ipv4_nat_entry *entry = (FAR struct ipv4_nat_entry *)p;
|
||||
FAR struct ipv4_nat_entry *entry =
|
||||
container_of(p, struct ipv4_nat_entry, hash_outbound);
|
||||
|
||||
/* Remove expired entries. */
|
||||
|
||||
|
@ -407,8 +457,6 @@ ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
|
|||
net_ipv4addr_cmp(entry->local_ip, local_ip) &&
|
||||
entry->local_port == local_port)
|
||||
{
|
||||
/* TODO: Use hash table, or move recent node to head. */
|
||||
|
||||
ipv4_nat_entry_refresh(entry);
|
||||
return entry;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <nuttx/hashtable.h>
|
||||
#include <nuttx/net/ip.h>
|
||||
#include <nuttx/net/netdev.h>
|
||||
|
||||
|
@ -43,14 +44,8 @@
|
|||
|
||||
struct ipv4_nat_entry
|
||||
{
|
||||
/* Support for doubly-linked lists.
|
||||
*
|
||||
* TODO: Implement a general hash table, and use it to optimize performance
|
||||
* here.
|
||||
*/
|
||||
|
||||
FAR struct ipv4_nat_entry *flink;
|
||||
FAR struct ipv4_nat_entry *blink;
|
||||
hash_node_t hash_inbound;
|
||||
hash_node_t hash_outbound;
|
||||
|
||||
/* Local Network External Network
|
||||
* |----------------|
|
||||
|
|
Loading…
Reference in a new issue