napt: Fixes and improvements (2.1.3)

1. Fix enable/disable to properly allocate and deallocate tables.
    Current algorithm is just broken.
 2. Introduce eviction policy when table gets full: oldest connection
    is evicted, instead of new ones getting silently dropped. this
    results in much better behavior with small tables than before.
    When TCP connection is dropped, RSTs are sent both ways to inform
    parties instead of dropping silently. thiw requires additional 8
    bytes per entry but is, again, a big improvement for clients in
    terms of usability.
 3. FIxed handling of timestamp wraparound (every ~50 days of uptime).
 3. Added ip_portmap_get() to retrieve current port mapping settings.
 4. Added ip_napt_get_stats() for some insight into the state of NAT.
This commit is contained in:
Deomid "rojer" Ryabkov
2022-02-24 23:28:50 +00:00
committed by Abhik Roy
parent f07097d740
commit fadb910946
5 changed files with 389 additions and 164 deletions
+1
View File
@@ -390,6 +390,7 @@ dhcp_check(struct netif *netif)
static void
dhcp_handle_offer(struct netif *netif, struct dhcp_msg *msg_in)
{
u8_t n;
struct dhcp *dhcp = netif_dhcp_data(netif);
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n",
+337 -150
View File
@@ -39,9 +39,11 @@
*
*/
#if ESP_LWIP
#if LWIP_IPV4
#if IP_NAPT
#if ESP_LWIP && LWIP_IPV4 && IP_NAPT
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include "lwip/sys.h"
#include "lwip/ip.h"
@@ -53,31 +55,38 @@
#include "lwip/priv/tcp_priv.h"
#include "lwip/lwip_napt.h"
#include "lwip/ip4_napt.h"
#include "string.h"
#include "assert.h"
#include "lwip/timeouts.h"
#define NO_IDX ((u16_t)-1)
#define NT(x) ((x) == NO_IDX ? NULL : &ip_napt_table[x])
struct napt_table {
#pragma GCC diagnostic push
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic ignored "-Wc90-c99-compat" /* To allow u8_t bit fields */
#endif
struct ip_napt_entry {
u32_t last;
u32_t src;
u32_t dest;
u16_t sport;
u16_t dport;
u16_t mport;
u32_t src; /* net */
u32_t dest; /* net */
u16_t sport; /* net */
u16_t dport; /* net */
u16_t mport; /* net */
u8_t proto;
unsigned int fin1 : 1;
unsigned int fin2 : 1;
unsigned int finack1 : 1;
unsigned int finack2 : 1;
unsigned int synack : 1;
unsigned int rst : 1;
u8_t fin1 : 1;
u8_t fin2 : 1;
u8_t finack1 : 1;
u8_t finack2 : 1;
u8_t synack : 1;
u8_t rst : 1;
u8_t _unused : 2;
u32_t src_seqno; /* host */
u32_t dest_seqno; /* host */
u16_t next, prev;
};
#pragma GCC diagnostic pop
#if IP_NAPT_PORTMAP
struct portmap_table {
struct ip_portmap_entry {
u32_t maddr;
u32_t daddr;
u16_t mport;
@@ -88,51 +97,72 @@ struct portmap_table {
#endif
static u16_t napt_list = NO_IDX, napt_list_last = NO_IDX, napt_free = 0;
static struct napt_table *ip_napt_table = NULL;
#if IP_NAPT_PORTMAP
static struct portmap_table *ip_portmap_table = NULL;
#endif
static int nr_active_napt_tcp = 0, nr_active_napt_udp = 0, nr_active_napt_icmp = 0;
static uint16_t ip_napt_max = 0;
static u8_t ip_napt_max = 0;
#if IP_NAPT_PORTMAP
static uint8_t ip_portmap_max = 0;
#endif
static struct ip_napt_entry *ip_napt_table = NULL;
#if IP_NAPT_PORTMAP
static struct ip_portmap_entry *ip_portmap_table = NULL;
#endif
static struct ip_napt_stats napt_stats;
static void ip_napt_gc(uint32_t now, bool force);
static void ip_napt_tmr(void *arg);
#define WRAPPED_AROUND(a, b) ((((a) ^ (b)) & (1UL << 31)) != 0)
#if NAPT_DEBUG
/* Print NAPT table using LWIP_DEBUGF
*/
/* Print NAPT table using LWIP_DEBUGF */
#define DPRINTF(m) LWIP_DEBUGF(NAPT_DEBUG, m)
/* #define DPRINTF(m) printf m */
static void
napt_debug_print(void)
{
int i, next;
LWIP_DEBUGF(NAPT_DEBUG, ("NAPT table:\n"));
LWIP_DEBUGF(NAPT_DEBUG, (" src dest sport dport mport \n"));
LWIP_DEBUGF(NAPT_DEBUG, ("+-----------------------+-----------------------+-------+-------+-------+\n"));
int i, next, p;
u32_t now = sys_now();
u32_t nr_total = napt_stats.nr_active_tcp + napt_stats.nr_active_udp + napt_stats.nr_active_icmp;
DPRINTF(("NAPT table (%"U16_F"+%"U16_F"+%"U16_F"=%"U32_F" / %"U16_F"):\n",
napt_stats.nr_active_tcp, napt_stats.nr_active_udp, napt_stats.nr_active_icmp, nr_total, napt_stats.max_entries));
if (nr_total == 0) return;
DPRINTF(("+-----------------------+-----------------------+-------+---------+----------+\n"));
DPRINTF(("| src | dest | mport | flags | age |\n"));
DPRINTF(("+-----------------------+-----------------------+-------+---------+----------+\n"));
for (i = napt_list; i != NO_IDX; i = next) {
struct napt_table *t = &ip_napt_table[i];
struct ip_napt_entry *t = &ip_napt_table[i];
next = t->next;
LWIP_DEBUGF(NAPT_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" |",
((const u8_t*) (&t->src))[0],
((const u8_t*) (&t->src))[1],
((const u8_t*) (&t->src))[2],
((const u8_t*) (&t->src))[3]));
DPRINTF(("| %3"U16_F".%3"U16_F".%3"U16_F".%3"U16_F":%5"U16_F" ",
((const u8_t*) (&t->src))[0],
((const u8_t*) (&t->src))[1],
((const u8_t*) (&t->src))[2],
((const u8_t*) (&t->src))[3],
lwip_ntohs(t->sport)));
LWIP_DEBUGF(NAPT_DEBUG, (" %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" |",
((const u8_t*) (&t->dest))[0],
((const u8_t*) (&t->dest))[1],
((const u8_t*) (&t->dest))[2],
((const u8_t*) (&t->dest))[3]));
DPRINTF(("| %3"U16_F".%3"U16_F".%3"U16_F".%3"U16_F":%5"U16_F" ",
((const u8_t*) (&t->dest))[0],
((const u8_t*) (&t->dest))[1],
((const u8_t*) (&t->dest))[2],
((const u8_t*) (&t->dest))[3],
lwip_ntohs(t->dport)));
LWIP_DEBUGF(NAPT_DEBUG, (" %5"U16_F" | %5"U16_F" | %5"U16_F" |\n",
lwip_htons(t->sport),
lwip_htons(t->dport),
lwip_htons(t->mport)));
p = t->proto;
DPRINTF(("| %5"U16_F" | %c%c%c%c%c%c%c | %8"U32_F" |\n",
lwip_ntohs(t->mport),
(p == IP_PROTO_TCP ? 'T' : (p == IP_PROTO_UDP ? 'U' : (p == IP_PROTO_ICMP ? 'I' : '?'))),
(t->fin1 ? 'f' : '.'),
(t->fin2 ? 'F' : '.'),
(t->finack1 ? 'a' : '.'),
(t->finack2 ? 'A' : '.'),
(t->synack ? 'S' : '.'),
(t->rst ? 'R' : '.'),
now - t->last));
}
DPRINTF(("+-----------------------+-----------------------+-------+---------+----------+\n"));
}
#endif /* NAPT_DEBUG */
@@ -144,12 +174,18 @@ napt_debug_print(void)
static void
ip_napt_deinit(void)
{
napt_list = NO_IDX;
napt_stats.max_entries = 0;
#if IP_NAPT_PORTMAP
ip_portmap_max = 0;
#endif
mem_free(ip_napt_table);
ip_napt_table = NULL;
#if IP_NAPT_PORTMAP
mem_free(ip_portmap_table);
ip_portmap_table = NULL;
#endif
ip_napt_table = NULL;
sys_untimeout(ip_napt_tmr, NULL);
}
/**
@@ -171,17 +207,20 @@ ip_napt_init(uint16_t max_nat, uint8_t max_portmap)
#endif
ip_napt_max = max_nat;
ip_napt_table = (struct napt_table*)mem_calloc(ip_napt_max, sizeof(struct napt_table[1]));
ip_napt_table = (struct ip_napt_entry *) mem_calloc(max_nat, sizeof(*ip_napt_table));
#if IP_NAPT_PORTMAP
ip_portmap_table = (struct portmap_table*)mem_calloc(ip_portmap_max, sizeof(struct portmap_table[1]));
ip_portmap_table = (struct ip_portmap_entry *) mem_calloc(max_portmap, sizeof(*ip_portmap_table));
assert(ip_portmap_table != NULL && ip_napt_table != NULL);
#else
assert(ip_napt_table != NULL);
#endif
for (i = 0; i < ip_napt_max - 1; i++)
for (i = 0; i < max_nat - 1; i++)
ip_napt_table[i].next = i + 1;
ip_napt_table[i].next = NO_IDX;
napt_stats.max_entries = max_nat;
sys_timeout(NAPT_TMR_INTERVAL, ip_napt_tmr, NULL);
}
}
@@ -191,17 +230,16 @@ ip_napt_enable(u32_t addr, int enable)
struct netif *netif;
int napt_in_any_netif = 0;
for (netif = netif_list; netif; netif = netif->next) {
if (netif->napt)
if (netif_is_up(netif) && !ip_addr_isany(&netif->ip_addr) && (ip_2_ip4(&netif->ip_addr)->addr) == addr) {
netif->napt = enable;
}
if (netif->napt) {
napt_in_any_netif = 1;
if (netif_is_up(netif) && !ip_addr_isany(&netif->ip_addr) && (ip_2_ip4(&netif->ip_addr)->addr) == addr && enable) {
netif->napt = 1;
ip_napt_init(IP_NAPT_MAX, IP_PORTMAP_MAX);
break;
}
}
if (!enable && !napt_in_any_netif) {
for (netif = netif_list; netif; netif = netif->next)
netif->napt = 0;
if (napt_in_any_netif) {
ip_napt_init(IP_NAPT_MAX, IP_PORTMAP_MAX);
} else {
ip_napt_deinit();
}
}
@@ -282,9 +320,40 @@ checksumadjust(u8_t *chksum, u8_t *optr, int olen, u8_t *nptr, int nlen)
chksum[0]=x/256; chksum[1]=x & 0xFFU;
}
static void
ip_napt_send_rst(u32_t src_be, u16_t sport_be, u32_t dst_be, u16_t dport_be, u32_t seqno_le, u32_t ackno_le)
{
struct pbuf *p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM);
struct tcp_hdr *tcphdr;
struct netif *netif;
ip_addr_t src, dst;
if (p == NULL) return;
tcphdr = (struct tcp_hdr *)p->payload;
tcphdr->src = sport_be;
tcphdr->dest = dport_be;
tcphdr->seqno = lwip_htonl(seqno_le);
tcphdr->ackno = lwip_htonl(ackno_le);
TCPH_HDRLEN_FLAGS_SET(tcphdr, 5, (TCP_RST | TCP_ACK));
tcphdr->wnd = lwip_htons(512);
tcphdr->urgp = 0;
tcphdr->chksum = 0;
ip_addr_set_ip4_u32_val(src, src_be);
ip_addr_set_ip4_u32_val(dst, dst_be);
tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len, &src, &dst);
netif = ip4_route(ip_2_ip4(&dst));
if (netif != NULL) {
err_t res = ip4_output_if(p, ip_2_ip4(&src), ip_2_ip4(&dst), ICMP_TTL, 0, IP_PROTO_TCP, netif);
LWIP_DEBUGF(NAPT_DEBUG, ("SEND RST to %#x:%u from %#x:%u seq %u ack %u res %d\n",
lwip_ntohl(src_be), lwip_ntohs(sport_be), lwip_ntohl(dst_be), lwip_ntohs(dport_be),
seqno_le, ackno_le, res));
}
pbuf_free(p);
}
/* t must be indexed by napt_free */
static void
ip_napt_insert(struct napt_table *t)
ip_napt_insert(struct ip_napt_entry *t)
{
u16_t ti = t - ip_napt_table;
assert(ti == napt_free);
@@ -299,21 +368,22 @@ ip_napt_insert(struct napt_table *t)
#if LWIP_TCP
if (t->proto == IP_PROTO_TCP)
nr_active_napt_tcp++;
napt_stats.nr_active_tcp++;
#endif
#if LWIP_UDP
if (t->proto == IP_PROTO_UDP)
nr_active_napt_udp++;
napt_stats.nr_active_udp++;
#endif
#if LWIP_ICMP
if (t->proto == IP_PROTO_ICMP)
nr_active_napt_icmp++;
napt_stats.nr_active_icmp++;
#endif
LWIP_DEBUGF(NAPT_DEBUG, ("ip_napt_insert(): TCP=%d, UDP=%d, ICMP=%d\n", nr_active_napt_tcp, nr_active_napt_udp, nr_active_napt_icmp));
LWIP_DEBUGF(NAPT_DEBUG, ("ip_napt_insert(): TCP=%d, UDP=%d, ICMP=%d\n",
napt_stats.nr_active_tcp, napt_stats.nr_active_udp, napt_stats.nr_active_icmp));
}
static void
ip_napt_free(struct napt_table *t)
ip_napt_free(struct ip_napt_entry *t)
{
u16_t ti = t - ip_napt_table;
if (ti == napt_list)
@@ -330,20 +400,26 @@ ip_napt_free(struct napt_table *t)
#if LWIP_TCP
if (t->proto == IP_PROTO_TCP)
nr_active_napt_tcp--;
napt_stats.nr_active_tcp--;
#endif
#if LWIP_UDP
if (t->proto == IP_PROTO_UDP)
nr_active_napt_udp--;
napt_stats.nr_active_udp--;
#endif
#if LWIP_ICMP
if (t->proto == IP_PROTO_ICMP)
nr_active_napt_icmp--;
napt_stats.nr_active_icmp--;
#endif
LWIP_DEBUGF(NAPT_DEBUG, ("ip_napt_free\n"));
#if NAPT_DEBUG
napt_debug_print();
#endif
/* Send RST to both sides to let them know connection is being evicted */
if (t->proto == IP_PROTO_TCP && t->synack && !(t->fin1 || t->fin2 || t-> rst)) {
/* Send RST both ways. */
ip_napt_send_rst(t->dest, t->dport, t->src, t->sport, t->dest_seqno, t->src_seqno);
ip_napt_send_rst(t->src, t->sport, t->dest, t->dport, t->src_seqno, t->dest_seqno);
}
}
#if LWIP_TCP
@@ -352,15 +428,16 @@ ip_napt_find_port(u8_t proto, u16_t port)
{
int i, next;
for (i = napt_list; i != NO_IDX; i = next) {
struct napt_table *t = &ip_napt_table[i];
struct ip_napt_entry *t = &ip_napt_table[i];
next = t->next;
if (t->proto == proto && t->mport == port)
return 1;
}
return 0;
}
#if IP_NAPT_PORTMAP
static struct portmap_table *
static struct ip_portmap_entry *
ip_portmap_find(u8_t proto, u16_t mport);
#endif
@@ -419,20 +496,20 @@ ip_napt_new_port(u8_t proto, u16_t port)
}
}
static struct napt_table*
static struct ip_napt_entry*
ip_napt_find(u8_t proto, u32_t addr, u16_t port, u16_t mport, u8_t dest)
{
u16_t i, next;
u32_t now;
struct napt_table *t;
struct ip_napt_entry *t;
LWIP_DEBUGF(NAPT_DEBUG, ("ip_napt_find\n"));
LWIP_DEBUGF(NAPT_DEBUG, ("looking up in table %s: %"U16_F".%"U16_F".%"U16_F".%"U16_F", port: %u, mport: %u\n",
(dest ? "dest" : "src"),
((const u8_t*) (&addr))[0], ((const u8_t*) (&addr))[1],
((const u8_t*) (&addr))[2], ((const u8_t*) (&addr))[3],
PP_HTONS(port),
PP_HTONS(mport)));
(dest ? "dest" : "src"),
((const u8_t*) (&addr))[0], ((const u8_t*) (&addr))[1],
((const u8_t*) (&addr))[2], ((const u8_t*) (&addr))[3],
PP_HTONS(port),
PP_HTONS(mport)));
#if NAPT_DEBUG
napt_debug_print();
#endif
@@ -441,33 +518,12 @@ ip_napt_find(u8_t proto, u32_t addr, u16_t port, u16_t mport, u8_t dest)
for (i = napt_list; i != NO_IDX; i = next) {
t = NT(i);
next = t->next;
#if LWIP_TCP
if (t->proto == IP_PROTO_TCP &&
((((t->finack1 && t->finack2) || !t->synack) &&
now - t->last > IP_NAPT_TIMEOUT_MS_TCP_DISCON) ||
now - t->last > IP_NAPT_TIMEOUT_MS_TCP)) {
ip_napt_free(t);
continue;
}
#endif
#if LWIP_UDP
if (t->proto == IP_PROTO_UDP && now - t->last > IP_NAPT_TIMEOUT_MS_UDP) {
ip_napt_free(t);
continue;
}
#endif
#if LWIP_ICMP
if (t->proto == IP_PROTO_ICMP && now - t->last > IP_NAPT_TIMEOUT_MS_ICMP) {
ip_napt_free(t);
continue;
}
#endif
if (dest == 0 && t->proto == proto && t->src == addr && t->sport == port) {
if (!dest && t->proto == proto && t->src == addr && t->sport == port) {
t->last = now;
LWIP_DEBUGF(NAPT_DEBUG, ("found\n"));
return t;
}
if (dest == 1 && t->proto == proto && t->dest == addr && t->dport == port
if (dest && t->proto == proto && t->dest == addr && t->dport == port
&& t->mport == mport) {
t->last = now;
LWIP_DEBUGF(NAPT_DEBUG, ("found\n"));
@@ -480,9 +536,9 @@ ip_napt_find(u8_t proto, u32_t addr, u16_t port, u16_t mport, u8_t dest)
}
static u16_t
ip_napt_add(u8_t proto, u32_t src, u16_t sport, u32_t dest, u16_t dport)
ip_napt_add(u8_t proto, u32_t src, u16_t sport, u32_t dest, u16_t dport, u32_t seqno)
{
struct napt_table *t = ip_napt_find(proto, src, sport, 0, 0);
struct ip_napt_entry *t = ip_napt_find(proto, src, sport, 0, 0);
if (t) {
t->last = sys_now();
t->dest = dest;
@@ -499,6 +555,10 @@ ip_napt_add(u8_t proto, u32_t src, u16_t sport, u32_t dest, u16_t dport)
return t->mport;
}
t = NT(napt_free);
if (!t) {
ip_napt_gc(sys_now(), true /* make_room */);
t = NT(napt_free);
}
if (t) {
u16_t mport = sport;
#if LWIP_TCP
@@ -517,6 +577,8 @@ ip_napt_add(u8_t proto, u32_t src, u16_t sport, u32_t dest, u16_t dport)
t->mport = mport;
t->proto = proto;
t->fin1 = t->fin2 = t->finack1 = t->finack2 = t->synack = t->rst = 0;
t->src_seqno = ntohl(seqno);
t->dest_seqno = 0;
ip_napt_insert(t);
LWIP_DEBUGF(NAPT_DEBUG, ("ip_napt_add\n"));
@@ -539,7 +601,7 @@ ip_portmap_add(u8_t proto, u32_t maddr, u16_t mport, u32_t daddr, u16_t dport)
dport = PP_HTONS(dport);
for (i = 0; i < ip_portmap_max; i++) {
struct portmap_table *p = &ip_portmap_table[i];
struct ip_portmap_entry *p = &ip_portmap_table[i];
if (p->valid && p->proto == proto && p->mport == mport) {
p->dport = dport;
p->daddr = daddr;
@@ -556,12 +618,12 @@ ip_portmap_add(u8_t proto, u32_t maddr, u16_t mport, u32_t daddr, u16_t dport)
return 0;
}
static struct portmap_table *
static struct ip_portmap_entry *
ip_portmap_find(u8_t proto, u16_t mport)
{
int i;
for (i = 0; i < ip_portmap_max; i++) {
struct portmap_table *p = &ip_portmap_table[i];
struct ip_portmap_entry *p = &ip_portmap_table[i];
if (!p->valid)
return 0;
if (p->proto == proto && p->mport == mport)
@@ -570,12 +632,12 @@ ip_portmap_find(u8_t proto, u16_t mport)
return NULL;
}
static struct portmap_table *
static struct ip_portmap_entry *
ip_portmap_find_dest(u8_t proto, u16_t dport, u32_t daddr)
{
int i;
for (i = 0; i < ip_portmap_max; i++) {
struct portmap_table *p = &ip_portmap_table[i];
struct ip_portmap_entry *p = &ip_portmap_table[i];
if (!p->valid)
return 0;
if (p->proto == proto && p->dport == dport && p->daddr == daddr)
@@ -584,11 +646,23 @@ ip_portmap_find_dest(u8_t proto, u16_t dport, u32_t daddr)
return NULL;
}
u8_t
ip_portmap_get(u8_t proto, u16_t mport, u32_t *maddr, u32_t *daddr, u16_t *dport)
{
struct ip_portmap_entry *m = ip_portmap_find(proto, PP_HTONS(mport));
if (!m)
return 0;
*maddr = m->maddr;
*daddr = m->daddr;
*dport = PP_NTOHS(m->dport);
return 1;
}
u8_t
ip_portmap_remove(u8_t proto, u16_t mport)
{
struct portmap_table *last = &ip_portmap_table[ip_portmap_max - 1];
struct portmap_table *m = ip_portmap_find(proto, PP_HTONS(mport));
struct ip_portmap_entry *last = &ip_portmap_table[ip_portmap_max - 1];
struct ip_portmap_entry *m = ip_portmap_find(proto, PP_HTONS(mport));
if (!m)
return 0;
for (; m != last; m++)
@@ -652,9 +726,12 @@ void
ip_napt_recv(struct pbuf *p, struct ip_hdr *iphdr)
{
#if IP_NAPT_PORTMAP
struct portmap_table *m;
struct ip_portmap_entry *m;
#endif
struct napt_table *t;
struct ip_napt_entry *t;
if (ip_napt_max == 0) return;
#if LWIP_ICMP
/* NAPT for ICMP Echo Request using identifier */
if (IPH_PROTO(iphdr) == IP_PROTO_ICMP) {
@@ -673,18 +750,19 @@ ip_napt_recv(struct pbuf *p, struct ip_hdr *iphdr)
#if LWIP_TCP
if (IPH_PROTO(iphdr) == IP_PROTO_TCP) {
uint32_t seqno, dest_seqno;
struct tcp_hdr *tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4);
LWIP_DEBUGF(NAPT_DEBUG, ("ip_napt_recv\n"));
LWIP_DEBUGF(NAPT_DEBUG, ("src: %"U16_F".%"U16_F".%"U16_F".%"U16_F", dest: %"U16_F".%"U16_F".%"U16_F".%"U16_F", \n",
ip4_addr1_16(&iphdr->src), ip4_addr2_16(&iphdr->src),
ip4_addr3_16(&iphdr->src), ip4_addr4_16(&iphdr->src),
ip4_addr1_16(&iphdr->dest), ip4_addr2_16(&iphdr->dest),
ip4_addr3_16(&iphdr->dest), ip4_addr4_16(&iphdr->dest)));
ip4_addr1_16(&iphdr->src), ip4_addr2_16(&iphdr->src),
ip4_addr3_16(&iphdr->src), ip4_addr4_16(&iphdr->src),
ip4_addr1_16(&iphdr->dest), ip4_addr2_16(&iphdr->dest),
ip4_addr3_16(&iphdr->dest), ip4_addr4_16(&iphdr->dest)));
LWIP_DEBUGF(NAPT_DEBUG, ("sport %u, dport: %u\n",
lwip_htons(tcphdr->src),
lwip_htons(tcphdr->dest)));
lwip_htons(tcphdr->src),
lwip_htons(tcphdr->dest)));
#if IP_NAPT_PORTMAP
m = ip_portmap_find(IP_PROTO_TCP, tcphdr->dest);
@@ -698,23 +776,28 @@ ip_napt_recv(struct pbuf *p, struct ip_hdr *iphdr)
}
#endif
t = ip_napt_find(IP_PROTO_TCP, iphdr->src.addr, tcphdr->src, tcphdr->dest, 1);
if (!t)
return; /* Unknown TCP session; do nothing */
if (!t)
return; /* Unknown TCP session; do nothing */
if (t->sport != tcphdr->dest)
ip_napt_modify_port_tcp(tcphdr, 1, t->sport);
ip_napt_modify_addr_tcp(tcphdr, &iphdr->dest, t->src);
ip_napt_modify_addr(iphdr, &iphdr->dest, t->src);
if (t->sport != tcphdr->dest)
ip_napt_modify_port_tcp(tcphdr, 1, t->sport);
ip_napt_modify_addr_tcp(tcphdr, &iphdr->dest, t->src);
ip_napt_modify_addr(iphdr, &iphdr->dest, t->src);
if ((TCPH_FLAGS(tcphdr) & (TCP_SYN|TCP_ACK)) == (TCP_SYN|TCP_ACK))
t->synack = 1;
if ((TCPH_FLAGS(tcphdr) & TCP_FIN))
t->fin1 = 1;
if (t->fin2 && (TCPH_FLAGS(tcphdr) & TCP_ACK))
t->finack2 = 1; /* FIXME: Currently ignoring ACK seq... */
if (TCPH_FLAGS(tcphdr) & TCP_RST)
t->rst = 1;
return;
if ((TCPH_FLAGS(tcphdr) & (TCP_SYN|TCP_ACK)) == (TCP_SYN|TCP_ACK))
t->synack = 1;
if ((TCPH_FLAGS(tcphdr) & TCP_FIN))
t->fin1 = 1;
if (t->fin2 && (TCPH_FLAGS(tcphdr) & TCP_ACK))
t->finack2 = 1; /* FIXME: Currently ignoring ACK seq... */
if (TCPH_FLAGS(tcphdr) & TCP_RST)
t->rst = 1;
seqno = ntohl(tcphdr->seqno);
dest_seqno = t->dest_seqno;
if (seqno >= dest_seqno || WRAPPED_AROUND(seqno, dest_seqno)) {
t->dest_seqno = seqno + (p->tot_len - IPH_HL(iphdr) * 4 - TCPH_HDRLEN_BYTES(tcphdr));
}
return;
}
#endif /* LWIP_TCP */
@@ -758,7 +841,7 @@ ip_napt_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp, struct
struct icmp_echo_hdr *iecho = (struct icmp_echo_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4);
if (iecho->type == ICMP_ECHO) {
/* register src addr and iecho->id and dest info */
ip_napt_add(IP_PROTO_ICMP, iphdr->src.addr, iecho->id, iphdr->dest.addr, iecho->id);
ip_napt_add(IP_PROTO_ICMP, iphdr->src.addr, iecho->id, iphdr->dest.addr, iecho->id, 0);
ip_napt_modify_addr(iphdr, &iphdr->src, ip_2_ip4(&outp->ip_addr)->addr);
}
@@ -773,7 +856,7 @@ ip_napt_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp, struct
u16_t mport;
#if IP_NAPT_PORTMAP
struct portmap_table *m = ip_portmap_find_dest(IP_PROTO_TCP, tcphdr->src, iphdr->src.addr);
struct ip_portmap_entry *m = ip_portmap_find_dest(IP_PROTO_TCP, tcphdr->src, iphdr->src.addr);
if (m) {
/* packet from port-mapped dest addr/port: rewrite source to this node */
if (m->mport != tcphdr->src)
@@ -787,24 +870,34 @@ ip_napt_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp, struct
PP_NTOHS(tcphdr->src) >= 1024) {
/* Register new TCP session to NAPT */
mport = ip_napt_add(IP_PROTO_TCP, iphdr->src.addr, tcphdr->src,
iphdr->dest.addr, tcphdr->dest);
if (mport == 0)
iphdr->dest.addr, tcphdr->dest, tcphdr->seqno);
if (mport == 0) {
#if LWIP_ICMP
icmp_dest_unreach(p, ICMP_DUR_PORT);
#endif
return ERR_RTE; /* routing err if add entry failed */
}
} else {
struct napt_table *t = ip_napt_find(IP_PROTO_TCP, iphdr->src.addr, tcphdr->src, 0, 0);
uint32_t seqno, src_seqno;
struct ip_napt_entry *t = ip_napt_find(IP_PROTO_TCP, iphdr->src.addr, tcphdr->src, 0, 0);
if (!t || t->dest != iphdr->dest.addr || t->dport != tcphdr->dest) {
#if LWIP_ICMP
icmp_dest_unreach(p, ICMP_DUR_PORT);
icmp_dest_unreach(p, ICMP_DUR_PORT);
#endif
return ERR_RTE; /* Drop unknown TCP session */
}
mport = t->mport;
if ((TCPH_FLAGS(tcphdr) & TCP_FIN))
return ERR_RTE; /* Drop unknown TCP session */
}
mport = t->mport;
if ((TCPH_FLAGS(tcphdr) & TCP_FIN))
t->fin2 = 1;
if (t->fin1 && (TCPH_FLAGS(tcphdr) & TCP_ACK))
if (t->fin1 && (TCPH_FLAGS(tcphdr) & TCP_ACK))
t->finack1 = 1; /* FIXME: Currently ignoring ACK seq... */
if (TCPH_FLAGS(tcphdr) & TCP_RST)
if (TCPH_FLAGS(tcphdr) & TCP_RST)
t->rst = 1;
seqno = ntohl(tcphdr->seqno);
src_seqno = t->src_seqno;
if (seqno >= t->src_seqno || WRAPPED_AROUND(seqno, src_seqno)) {
t->src_seqno = seqno + (p->tot_len - IPH_HL(iphdr) * 4 - TCPH_HDRLEN_BYTES(tcphdr));
}
}
if (mport != tcphdr->src)
@@ -822,7 +915,7 @@ ip_napt_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp, struct
u16_t mport;
#if IP_NAPT_PORTMAP
struct portmap_table *m = ip_portmap_find_dest(IP_PROTO_UDP, udphdr->src, iphdr->src.addr);
struct ip_portmap_entry *m = ip_portmap_find_dest(IP_PROTO_UDP, udphdr->src, iphdr->src.addr);
if (m) {
/* packet from port-mapped dest addr/port: rewrite source to this node */
if (m->mport != udphdr->src)
@@ -836,11 +929,11 @@ ip_napt_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp, struct
if (PP_NTOHS(udphdr->src) >= 1024) {
/* Register new UDP session */
mport = ip_napt_add(IP_PROTO_UDP, iphdr->src.addr, udphdr->src,
iphdr->dest.addr, udphdr->dest);
iphdr->dest.addr, udphdr->dest, 0);
if (mport == 0)
return ERR_RTE; /* routing err if add entry failed */
} else {
struct napt_table *t = ip_napt_find(IP_PROTO_UDP, iphdr->src.addr, udphdr->src, 0, 0);
struct ip_napt_entry *t = ip_napt_find(IP_PROTO_UDP, iphdr->src.addr, udphdr->src, 0, 0);
if (!t || t->dest != iphdr->dest.addr || t->dport != udphdr->dest) {
#if LWIP_ICMP
icmp_dest_unreach(p, ICMP_DUR_PORT);
@@ -860,6 +953,100 @@ ip_napt_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp, struct
return ERR_OK;
}
#endif /* IP_NAPT */
#endif /* LWIP_IPV4 */
#endif /* ESP_LWIP */
static void
ip_napt_gc(uint32_t now, bool force)
{
u16_t i, next, oldest = NO_IDX;
u32_t age = 0, oldest_age = 0;
int checked = 0, evicted = 0, forced = 0;
for (i = napt_list; i != NO_IDX; i = next) {
struct ip_napt_entry *t = &ip_napt_table[i];
checked++;
next = t->next;
age = now - t->last;
if (age > oldest_age) {
oldest = i;
oldest_age = age;
}
#if LWIP_TCP
if (t->proto == IP_PROTO_TCP) {
if (age > IP_NAPT_TIMEOUT_MS_TCP_DISCON) {
if ((t->finack1 || t->finack2 || !t->synack || t->rst) ||
age > IP_NAPT_TIMEOUT_MS_TCP) {
ip_napt_free(t);
evicted++;
if (force) break;
}
}
continue;
}
#endif
#if LWIP_UDP
if (t->proto == IP_PROTO_UDP) {
if (age > IP_NAPT_TIMEOUT_MS_UDP) {
ip_napt_free(t);
evicted++;
if (force) break;
}
continue;
}
#endif
#if LWIP_ICMP
if (t->proto == IP_PROTO_ICMP) {
if (age > IP_NAPT_TIMEOUT_MS_ICMP) {
ip_napt_free(t);
evicted++;
if (force) break;
}
continue;
}
#endif
}
if (napt_free == NO_IDX && force && oldest != NO_IDX) {
ip_napt_free(&ip_napt_table[oldest]);
evicted++;
forced++;
napt_stats.nr_forced_evictions++;
}
LWIP_DEBUGF(NAPT_DEBUG, ("ip_napt_gc(%d): chk %d evict %d (forced %d), oldest %u\n",
force, checked, evicted, forced, oldest_age));
}
static void
ip_napt_maint(void)
{
static uint32_t s_last_now = 0;
uint32_t now;
if (napt_list == NO_IDX) return;
now = sys_now();
/* Check for timestamp wraparound (happens every ~49.7 days). */
if (WRAPPED_AROUND(s_last_now, now)) {
u16_t i;
struct ip_napt_entry *t;
for (i = napt_list; i != NO_IDX; i = t->next) {
t = &ip_napt_table[i];
/* It's a very simplistic way of dealing with it
* but it's fine for our purposes. */
t->last = now;
}
/* Skip until next tick, nothing to be done here anyway. */
return;
}
ip_napt_gc(now, false /* make_room */);
s_last_now = now;
}
static void
ip_napt_tmr(void *arg) {
ip_napt_maint();
sys_timeout(NAPT_TMR_INTERVAL, ip_napt_tmr, arg);
}
void
ip_napt_get_stats(struct ip_napt_stats *stats)
{
*stats = napt_stats;
}
#endif /* ESP_LWIP && LWIP_IPV4 && IP_NAPT */
+14 -9
View File
@@ -60,16 +60,21 @@ extern "C" {
#include "lwip/err.h"
#include "lwip/ip4.h"
#ifndef NAPT_TMR_INTERVAL
#define NAPT_TMR_INTERVAL 2000
#endif
/**
* NAPT for a forwarded packet. It checks weather we need NAPT and modify
* the packet source address and port if needed.
*
* @param p the packet to forward (p->payload points to IP header)
* @param iphdr the IP header of the input packet
* @param inp the netif on which this packet was received
* @param outp the netif on which this packet will be sent
* @return ERR_OK if packet should be sent, or ERR_RTE if it should be dropped
*/
* NAPT for a forwarded packet. It checks weather we need NAPT and modify
* the packet source address and port if needed.
*
* @param p the packet to forward (p->payload points to IP header)
* @param iphdr the IP header of the input packet
* @param inp the netif on which this packet was received
* @param outp the netif on which this packet will be sent
* @return ERR_OK if packet should be sent, or ERR_RTE if it should be dropped
*/
err_t
ip_napt_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp, struct netif *outp);
+36 -5
View File
@@ -60,13 +60,24 @@ extern "C" {
#endif
/* Timeouts in sec for the various protocol types */
#ifndef IP_NAPT_TIMEOUT_MS_TCP
#define IP_NAPT_TIMEOUT_MS_TCP (30*60*1000)
#define IP_NAPT_TIMEOUT_MS_TCP_DISCON (20*1000)
#endif
#ifndef IP_NAPT_TIMEOUT_MS_TCP_DISCON
#define IP_NAPT_TIMEOUT_MS_TCP_DISCON (TCP_MSL)
#endif
#ifndef IP_NAPT_TIMEOUT_MS_UDP
#define IP_NAPT_TIMEOUT_MS_UDP (2*1000)
#endif
#ifndef IP_NAPT_TIMEOUT_MS_ICMP
#define IP_NAPT_TIMEOUT_MS_ICMP (2*1000)
#endif
#ifndef IP_NAPT_PORT_RANGE_START
#define IP_NAPT_PORT_RANGE_START 49152
#endif
#ifndef IP_NAPT_PORT_RANGE_END
#define IP_NAPT_PORT_RANGE_END 61439
#endif
/**
@@ -82,13 +93,12 @@ ip_napt_enable(u32_t addr, int enable);
/**
* Enable/Disable NAPT for a specified interface.
*
* @param number netif number of the interface
* @param number number of the interface
* @param enable non-zero to enable NAPT, or 0 to disable.
*/
void
ip_napt_enable_no(u8_t number, int enable);
/**
* Enable/Disable NAPT for a specified interface.
*
@@ -117,16 +127,37 @@ ip_napt_enable_netif(struct netif *netif, int enable);
u8_t
ip_portmap_add(u8_t proto, u32_t maddr, u16_t mport, u32_t daddr, u16_t dport);
u8_t
ip_portmap_get(u8_t proto, u16_t mport, u32_t *maddr, u32_t *daddr, u16_t *dport);
/**
* Unregister port mapping on the external interface to internal interface.
*
* @param proto target protocol
* @param mport mapped port
* @param mport mapped port on the external interface, in host byte order.
*/
u8_t
ip_portmap_remove(u8_t proto, u16_t mport);
struct ip_napt_stats {
u16_t max_entries;
u16_t nr_active_tcp;
u16_t nr_active_udp;
u16_t nr_active_icmp;
u16_t nr_forced_evictions;
};
/**
* Get statistics.
*
* @param stats struct to receive current stats
*/
void
ip_napt_get_stats(struct ip_napt_stats *stats);
#endif /* IP_NAPT */
#endif /* IP_FORWARD */
#endif /* ESP_LWIP */
+1
View File
@@ -474,6 +474,7 @@ END_TEST
START_TEST(test_ip4_route_netif_max_napt)
{
#define TCP_PORT 2222
int i;
packet_type_t packet_type = PACKET_PBUF_RAM;
ip4_addr_t addr, src_addr, sta_addr;
ip4_addr_t netmask;