diff --git a/include/nuttx/queue.h b/include/nuttx/queue.h index 4bf3cbcb37..80b2a04acd 100644 --- a/include/nuttx/queue.h +++ b/include/nuttx/queue.h @@ -158,6 +158,10 @@ #define sq_for_every(q, p) \ for ((p) = (q)->head; (p) != NULL; (p) = (p)->flink) +#define sq_for_every_safe(q, p, tmp) \ + for((p) = (q)->head, (tmp) = (p) ? (p)->flink : NULL; \ + (p) != NULL; (p) = (tmp), (tmp) = (p) ? (p)->flink : NULL) + #define sq_rem(p, q) \ do \ { \ diff --git a/net/nat/Kconfig b/net/nat/Kconfig index f50d237695..670a2f2d28 100644 --- a/net/nat/Kconfig +++ b/net/nat/Kconfig @@ -9,3 +9,13 @@ config NET_NAT depends on NET_IPFORWARD ---help--- Enable or disable Network Address Translation (NAT) function. + +config NET_NAT_TCP_EXPIRE_SEC + int "TCP NAT entry expiration seconds" + default 86400 + depends on NET_NAT + ---help--- + The expiration time for idle TCP entry in NAT. + + Note: The default value 86400 is suggested by RFC2663, Section 2.6, + Page 5. diff --git a/net/nat/ipv4_nat.c b/net/nat/ipv4_nat.c index 8b0811e52e..12a4831585 100644 --- a/net/nat/ipv4_nat.c +++ b/net/nat/ipv4_nat.c @@ -79,7 +79,7 @@ static int ipv4_nat_inbound_tcp(FAR struct ipv4_hdr_s *ipv4) FAR struct tcp_hdr_s *tcp = (FAR struct tcp_hdr_s *)((FAR uint8_t *)ipv4 + iphdrlen); FAR struct ipv4_nat_entry *entry = - ipv4_nat_inbound_entry_find(IP_PROTO_TCP, tcp->destport); + ipv4_nat_inbound_entry_find(IP_PROTO_TCP, tcp->destport, true); if (!entry) { /* Inbound without entry is OK (e.g. towards NuttX itself), skip NAT. */ @@ -340,7 +340,7 @@ int ipv4_nat_outbound(FAR struct net_driver_s *dev, bool ipv4_nat_port_inuse(uint8_t protocol, in_addr_t ip, uint16_t port) { FAR struct ipv4_nat_entry *entry = - ipv4_nat_inbound_entry_find(protocol, port); + ipv4_nat_inbound_entry_find(protocol, port, false); /* Not checking ip is enough for single NAT device, may save external_ip in * entry for multiple device support in future. diff --git a/net/nat/ipv4_nat_entry.c b/net/nat/ipv4_nat_entry.c index 4a57549355..a5e4681111 100644 --- a/net/nat/ipv4_nat_entry.c +++ b/net/nat/ipv4_nat_entry.c @@ -26,6 +26,7 @@ #include +#include #include #include @@ -37,7 +38,7 @@ * Private Data ****************************************************************************/ -static sq_queue_t g_entries; +static dq_queue_t g_entries; /**************************************************************************** * Private Functions @@ -69,6 +70,45 @@ static uint16_t ipv4_nat_select_port(uint8_t protocol, uint16_t local_port) return local_port; } +/**************************************************************************** + * Name: ipv4_nat_entry_refresh + * + * Description: + * Refresh a NAT entry, update its expiration time. + * + * Input Parameters: + * entry - The entry to refresh. + * + ****************************************************************************/ + +static void ipv4_nat_entry_refresh(FAR struct ipv4_nat_entry *entry) +{ + switch (entry->protocol) + { +#ifdef CONFIG_NET_TCP + case IP_PROTO_TCP: + /* NOTE: According to RFC2663, Section 2.6, Page 5, we can reduce the + * time to 4min if we have received FINs from both side of one + * connection, and keep 24h for other TCP connections. However, full + * cone NAT may have multiple connections on one entry, so this + * optimization may not work and we only use one expiration time. + */ + + entry->expire_time = TICK2SEC(clock_systime_ticks()) + + CONFIG_NET_NAT_TCP_EXPIRE_SEC; + break; +#endif + +#ifdef CONFIG_NET_UDP +# warning Missing logic +#endif + +#ifdef CONFIG_NET_ICMP +# warning Missing logic +#endif + } +} + /**************************************************************************** * Name: ipv4_nat_entry_create * @@ -103,10 +143,33 @@ ipv4_nat_entry_create(uint8_t protocol, uint16_t external_port, entry->local_ip = local_ip; entry->local_port = local_port; - sq_addfirst((FAR sq_entry_t *)entry, &g_entries); + ipv4_nat_entry_refresh(entry); + + dq_addfirst((FAR dq_entry_t *)entry, &g_entries); return entry; } +/**************************************************************************** + * Name: ipv4_nat_entry_delete + * + * Description: + * Delete a NAT entry and remove from entry list. + * + * Input Parameters: + * entry - The entry to remove. + * + ****************************************************************************/ + +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); + kmm_free(entry); +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -120,6 +183,7 @@ ipv4_nat_entry_create(uint8_t protocol, uint16_t external_port, * Input Parameters: * protocol - The L4 protocol of the packet. * external_port - The external port of the packet. + * refresh - Whether to refresh the selected entry. * * Returned Value: * Pointer to entry on success; null on failure @@ -127,17 +191,35 @@ ipv4_nat_entry_create(uint8_t protocol, uint16_t external_port, ****************************************************************************/ FAR struct ipv4_nat_entry * -ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port) +ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port, + bool refresh) { FAR sq_entry_t *p; - sq_for_every(&g_entries, p) + FAR sq_entry_t *tmp; + uint32_t current_time = TICK2SEC(clock_systime_ticks()); + + sq_for_every_safe((FAR sq_queue_t *)&g_entries, p, tmp) { FAR struct ipv4_nat_entry *entry = (FAR struct ipv4_nat_entry *)p; + + /* Remove expired entries. */ + + if (entry->expire_time < current_time) + { + ipv4_nat_entry_delete(entry); + continue; + } + 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); + } + return entry; } } @@ -169,15 +251,28 @@ ipv4_nat_outbound_entry_find(uint8_t protocol, in_addr_t local_ip, uint16_t local_port) { FAR sq_entry_t *p; - sq_for_every(&g_entries, p) + FAR sq_entry_t *tmp; + uint32_t current_time = TICK2SEC(clock_systime_ticks()); + + sq_for_every_safe((FAR sq_queue_t *)&g_entries, p, tmp) { FAR struct ipv4_nat_entry *entry = (FAR struct ipv4_nat_entry *)p; + + /* Remove expired entries. */ + + if (entry->expire_time < current_time) + { + ipv4_nat_entry_delete(entry); + continue; + } + if (entry->protocol == 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; } } diff --git a/net/nat/nat.h b/net/nat/nat.h index e5df4db633..a68d0af4db 100644 --- a/net/nat/nat.h +++ b/net/nat/nat.h @@ -43,13 +43,14 @@ struct ipv4_nat_entry { - /* Support for single-linked lists. + /* 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; /* Local Network External Network * |----------------| @@ -67,7 +68,7 @@ struct ipv4_nat_entry uint16_t external_port; /* The external port of local (private) host. */ uint8_t protocol; /* L4 protocol (TCP, UDP etc). */ - /* TODO: Timeout check and remove outdated entry. */ + uint32_t expire_time; /* The expiration time of this entry. */ }; /**************************************************************************** @@ -184,6 +185,7 @@ bool ipv4_nat_port_inuse(uint8_t protocol, in_addr_t ip, uint16_t port); * Input Parameters: * protocol - The L4 protocol of the packet. * external_port - The external port of the packet. + * refresh - Whether to refresh the selected entry. * * Returned Value: * Pointer to entry on success; null on failure @@ -191,7 +193,8 @@ bool ipv4_nat_port_inuse(uint8_t protocol, in_addr_t ip, uint16_t port); ****************************************************************************/ FAR struct ipv4_nat_entry * -ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port); +ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port, + bool refresh); /**************************************************************************** * Name: ipv4_nat_outbound_entry_find