From 3462f21a38337022a57e7427f97168300e72e4f6 Mon Sep 17 00:00:00 2001 From: "Deomid \"rojer\" Ryabkov" Date: Thu, 24 Feb 2022 23:28:50 +0000 Subject: [PATCH] napt: Fixes and improvements (2.2.0) 2.1.3-esp: fadb9109 napt: Fixes and improvements (2.1.3) 2.1.3-esp: 5c4f899f napt: restore to the initial state in deinit 2.1.3-esp: d65ad241 napt: Fix ip_portmap_add() to keep only one port mapping 2.1.3-esp: 7033e26f napt: Fix clean compilation 2.1.3-esp: bc78df87 napt: Fix IP forwarding when forward netif enable NAPT 2.1.3-esp: b55e64ae napt/stats: Move some napt counters to stats module 2.1.3-esp: a7e0a50c ip_napt_maint: Fix timestamp overflow handling (2.1.3) 2.1.3-esp: partially f5c43549 lwip_debug: Fixed string format error in ip6 and napt --- src/core/ipv4/ip4.c | 7 +- src/core/ipv4/ip4_napt.c | 517 +++++++++++++++++++++++++---------- src/core/stats.c | 12 + src/include/lwip/ip4_napt.h | 23 +- src/include/lwip/lwip_napt.h | 35 ++- src/include/lwip/opt.h | 9 + src/include/lwip/stats.h | 30 ++ 7 files changed, 465 insertions(+), 168 deletions(-) diff --git a/src/core/ipv4/ip4.c b/src/core/ipv4/ip4.c index 7e4a9b25..863df55d 100644 --- a/src/core/ipv4/ip4.c +++ b/src/core/ipv4/ip4.c @@ -332,8 +332,11 @@ ip4_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) #if ESP_LWIP #if IP_NAPT - if (ip_napt_forward(p, iphdr, inp, netif) != ERR_OK) - return; + /* If the output netif uses NAPT, we will not perform NAPT forwarding (because NAPT netif will not search the NAPT table on ip4_input) */ + if (!netif->napt) { + if (ip_napt_forward(p, iphdr, inp, netif) != ERR_OK) + return; + } #endif #endif /* ESP_LWIP */ diff --git a/src/core/ipv4/ip4_napt.c b/src/core/ipv4/ip4_napt.c index a907c0a8..c02b599e 100644 --- a/src/core/ipv4/ip4_napt.c +++ b/src/core/ipv4/ip4_napt.c @@ -39,9 +39,11 @@ * */ -#if ESP_LWIP -#if LWIP_IPV4 -#if IP_NAPT +#if ESP_LWIP && LWIP_IPV4 && IP_NAPT + +#include +#include +#include #include "lwip/sys.h" #include "lwip/ip.h" @@ -53,31 +55,39 @@ #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" +#include "lwip/stats.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 +98,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 u16_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 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(); +#if LWIP_STATS + u32_t nr_total = STATS_GET(ip_napt.nr_active_tcp) + STATS_GET(ip_napt.nr_active_udp) + STATS_GET(ip_napt.nr_active_icmp); + DPRINTF(("NAPT table (%"STAT_COUNTER_F"+%"STAT_COUNTER_F"+%"STAT_COUNTER_F"=%"U32_F" / %"U16_F"):\n", + STATS_GET(ip_napt.nr_active_tcp), STATS_GET(ip_napt.nr_active_udp), STATS_GET(ip_napt.nr_active_icmp), nr_total, ip_napt_max)); + if (nr_total == 0) return; +#endif + 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 +175,20 @@ napt_debug_print(void) static void ip_napt_deinit(void) { + napt_list = NO_IDX; + napt_list_last = NO_IDX; + napt_free = 0; + ip_napt_max = 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); } /** @@ -159,7 +198,11 @@ ip_napt_deinit(void) * @param max_portmap max number of enties in the NAPT table (use IP_PORTMAP_MAX if in doubt) */ static void +#if IP_NAPT_PORTMAP ip_napt_init(uint16_t max_nat, uint8_t max_portmap) +#else +ip_napt_init(uint16_t max_nat) +#endif { u16_t i; @@ -171,17 +214,19 @@ 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; + + sys_timeout(NAPT_TMR_INTERVAL, ip_napt_tmr, NULL); } } @@ -191,17 +236,20 @@ 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) { +#if IP_NAPT_PORTMAP + ip_napt_init(IP_NAPT_MAX, IP_PORTMAP_MAX); +#else + ip_napt_init(IP_NAPT_MAX); +#endif + } else { ip_napt_deinit(); } } @@ -214,7 +262,11 @@ ip_napt_enable_no(u8_t number, int enable) if (netif->num == number) { netif->napt = !!enable; if (enable) +#if IP_NAPT_PORTMAP ip_napt_init(IP_NAPT_MAX, IP_PORTMAP_MAX); +#else + ip_napt_init(IP_NAPT_MAX); +#endif else ip_napt_deinit(); break; @@ -234,7 +286,11 @@ ip_napt_enable_netif(struct netif *netif, int enable) if (!netif->napt && enable) { /* Enable napt */ netif->napt = 1; +#if IP_NAPT_PORTMAP ip_napt_init(IP_NAPT_MAX, IP_PORTMAP_MAX); +#else + ip_napt_init(IP_NAPT_MAX); +#endif } else if (netif->napt && !enable) { /* Disable napt */ @@ -282,9 +338,41 @@ 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_UNUSED_ARG(res); /* might be unused if debugging off */ + LWIP_DEBUGF(NAPT_DEBUG, ("SEND RST to %#"X32_F":%"U16_F" from %#"X32_F":%"U16_F" seq %"U32_F" ack %"U32_F" 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); @@ -297,23 +385,26 @@ ip_napt_insert(struct napt_table *t) if (napt_list_last == NO_IDX) napt_list_last = ti; +#if LWIP_STATS #if LWIP_TCP if (t->proto == IP_PROTO_TCP) - nr_active_napt_tcp++; + STATS_INC(ip_napt.nr_active_tcp); #endif #if LWIP_UDP if (t->proto == IP_PROTO_UDP) - nr_active_napt_udp++; + STATS_INC(ip_napt.nr_active_udp); #endif #if LWIP_ICMP if (t->proto == IP_PROTO_ICMP) - nr_active_napt_icmp++; + STATS_INC(ip_napt.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=%"STAT_COUNTER_F", UDP=%"STAT_COUNTER_F", ICMP=%"STAT_COUNTER_F"\n", + STATS_GET(ip_napt.nr_active_tcp), STATS_GET(ip_napt.nr_active_udp), STATS_GET(ip_napt.nr_active_icmp))); +#endif /* LWIP_STATS */ } 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) @@ -328,22 +419,31 @@ ip_napt_free(struct napt_table *t) t->next = napt_free; napt_free = ti; +#if LWIP_STATS #if LWIP_TCP if (t->proto == IP_PROTO_TCP) - nr_active_napt_tcp--; + STATS_DEC(ip_napt.nr_active_tcp); #endif #if LWIP_UDP if (t->proto == IP_PROTO_UDP) - nr_active_napt_udp--; + STATS_DEC(ip_napt.nr_active_udp); #endif #if LWIP_ICMP if (t->proto == IP_PROTO_ICMP) - nr_active_napt_icmp--; + STATS_DEC(ip_napt.nr_active_icmp); #endif +#endif /* LWIP_STATS */ + 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 +452,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 +520,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))); + LWIP_DEBUGF(NAPT_DEBUG, ("looking up in table %s: %"U16_F".%"U16_F".%"U16_F".%"U16_F", port: %"U16_F", mport: %"U16_F"\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))); #if NAPT_DEBUG napt_debug_print(); #endif @@ -441,33 +542,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 +560,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 +579,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 +601,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,10 +625,11 @@ 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; + p->maddr = maddr; } else if (!p->valid) { p->maddr = maddr; p->daddr = daddr; @@ -556,12 +643,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 +657,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 +671,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 +751,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 +775,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_DEBUGF(NAPT_DEBUG, ("sport %"U16_F", dport: %"U16_F"\n", + lwip_htons(tcphdr->src), + lwip_htons(tcphdr->dest))); #if IP_NAPT_PORTMAP m = ip_portmap_find(IP_PROTO_TCP, tcphdr->dest); @@ -698,23 +801,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 +866,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 +881,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 +895,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 +940,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 +954,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 +978,101 @@ 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++; + STATS_INC(ip_napt.nr_forced_evictions); + } + LWIP_DEBUGF(NAPT_DEBUG, ("ip_napt_gc(%d): chk %d evict %d (forced %d), oldest %"U32_F"\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; + } + } + 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); +} + +#if LWIP_STATS +void +ip_napt_get_stats(struct stats_ip_napt *stats) +{ + *stats = STATS_GET(ip_napt); +} +#endif /* LWIP_STATS */ + +#endif /* ESP_LWIP && LWIP_IPV4 && IP_NAPT */ diff --git a/src/core/stats.c b/src/core/stats.c index 95445ec9..53f759c1 100644 --- a/src/core/stats.c +++ b/src/core/stats.c @@ -139,6 +139,17 @@ stats_display_sys(struct stats_sys *sys) } #endif /* SYS_STATS */ +#if IP_NAPT_STATS +void stats_display_ip_napt(struct stats_ip_napt *napt) +{ + LWIP_PLATFORM_DIAG(("\nIP NAPT\n\t")); + LWIP_PLATFORM_DIAG(("nr_active_tcp: %"STAT_COUNTER_F"\n\t", napt->nr_active_tcp)); + LWIP_PLATFORM_DIAG(("nr_active_udp: %"STAT_COUNTER_F"\n\t", napt->nr_active_udp)); + LWIP_PLATFORM_DIAG(("nr_active_icmp: %"STAT_COUNTER_F"\n\t", napt->nr_active_icmp)); + LWIP_PLATFORM_DIAG(("nr_forced_evictions: %"STAT_COUNTER_F"\n\t", napt->nr_forced_evictions)); +} +#endif /* IP_NAPT_STATS */ + void stats_display(void) { @@ -162,6 +173,7 @@ stats_display(void) MEMP_STATS_DISPLAY(i); } SYS_STATS_DISPLAY(); + IP_NAPT_STATS_DISPLAY(); } #endif /* LWIP_STATS_DISPLAY */ diff --git a/src/include/lwip/ip4_napt.h b/src/include/lwip/ip4_napt.h index 41b0fc68..8246d6fd 100644 --- a/src/include/lwip/ip4_napt.h +++ b/src/include/lwip/ip4_napt.h @@ -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); diff --git a/src/include/lwip/lwip_napt.h b/src/include/lwip/lwip_napt.h index 836317f1..a3d27955 100644 --- a/src/include/lwip/lwip_napt.h +++ b/src/include/lwip/lwip_napt.h @@ -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,31 @@ 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); + + +#if LWIP_STATS +/** + * Get statistics. + * + * @param stats struct to receive current stats + */ +void +ip_napt_get_stats(struct stats_ip_napt *stats); +#endif + #endif /* IP_NAPT */ #endif /* IP_FORWARD */ #endif /* ESP_LWIP */ diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h index 31d9e994..733e0ce3 100644 --- a/src/include/lwip/opt.h +++ b/src/include/lwip/opt.h @@ -2305,6 +2305,14 @@ #define MIB2_STATS 0 #endif +/** + * IP_NAPT_STATS==1: Stats for IP NAPT. + */ +#if !defined IP_NAPT_STATS || defined __DOXYGEN__ +#define IP_NAPT_STATS (IP_NAPT) +#endif + + #else #define LINK_STATS 0 @@ -2325,6 +2333,7 @@ #define MLD6_STATS 0 #define ND6_STATS 0 #define MIB2_STATS 0 +#define IP_NAPT_STATS 0 #endif /* LWIP_STATS */ /** diff --git a/src/include/lwip/stats.h b/src/include/lwip/stats.h index b570dbac..94e16691 100644 --- a/src/include/lwip/stats.h +++ b/src/include/lwip/stats.h @@ -228,6 +228,18 @@ struct stats_mib2_netif_ctrs { u32_t ifouterrors; }; +#if ESP_LWIP && IP_NAPT_STATS +/** + * IP NAPT stats + */ +struct stats_ip_napt { + STAT_COUNTER nr_active_tcp; + STAT_COUNTER nr_active_udp; + STAT_COUNTER nr_active_icmp; + STAT_COUNTER nr_forced_evictions; +}; +#endif /* ESP_LWIP && IP_NAPT */ + /** lwIP stats container */ struct stats_ { #if LINK_STATS @@ -298,6 +310,11 @@ struct stats_ { /** SNMP MIB2 */ struct stats_mib2 mib2; #endif +#if ESP_LWIP && IP_NAPT_STATS + /** IP NAPT */ + struct stats_ip_napt ip_napt; +#endif + }; /** Global variable containing lwIP internal statistics. Add this to your debugger's watchlist. */ @@ -467,6 +484,19 @@ void stats_init(void); #define MIB2_STATS_INC(x) #endif +#if IP_NAPT_STATS +#define IP_NAPT_STATS_INC(x) STATS_INC(x) +#else +#define IP_NAPT_STATS_INC(x) +#endif + +#if LWIP_STATS_DISPLAY && IP_NAPT_STATS +void stats_display_ip_napt(struct stats_ip_napt *napt); +#define IP_NAPT_STATS_DISPLAY() stats_display_ip_napt(&lwip_stats.ip_napt) +#else +#define IP_NAPT_STATS_DISPLAY() +#endif + /* Display of statistics */ #if LWIP_STATS_DISPLAY void stats_display(void);