diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 62cd52fd..1c78f835 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -23,10 +23,16 @@ jobs: unzip -oq ${CONTRIB}.zip patch -s -p0 < test/${CONTRIB}.patch - - name: Build unit tests with make - run: LWIPDIR=../../../../src make -C ${CONTRIB}/ports/unix/check - - name: Run unit tests - run: LWIPDIR=../../../../src make -C ${CONTRIB}/ports/unix/check check + - name: Build and Run unit tests with make + run: | + export LWIPDIR=../../../../src && cd ${CONTRIB}/ports/unix/check + make -j 4 check + make clean + export EXTRA_CFLAGS="-DIP_FORWARD=1" && export CC="cc $EXTRA_CFLAGS" + make -j 4 check + make clean + export EXTRA_CFLAGS="-DIP_FORWARD=1 -DIP_NAPT=1 -DLWIP_ARCH_CC_H -include cc_esp_platform.h" && export CC="cc $EXTRA_CFLAGS" + make -j 4 check - name: Run cmake run: mkdir build && cd build && cmake .. -G Ninja diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 88866e95..df8fe987 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -39,7 +39,21 @@ run_unittests: dependencies: [] script: - *get_contrib - - LWIPDIR=../../../../src make -C ${CONTRIB}/ports/unix/check check + - export LWIPDIR=../../../../src && cd ${CONTRIB}/ports/unix/check + # build and run default lwip tests + - make -j 4 check + # retest with IP_FORWARD enabled + - make clean + - export EXTRA_CFLAGS="-DIP_FORWARD=1" && export CC="cc $EXTRA_CFLAGS" + - make -j 4 check + # retest with IP_FORWARD and IP_NAPT enabled + - make clean + - export EXTRA_CFLAGS="-DIP_FORWARD=1 -DIP_NAPT=1 -DLWIP_ARCH_CC_H -include cc_esp_platform.h" && export CC="cc $EXTRA_CFLAGS" + - make -j 4 check + # Please uncomment the below to test IP_FORWARD/IP_NAPT tests with debug output (only ip4_route test suite will be executed) + #- make clean + #- export EXTRA_CFLAGS="-DIP_FORWARD=1 -DESP_TEST_DEBUG=1 -DIP_NAPT=1 -DLWIP_ARCH_CC_H -include cc_esp_platform.h" && export CC="cc $EXTRA_CFLAGS" + #- make -j 4 check run_unittests_cmake: stage: host_test diff --git a/src/Filelists.cmake b/src/Filelists.cmake index bb6f2b3a..7cbac9a7 100644 --- a/src/Filelists.cmake +++ b/src/Filelists.cmake @@ -61,6 +61,7 @@ set(lwipcore4_SRCS ${LWIP_DIR}/src/core/ipv4/igmp.c ${LWIP_DIR}/src/core/ipv4/ip4_frag.c ${LWIP_DIR}/src/core/ipv4/ip4.c + ${LWIP_DIR}/src/core/ipv4/ip4_napt.c ${LWIP_DIR}/src/core/ipv4/ip4_addr.c ) set(lwipcore6_SRCS diff --git a/src/Filelists.mk b/src/Filelists.mk index 828b9f2a..88e8baab 100644 --- a/src/Filelists.mk +++ b/src/Filelists.mk @@ -58,6 +58,7 @@ CORE4FILES=$(LWIPDIR)/core/ipv4/autoip.c \ $(LWIPDIR)/core/ipv4/igmp.c \ $(LWIPDIR)/core/ipv4/ip4_frag.c \ $(LWIPDIR)/core/ipv4/ip4.c \ + $(LWIPDIR)/core/ipv4/ip4_napt.c \ $(LWIPDIR)/core/ipv4/ip4_addr.c CORE6FILES=$(LWIPDIR)/core/ipv6/dhcp6.c \ diff --git a/src/core/init.c b/src/core/init.c index 3620e1d4..177a05d8 100644 --- a/src/core/init.c +++ b/src/core/init.c @@ -241,6 +241,8 @@ PACK_STRUCT_END #error "For LWIP_NETCONN_FULLDUPLEX to work, LWIP_NETCONN_SEM_PER_THREAD is required" #endif +#if LWIP_SOCKET +#endif /* LWIP_SOCKET */ /* Compile-time checks for deprecated options. */ diff --git a/src/core/ipv4/ip4.c b/src/core/ipv4/ip4.c index 26c26a91..0cf58903 100644 --- a/src/core/ipv4/ip4.c +++ b/src/core/ipv4/ip4.c @@ -56,6 +56,7 @@ #include "lwip/autoip.h" #include "lwip/stats.h" #include "lwip/prot/iana.h" +#include "lwip/ip4_napt.h" #include @@ -329,6 +330,13 @@ ip4_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) return; } +#if ESP_LWIP +#if IP_NAPT + if (ip_napt_forward(p, iphdr, inp, netif) != ERR_OK) + return; +#endif +#endif /* ESP_LWIP */ + /* Incrementally update the IP checksum. */ if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffffU - 0x100)) { IPH_CHKSUM_SET(iphdr, (u16_t)(IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1)); @@ -425,7 +433,11 @@ ip4_input_accept(struct netif *netif) err_t ip4_input(struct pbuf *p, struct netif *inp) { +#if ESP_LWIP && IP_NAPT + struct ip_hdr *iphdr; +#else const struct ip_hdr *iphdr; +#endif /* IP_NAPT && ESP_LWIP */ struct netif *netif; u16_t iphdr_hlen; u16_t iphdr_len; @@ -511,6 +523,14 @@ ip4_input(struct pbuf *p, struct netif *inp) } #endif +#if ESP_LWIP +#if IP_NAPT + /* for unicast packet, check NAPT table and modify dest if needed */ + if (!inp->napt && ip4_addr_cmp(&iphdr->dest, netif_ip4_addr(inp))) + ip_napt_recv(p, iphdr); +#endif +#endif /* ESP_LWIP */ + /* copy IP addresses to aligned ip_addr_t */ ip_addr_copy_from_ip4(ip_data.current_iphdr_dest, iphdr->dest); ip_addr_copy_from_ip4(ip_data.current_iphdr_src, iphdr->src); @@ -644,7 +664,11 @@ ip4_input(struct pbuf *p, struct netif *inp) if (p == NULL) { return ERR_OK; } +#if ESP_LWIP && IP_NAPT + iphdr = (struct ip_hdr *)p->payload; +#else iphdr = (const struct ip_hdr *)p->payload; +#endif #else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */ pbuf_free(p); LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n", @@ -981,6 +1005,10 @@ ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *d ip4_debug_print(p); #if ENABLE_LOOPBACK +#if ESP_LWIP && IP_NAPT + /* doesn't work for external wifi interfaces */ +#else + if (ip4_addr_cmp(dest, netif_ip4_addr(netif)) #if !LWIP_HAVE_LOOPIF || ip4_addr_isloopback(dest) @@ -995,6 +1023,7 @@ ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *d netif_loop_output(netif, p); } #endif /* LWIP_MULTICAST_TX_OPTIONS */ +#endif /* ESP_LWIP && IP_NAPT */ #endif /* ENABLE_LOOPBACK */ #if IP_FRAG /* don't fragment if interface has mtu set to 0 [loopif] */ diff --git a/src/core/ipv4/ip4_napt.c b/src/core/ipv4/ip4_napt.c new file mode 100644 index 00000000..68b07958 --- /dev/null +++ b/src/core/ipv4/ip4_napt.c @@ -0,0 +1,800 @@ +/** + * @file + * This is the NAPT implementation on IPv4 layer + * + * @see ip4.c + * + */ +#include "lwip/opt.h" + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * original reassembly code by Adam Dunkels + * + */ + +#if ESP_LWIP +#if LWIP_IPV4 +#if IP_NAPT + +#include "lwip/sys.h" +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/udp.h" +#include "lwip/priv/tcp_priv.h" +#include "lwip/lwip_napt.h" +#include "lwip/ip4_napt.h" +#include "string.h" +#include "assert.h" + +#define NO_IDX ((u16_t)-1) +#define NT(x) ((x) == NO_IDX ? NULL : &ip_napt_table[x]) + +struct napt_table { + u32_t last; + u32_t src; + u32_t dest; + u16_t sport; + u16_t dport; + u16_t mport; + 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; + u16_t next, prev; +}; + +struct portmap_table { + u32_t maddr; + u32_t daddr; + u16_t mport; + u16_t dport; + u8_t proto; + u8_t valid; +}; + +static u16_t napt_list = NO_IDX, napt_list_last = NO_IDX, napt_free = 0; + +static struct napt_table *ip_napt_table = NULL; +static struct portmap_table *ip_portmap_table = NULL; + +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 uint8_t ip_portmap_max = 0; + +#if NAPT_DEBUG +/* Print NAPT table using LWIP_DEBUGF +*/ +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")); + + for (i = napt_list; i != NO_IDX; i = next) { + struct napt_table *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])); + + 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])); + + 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))); + + } +} +#endif /* NAPT_DEBUG */ + +/** + * Deallocates the NAPT tables. + * + */ +static void +ip_napt_deinit(void) +{ + mem_free(ip_napt_table); + mem_free(ip_portmap_table); + ip_portmap_table = NULL; + ip_napt_table = NULL; +} + +/** + * Allocates and initializes the NAPT tables. + * + * @param max_nat max number of enties in the NAPT table (use IP_NAPT_MAX if in doubt) + * @param max_portmap max number of enties in the NAPT table (use IP_PORTMAP_MAX if in doubt) + */ +static void +ip_napt_init(uint16_t max_nat, uint8_t max_portmap) +{ + u16_t i; + + if (ip_portmap_table == NULL && ip_napt_table == NULL) { + ip_napt_max = max_nat; + ip_portmap_max = max_portmap; + + ip_napt_table = (struct napt_table*)mem_calloc(ip_napt_max, sizeof(struct napt_table[1])); + ip_portmap_table = (struct portmap_table*)mem_calloc(ip_portmap_max, sizeof(struct portmap_table[1])); + assert(ip_portmap_table != NULL && ip_napt_table != NULL); + + for (i = 0; i < ip_napt_max - 1; i++) + ip_napt_table[i].next = i + 1; + ip_napt_table[i].next = NO_IDX; + } +} + +void +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) + 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; + ip_napt_deinit(); + } +} + +void +ip_napt_enable_no(u8_t number, int enable) +{ + struct netif *netif; + for (netif = netif_list; netif; netif = netif->next) { + if (netif->num == number) { + netif->napt = !!enable; + if (enable) + ip_napt_init(IP_NAPT_MAX, IP_PORTMAP_MAX); + else + ip_napt_deinit(); + break; + } + } +} + +/* adjusts checksum in a packet +- chksum points to the chksum in the packet +- optr points to the old data in the packet (before) +- nptr points to the new data in the packet (after) +*/ +static void +checksumadjust(u8_t *chksum, u8_t *optr, int olen, u8_t *nptr, int nlen) +{ + s32_t x, before, after; + x=chksum[0]*256+chksum[1]; + x=~x & 0xFFFFU; + while (olen) + { + before=optr[0]*256+optr[1]; optr+=2; + x-=before & 0xFFFFU; + if (x<=0) { x--; x&=0xFFFFU; } + olen-=2; + } + while (nlen) + { + after=nptr[0]*256+nptr[1]; nptr+=2; + x+=after & 0xFFFFU; + if (x & 0x10000U) { x++; x&=0xFFFFU; } + nlen-=2; + } + x=~x & 0xFFFFU; + chksum[0]=x/256; chksum[1]=x & 0xFFU; +} + +/* t must be indexed by napt_free */ +static void +ip_napt_insert(struct napt_table *t) +{ + u16_t ti = t - ip_napt_table; + assert(ti == napt_free); + napt_free = t->next; + t->prev = NO_IDX; + t->next = napt_list; + if (napt_list != NO_IDX) + NT(napt_list)->prev = ti; + napt_list = ti; + if (napt_list_last == NO_IDX) + napt_list_last = ti; + +#if LWIP_TCP + if (t->proto == IP_PROTO_TCP) + nr_active_napt_tcp++; +#endif +#if LWIP_UDP + if (t->proto == IP_PROTO_UDP) + nr_active_napt_udp++; +#endif +#if LWIP_ICMP + if (t->proto == IP_PROTO_ICMP) + nr_active_napt_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)); +} + +static void +ip_napt_free(struct napt_table *t) +{ + u16_t ti = t - ip_napt_table; + if (ti == napt_list) + napt_list = t->next; + if (ti == napt_list_last) + napt_list_last = t->prev; + if (t->next != NO_IDX) + NT(t->next)->prev = t->prev; + if (t->prev != NO_IDX) + NT(t->prev)->next = t->next; + t->prev = NO_IDX; + t->next = napt_free; + napt_free = ti; + +#if LWIP_TCP + if (t->proto == IP_PROTO_TCP) + nr_active_napt_tcp--; +#endif +#if LWIP_UDP + if (t->proto == IP_PROTO_UDP) + nr_active_napt_udp--; +#endif +#if LWIP_ICMP + if (t->proto == IP_PROTO_ICMP) + nr_active_napt_icmp--; +#endif + LWIP_DEBUGF(NAPT_DEBUG, ("ip_napt_free\n")); +#if NAPT_DEBUG + napt_debug_print(); +#endif +} + +#if LWIP_TCP +static u8_t +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]; + next = t->next; + if (t->proto == proto && t->mport == port) + return 1; + } + return 0; +} + +static struct portmap_table * +ip_portmap_find(u8_t proto, u16_t mport); + +static u8_t +tcp_listening(u16_t port) +{ + struct tcp_pcb_listen *t; + for (t = tcp_listen_pcbs.listen_pcbs; t; t = t->next) + if (t->local_port == port) + return 1; + if (ip_portmap_find(IP_PROTO_TCP, port)) + return 1; + return 0; +} +#endif /* LWIP_TCP */ + +#if LWIP_UDP +static u8_t +udp_listening(u16_t port) +{ + struct udp_pcb *pcb; + for (pcb = udp_pcbs; pcb; pcb = pcb->next) + if (pcb->local_port == port) + return 1; + if (ip_portmap_find(IP_PROTO_UDP, port)) + return 1; + return 0; +} +#endif /* LWIP_UDP */ + +static u16_t +ip_napt_new_port(u8_t proto, u16_t port) +{ + if (PP_NTOHS(port) >= IP_NAPT_PORT_RANGE_START && PP_NTOHS(port) <= IP_NAPT_PORT_RANGE_END) + if (!ip_napt_find_port(proto, port) && !tcp_listening(port)) + return port; + for (;;) { + port = PP_HTONS(IP_NAPT_PORT_RANGE_START + + LWIP_RAND() % (IP_NAPT_PORT_RANGE_END - IP_NAPT_PORT_RANGE_START + 1)); + if (ip_napt_find_port(proto, port)) + continue; +#if LWIP_TCP + if (proto == IP_PROTO_TCP && tcp_listening(port)) + continue; +#endif /* LWIP_TCP */ +#if LWIP_UDP + if (proto == IP_PROTO_UDP && udp_listening(port)) + continue; +#endif /* LWIP_UDP */ + + return port; + } +} + +static struct napt_table* +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; + + 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))); +#if NAPT_DEBUG + napt_debug_print(); +#endif + + now = sys_now(); + 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) { + t->last = now; + LWIP_DEBUGF(NAPT_DEBUG, ("found\n")); + return t; + } + if (dest == 1 && t->proto == proto && t->dest == addr && t->dport == port + && t->mport == mport) { + t->last = now; + LWIP_DEBUGF(NAPT_DEBUG, ("found\n")); + return t; + } + } + + LWIP_DEBUGF(NAPT_DEBUG, ("not found\n")); + return NULL; +} + +static u16_t +ip_napt_add(u8_t proto, u32_t src, u16_t sport, u32_t dest, u16_t dport) +{ + struct napt_table *t = ip_napt_find(proto, src, sport, 0, 0); + if (t) { + t->last = sys_now(); + t->dest = dest; + t->dport = dport; + /* move this entry to the top of napt_list */ + ip_napt_free(t); + ip_napt_insert(t); + + LWIP_DEBUGF(NAPT_DEBUG, ("ip_napt_add\n")); +#if NAPT_DEBUG + napt_debug_print(); +#endif + + return t->mport; + } + t = NT(napt_free); + if (t) { + u16_t mport = sport; +#if LWIP_TCP + if (proto == IP_PROTO_TCP) + mport = ip_napt_new_port(IP_PROTO_TCP, sport); +#endif +#if LWIP_TCP + if (proto == IP_PROTO_UDP) + mport = ip_napt_new_port(IP_PROTO_UDP, sport); +#endif + t->last = sys_now(); + t->src = src; + t->dest = dest; + t->sport = sport; + t->dport = dport; + t->mport = mport; + t->proto = proto; + t->fin1 = t->fin2 = t->finack1 = t->finack2 = t->synack = t->rst = 0; + ip_napt_insert(t); + + LWIP_DEBUGF(NAPT_DEBUG, ("ip_napt_add\n")); +#if NAPT_DEBUG + napt_debug_print(); +#endif + + return mport; + } + LWIP_DEBUGF(NAPT_DEBUG, ("ip_napt_add() failed to insert\n")); + return 0; +} + +u8_t +ip_portmap_add(u8_t proto, u32_t maddr, u16_t mport, u32_t daddr, u16_t dport) +{ + int i; + mport = PP_HTONS(mport); + dport = PP_HTONS(dport); + + for (i = 0; i < ip_portmap_max; i++) { + struct portmap_table *p = &ip_portmap_table[i]; + if (p->valid && p->proto == proto && p->mport == mport) { + p->dport = dport; + p->daddr = daddr; + } else if (!p->valid) { + p->maddr = maddr; + p->daddr = daddr; + p->mport = mport; + p->dport = dport; + p->proto = proto; + p->valid = 1; + return 1; + } + } + return 0; +} + +static struct portmap_table * +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]; + if (!p->valid) + return 0; + if (p->proto == proto && p->mport == mport) + return p; + } + return NULL; +} + +static struct portmap_table * +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]; + if (!p->valid) + return 0; + if (p->proto == proto && p->dport == dport && p->daddr == daddr) + return p; + } + return NULL; +} + +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)); + if (!m) + return 0; + for (; m != last; m++) + memcpy(m, m + 1, sizeof(*m)); + last->valid = 0; + return 1; +} + +#if LWIP_TCP +static void +ip_napt_modify_port_tcp(struct tcp_hdr *tcphdr, u8_t dest, u16_t newval) +{ + if (dest) { + checksumadjust((u8_t *)&tcphdr->chksum, (u8_t *)&tcphdr->dest, 2, (u8_t *)&newval, 2); + tcphdr->dest = newval; + } else { + checksumadjust((u8_t *)&tcphdr->chksum, (u8_t *)&tcphdr->src, 2, (u8_t *)&newval, 2); + tcphdr->src = newval; + } +} + +static void +ip_napt_modify_addr_tcp(struct tcp_hdr *tcphdr, ip4_addr_p_t *oldval, u32_t newval) +{ + checksumadjust((u8_t *)&tcphdr->chksum, (u8_t *)&oldval->addr, 4, (u8_t *)&newval, 4); +} +#endif /* LWIP_TCP */ + +#if LWIP_UDP +static void +ip_napt_modify_port_udp(struct udp_hdr *udphdr, u8_t dest, u16_t newval) +{ + if (dest) { + if (udphdr->chksum != 0) + checksumadjust((u8_t *)&udphdr->chksum, (u8_t *)&udphdr->dest, 2, (u8_t *)&newval, 2); + udphdr->dest = newval; + } else { + if (udphdr->chksum != 0) + checksumadjust((u8_t *)&udphdr->chksum, (u8_t *)&udphdr->src, 2, (u8_t *)&newval, 2); + udphdr->src = newval; + } +} + +static void +ip_napt_modify_addr_udp(struct udp_hdr *udphdr, ip4_addr_p_t *oldval, u32_t newval) +{ + if (udphdr->chksum != 0) + checksumadjust( (u8_t *)&udphdr->chksum, (u8_t *)&oldval->addr, 4, (u8_t *)&newval, 4); +} +#endif /* LWIP_UDP */ + +static void +ip_napt_modify_addr(struct ip_hdr *iphdr, ip4_addr_p_t *field, u32_t newval) +{ + checksumadjust((u8_t *)&IPH_CHKSUM(iphdr), (u8_t *)&field->addr, 4, (u8_t *)&newval, 4); + field->addr = newval; +} + +void +ip_napt_recv(struct pbuf *p, struct ip_hdr *iphdr) +{ + struct portmap_table *m; + struct napt_table *t; +#if LWIP_ICMP + /* NAPT for ICMP Echo Request using identifier */ + if (IPH_PROTO(iphdr) == IP_PROTO_ICMP) { + struct icmp_echo_hdr *iecho = (struct icmp_echo_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4); + if (iecho->type == ICMP_ER) { + t = ip_napt_find(IP_PROTO_ICMP, iphdr->src.addr, iecho->id, iecho->id, 1); + if (!t) + return; + ip_napt_modify_addr(iphdr, &iphdr->dest, t->src); + return; + } + + return; + } +#endif /* LWIP_ICMP */ + +#if LWIP_TCP + if (IPH_PROTO(iphdr) == IP_PROTO_TCP) { + 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))); + + LWIP_DEBUGF(NAPT_DEBUG, ("sport %u, dport: %u\n", + lwip_htons(tcphdr->src), + lwip_htons(tcphdr->dest))); + + m = ip_portmap_find(IP_PROTO_TCP, tcphdr->dest); + if (m) { + /* packet to mapped port: rewrite destination */ + if (m->dport != tcphdr->dest) + ip_napt_modify_port_tcp(tcphdr, 1, m->dport); + ip_napt_modify_addr_tcp(tcphdr, &iphdr->dest, m->daddr); + ip_napt_modify_addr(iphdr, &iphdr->dest, m->daddr); + return; + } + 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->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; + } +#endif /* LWIP_TCP */ + +#if LWIP_UDP + if (IPH_PROTO(iphdr) == IP_PROTO_UDP) { + struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4); + m = ip_portmap_find(IP_PROTO_UDP, udphdr->dest); + if (m) { + /* packet to mapped port: rewrite destination */ + if (m->dport != udphdr->dest) + ip_napt_modify_port_udp(udphdr, 1, m->dport); + ip_napt_modify_addr_udp(udphdr, &iphdr->dest, m->daddr); + ip_napt_modify_addr(iphdr, &iphdr->dest, m->daddr); + return; + } + t = ip_napt_find(IP_PROTO_UDP, iphdr->src.addr, udphdr->src, udphdr->dest, 1); + if (!t) + return; /* Unknown session; do nothing */ + + if (t->sport != udphdr->dest) + ip_napt_modify_port_udp(udphdr, 1, t->sport); + ip_napt_modify_addr_udp(udphdr, &iphdr->dest, t->src); + ip_napt_modify_addr(iphdr, &iphdr->dest, t->src); + return; + } +#endif /* LWIP_UDP */ +} + +err_t +ip_napt_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp, struct netif *outp) +{ + if (!inp->napt) + return ERR_OK; + +#if LWIP_ICMP + /* NAPT for ICMP Echo Request using identifier */ + if (IPH_PROTO(iphdr) == IP_PROTO_ICMP) { + 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_modify_addr(iphdr, &iphdr->src, ip_2_ip4(&outp->ip_addr)->addr); + } + + return ERR_OK; + } +#endif + +#if LWIP_TCP + if (IPH_PROTO(iphdr) == IP_PROTO_TCP) { + struct tcp_hdr *tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4); + u16_t mport; + + struct portmap_table *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) + ip_napt_modify_port_tcp(tcphdr, 0, m->mport); + ip_napt_modify_addr_tcp(tcphdr, &iphdr->src, m->maddr); + ip_napt_modify_addr(iphdr, &iphdr->src, m->maddr); + return ERR_OK; + } + if ((TCPH_FLAGS(tcphdr) & (TCP_SYN|TCP_ACK)) == TCP_SYN && + 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) + 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); + if (!t || t->dest != iphdr->dest.addr || t->dport != tcphdr->dest) { +#if LWIP_ICMP + icmp_dest_unreach(p, ICMP_DUR_PORT); +#endif + 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)) + t->finack1 = 1; /* FIXME: Currently ignoring ACK seq... */ + if (TCPH_FLAGS(tcphdr) & TCP_RST) + t->rst = 1; + } + + if (mport != tcphdr->src) + ip_napt_modify_port_tcp(tcphdr, 0, mport); + ip_napt_modify_addr_tcp(tcphdr, &iphdr->src, ip_2_ip4(&outp->ip_addr)->addr); + ip_napt_modify_addr(iphdr, &iphdr->src, ip_2_ip4(&outp->ip_addr)->addr); + + return ERR_OK; + } +#endif + +#if LWIP_UDP + if (IPH_PROTO(iphdr) == IP_PROTO_UDP) { + struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4); + u16_t mport; + + struct portmap_table *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) + ip_napt_modify_port_udp(udphdr, 0, m->mport); + ip_napt_modify_addr_udp(udphdr, &iphdr->src, m->maddr); + LWIP_DEBUGF(NAPT_DEBUG, ("Modify UDP addr %x %x", iphdr->src.addr, m->maddr)); + ip_napt_modify_addr(iphdr, &iphdr->src, m->maddr); + return ERR_OK; + } + 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); + 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); + if (!t || t->dest != iphdr->dest.addr || t->dport != udphdr->dest) { +#if LWIP_ICMP + icmp_dest_unreach(p, ICMP_DUR_PORT); +#endif + return ERR_RTE; /* Drop unknown UDP session */ + } + mport = t->mport; + } + + if (mport != udphdr->src) + ip_napt_modify_port_udp(udphdr, 0, mport); + ip_napt_modify_addr_udp(udphdr, &iphdr->src, ip_2_ip4(&outp->ip_addr)->addr); + ip_napt_modify_addr(iphdr, &iphdr->src, ip_2_ip4(&outp->ip_addr)->addr); + return ERR_OK; + } +#endif + + return ERR_OK; +} +#endif /* IP_NAPT */ +#endif /* LWIP_IPV4 */ +#endif /* ESP_LWIP */ diff --git a/src/core/netif.c b/src/core/netif.c index 088b50ed..49ee313d 100644 --- a/src/core/netif.c +++ b/src/core/netif.c @@ -366,6 +366,12 @@ netif_add(struct netif *netif, #endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ #endif /* ENABLE_LOOPBACK */ +#if ESP_LWIP +#if IP_NAPT + netif->napt = 0; +#endif /* IP_NAPT */ +#endif /* ESP_LWIP */ + #if LWIP_IPV4 netif_set_addr(netif, ipaddr, netmask, gw); #endif /* LWIP_IPV4 */ diff --git a/src/include/lwip/ip4_napt.h b/src/include/lwip/ip4_napt.h new file mode 100644 index 00000000..41b0fc68 --- /dev/null +++ b/src/include/lwip/ip4_napt.h @@ -0,0 +1,95 @@ +/** + * @file ip4_napt.h + * This is a private interface of ip4_napt used from ip4.c + * + * @see ip4_napt.c + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * original reassembly code by Adam Dunkels + * + */ + +#ifndef LWIP_HDR_IP4_NAPT_H +#define LWIP_HDR_IP4_NAPT_H + +#include "lwip/opt.h" + +#if ESP_LWIP +#if IP_FORWARD +#if IP_NAPT + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/ip4_addr.h" +#include "lwip/err.h" +#include "lwip/netif.h" +#include "lwip/prot/ip4.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lwip/err.h" +#include "lwip/ip4.h" + +/** +* 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); + +/** + * NAPT for an input packet. It checks weather the destination is on NAPT + * table and modify the packet destination 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 + */ +void +ip_napt_recv(struct pbuf *p, struct ip_hdr *iphdr); + +#ifdef __cplusplus +} +#endif + +#endif /* IP_NAPT */ +#endif /* IP_FORWARD */ +#endif /* ESP_LWIP */ + +#endif /* LWIP_HDR_IP4_NAPT_H */ + diff --git a/src/include/lwip/lwip_napt.h b/src/include/lwip/lwip_napt.h new file mode 100644 index 00000000..605b8eb7 --- /dev/null +++ b/src/include/lwip/lwip_napt.h @@ -0,0 +1,122 @@ +/** + * @file lwip_napt.h + * public API of ip4_napt + * + * @see ip4_napt.c + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * original reassembly code by Adam Dunkels + * + */ + +#ifndef __LWIP_NAPT_H__ +#define __LWIP_NAPT_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if ESP_LWIP +#if IP_FORWARD +#if IP_NAPT + +/* Default size of the tables used for NAPT */ +#ifndef IP_NAPT_MAX +#define IP_NAPT_MAX 512 +#endif +#ifndef IP_PORTMAP_MAX +#define IP_PORTMAP_MAX 32 +#endif + +/* Timeouts in sec for the various protocol types */ +#define IP_NAPT_TIMEOUT_MS_TCP (30*60*1000) +#define IP_NAPT_TIMEOUT_MS_TCP_DISCON (20*1000) +#define IP_NAPT_TIMEOUT_MS_UDP (2*1000) +#define IP_NAPT_TIMEOUT_MS_ICMP (2*1000) + +#define IP_NAPT_PORT_RANGE_START 49152 +#define IP_NAPT_PORT_RANGE_END 61439 + +/** + * Enable/Disable NAPT for a specified interface. + * + * @param addr ip address of the interface + * @param enable non-zero to enable NAPT, or 0 to disable. + */ +void +ip_napt_enable(u32_t addr, int enable); + + +/** + * Enable/Disable NAPT for a specified interface. + * + * @param number netif 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); + + +/** + * Register port mapping on the external interface to internal interface. + * When the same port mapping is registered again, the old mapping is overwritten. + * In this implementation, only 1 unique port mapping can be defined for each target address/port. + * + * @param proto target protocol + * @param maddr ip address of the external interface + * @param mport mapped port on the external interface, in host byte order. + * @param daddr destination ip address + * @param dport destination port, in host byte order. + */ +u8_t +ip_portmap_add(u8_t proto, u32_t maddr, u16_t mport, u32_t daddr, u16_t dport); + + +/** + * Unregister port mapping on the external interface to internal interface. + * + * @param proto target protocol + * @param mport mapped port + */ +u8_t +ip_portmap_remove(u8_t proto, u16_t mport); + +#endif /* IP_NAPT */ +#endif /* IP_FORWARD */ +#endif /* ESP_LWIP */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_NAPT_H__ */ diff --git a/src/include/lwip/netif.h b/src/include/lwip/netif.h index 9a16deda..4cc59479 100644 --- a/src/include/lwip/netif.h +++ b/src/include/lwip/netif.h @@ -391,6 +391,9 @@ struct netif { u8_t reschedule_poll; #endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ #endif /* ENABLE_LOOPBACK */ +#if LWIP_IPV4 && IP_NAPT + u8_t napt; +#endif /*LWIP_IPV4 && IP_NAPT */ }; #if LWIP_CHECKSUM_CTRL_PER_NETIF diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h index b359b1db..87e22973 100644 --- a/src/include/lwip/opt.h +++ b/src/include/lwip/opt.h @@ -764,6 +764,14 @@ #define IP_FRAG 0 #endif /* !LWIP_IPV4 */ +/** + * IP_NAPT==1: Enables IPv4 Network Address and Port Translation + * Note that IP_FORWARD needs to be enabled for NAPT to work + */ +#if !defined IP_NAPT || defined __DOXYGEN__ +#define IP_NAPT 0 +#endif + /** * IP_OPTIONS_ALLOWED: Defines the behavior for IP options. * IP_OPTIONS_ALLOWED==0: All packets with IP options are dropped. @@ -3511,6 +3519,13 @@ #define LWIP_TESTMODE 0 #endif +/** + * NAPT_DEBUG: Enable debugging for NAPT. + */ +#ifndef NAPT_DEBUG +#define NAPT_DEBUG LWIP_DBG_OFF +#endif + /* -------------------------------------------------- ---------- Performance tracking options ---------- diff --git a/test/unit/Filelists.cmake b/test/unit/Filelists.cmake index c5c8afb7..25cc3c6b 100644 --- a/test/unit/Filelists.cmake +++ b/test/unit/Filelists.cmake @@ -18,6 +18,7 @@ set(LWIP_TESTFILES ${LWIP_TESTDIR}/core/test_netif.c ${LWIP_TESTDIR}/core/test_pbuf.c ${LWIP_TESTDIR}/core/test_timers.c + ${LWIP_TESTDIR}/core/test_ip4_route.c ${LWIP_TESTDIR}/dhcp/test_dhcp.c ${LWIP_TESTDIR}/etharp/test_etharp.c ${LWIP_TESTDIR}/ip4/test_ip4.c diff --git a/test/unit/Filelists.mk b/test/unit/Filelists.mk index 10320850..32762559 100644 --- a/test/unit/Filelists.mk +++ b/test/unit/Filelists.mk @@ -36,6 +36,7 @@ TESTFILES=$(TESTDIR)/lwip_unittests.c \ $(TESTDIR)/core/test_def.c \ $(TESTDIR)/core/test_mem.c \ $(TESTDIR)/core/test_netif.c \ + $(TESTDIR)/core/test_ip4_route.c \ $(TESTDIR)/core/test_pbuf.c \ $(TESTDIR)/core/test_timers.c \ $(TESTDIR)/dhcp/test_dhcp.c \ diff --git a/test/unit/cc_esp_platform.h b/test/unit/cc_esp_platform.h new file mode 100644 index 00000000..b1ee00d5 --- /dev/null +++ b/test/unit/cc_esp_platform.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_ARCH_CC_ESP_H +#define LWIP_ARCH_CC_ESP_H + +/* see https://sourceforge.net/p/predef/wiki/OperatingSystems/ */ +#if defined __ANDROID__ +#define LWIP_UNIX_ANDROID +#elif defined __linux__ +#define LWIP_UNIX_LINUX +#elif defined __APPLE__ +#define LWIP_UNIX_MACH +#elif defined __OpenBSD__ +#define LWIP_UNIX_OPENBSD +#elif defined __CYGWIN__ +#define LWIP_UNIX_CYGWIN +#elif defined __GNU__ +#define LWIP_UNIX_HURD +#endif + +#define LWIP_TIMEVAL_PRIVATE 0 +#include + +#define LWIP_ERRNO_INCLUDE + +#if defined(LWIP_UNIX_LINUX) || defined(LWIP_UNIX_HURD) +#define LWIP_ERRNO_STDINCLUDE 1 +#endif + +#ifdef LWIP_RAND +#define LWIP_RAND() ((u32_t)rand()) +#endif + +/* different handling for unit test, normally not needed */ +#ifdef LWIP_NOASSERT_ON_ERROR +#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \ + handler;}} while(0) +#endif + +#if defined(LWIP_UNIX_ANDROID) && defined(FD_SET) +typedef __kernel_fd_set fd_set; +#endif + +#if defined(LWIP_UNIX_MACH) +/* sys/types.h and signal.h bring in Darwin byte order macros. pull the + header here and disable LwIP's version so that apps still can get + the macros via LwIP headers and use system headers */ +#include +#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS +#endif + +struct sio_status_s; +typedef struct sio_status_s sio_status_t; +#define sio_fd_t sio_status_t* +#define __sio_fd_t_defined + +typedef unsigned int sys_prot_t; + +#endif /* LWIP_ARCH_CC_ESP_H */ diff --git a/test/unit/core/test_ip4_route.c b/test/unit/core/test_ip4_route.c new file mode 100644 index 00000000..d159bb4c --- /dev/null +++ b/test/unit/core/test_ip4_route.c @@ -0,0 +1,561 @@ +#include "test_ip4_route.h" + +#include "lwip/netif.h" +#include "lwip/stats.h" +#include "lwip/etharp.h" +#include "netif/ethernet.h" +#include "lwip/ip4.h" +#include "lwip/udp.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/prot/ip.h" +#include "lwip/prot/ip4.h" +#include "lwip/lwip_napt.h" +#include "lwip/priv/tcp_priv.h" + +#include "lwip/tcpip.h" + +#if !LWIP_IPV4 || !IP_REASSEMBLY || !MIB2_STATS || !IPFRAG_STATS +#error "This tests needs LWIP_IPV4, IP_REASSEMBLY; MIB2- and IPFRAG-statistics enabled" +#endif + +#if !LWIP_NETIF_EXT_STATUS_CALLBACK +#error "This tests needs LWIP_NETIF_EXT_STATUS_CALLBACK enabled" +#endif + +static struct netif sta; +static struct netif ap; + +static int ap_cnt = 0; +static int sta_cnt = 0; +static u32_t last_src_addr = 0; +static u32_t last_dst_addr = 0; +static u16_t last_src_port = 0; + +#if IP_NAPT +static int random_mock = -1; +/* Mock the esp-random to return 0 for easier result checking */ +uint32_t esp_random(void) +{ + if (random_mock == -1) { + /* fall back to default "port/unix" rand (if other tests are launched with IP_NAPT ON) */ + return rand(); + } + return random_mock; +} +#endif + +/* Setups/teardown functions */ +static void +ip4route_setup(void) +{ + lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT)); +} + +static void +ip4route_teardown(void) +{ + lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT)); +} + +/* test helper functions */ +static void debug_print_packet(struct pbuf *q) +{ + struct ip_hdr *iphdr = (struct ip_hdr *)q->payload; + u16_t iphdr_hlen; + + iphdr_hlen = IPH_HL_BYTES(iphdr); + fail_unless(0 == inet_chksum((struct ip_hdr *)q->payload, iphdr_hlen)); + + ip4_debug_print(q); + + last_src_addr = iphdr->src.addr; + last_dst_addr = iphdr->dest.addr; + + if (IPH_PROTO(iphdr) == IP_PROTO_UDP) { + struct udp_hdr *udphdr = (struct udp_hdr *)(( u8_t *)iphdr + iphdr_hlen); + udp_debug_print(udphdr); + LWIP_DEBUGF(UDP_DEBUG, ("udp (")); + ip_addr_debug_print_val(UDP_DEBUG, *ip_current_dest_addr()); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", lwip_ntohs(udphdr->dest))); + ip_addr_debug_print_val(UDP_DEBUG, *ip_current_src_addr()); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", lwip_ntohs(udphdr->src))); + last_src_port = udphdr->src; + + } else if (IPH_PROTO(iphdr) == IP_PROTO_TCP) { + struct tcp_hdr* tcphdr; + pbuf_header(q, -(s16_t)sizeof(struct ip_hdr)); + tcphdr = (struct tcp_hdr*)q->payload; + tcp_debug_print(tcphdr); + last_src_port = tcphdr->src; + + + } + +} + +static err_t +ap_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr) +{ + ap_cnt++; + debug_print_packet(q); + LWIP_UNUSED_ARG(netif); + LWIP_UNUSED_ARG(q); + LWIP_UNUSED_ARG(ipaddr); + + return ERR_OK; +} + +static err_t +sta_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr) +{ + sta_cnt++; + debug_print_packet(q); + LWIP_UNUSED_ARG(netif); + LWIP_UNUSED_ARG(q); + LWIP_UNUSED_ARG(ipaddr); + + return ERR_OK; +} + +static err_t +sta_tx_func(struct netif *netif, struct pbuf *p) +{ + LWIP_UNUSED_ARG(netif); + LWIP_UNUSED_ARG(p); + return ERR_OK; +} + +static err_t +ap_tx_func(struct netif *netif, struct pbuf *p) +{ + LWIP_UNUSED_ARG(netif); + LWIP_UNUSED_ARG(p); + return ERR_OK; +} + +#if IP_NAPT +static struct pbuf* +test_create_tcp_packet(u32_t src_ip4, u32_t dst_ip4, + u16_t src_port, u16_t dst_port, + u32_t seqno, u32_t ackno, u8_t headerflags, u16_t wnd) +{ + struct pbuf *p, *q; + ip_addr_t src_ip; + ip_addr_t dst_ip; + struct ip_hdr* iphdr; + struct tcp_hdr* tcphdr; + u16_t pbuf_len = (u16_t)(sizeof(struct ip_hdr) + sizeof(struct tcp_hdr)); + + src_ip.u_addr.ip4.addr = src_ip4; + dst_ip.u_addr.ip4.addr = dst_ip4; + + p = pbuf_alloc(PBUF_RAW, pbuf_len, PBUF_POOL); + EXPECT_RETNULL(p != NULL); + /* first pbuf must be big enough to hold the headers */ + EXPECT_RETNULL(p->len >= (sizeof(struct ip_hdr) + sizeof(struct tcp_hdr))); + + for(q = p; q != NULL; q = q->next) { + memset(q->payload, 0, q->len); + } + + iphdr = (struct ip_hdr*)p->payload; + /* fill IP header */ + iphdr->dest.addr = ip_2_ip4(&dst_ip)->addr; + iphdr->src.addr = ip_2_ip4(&src_ip)->addr; + IPH_VHL_SET(iphdr, 4, IP_HLEN / 4); + IPH_TOS_SET(iphdr, 0); + IPH_LEN_SET(iphdr, htons(p->tot_len)); + iphdr->_ttl = 32; + iphdr->_proto = IP_PROTO_TCP; + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + + /* let p point to TCP header */ + pbuf_header(p, -(s16_t)sizeof(struct ip_hdr)); + + tcphdr = (struct tcp_hdr*)p->payload; + tcphdr->src = htons(src_port); + tcphdr->dest = htons(dst_port); + tcphdr->seqno = htonl(seqno); + tcphdr->ackno = htonl(ackno); + TCPH_HDRLEN_SET(tcphdr, sizeof(struct tcp_hdr)/4); + TCPH_FLAGS_SET(tcphdr, headerflags); + tcphdr->wnd = htons(wnd); + + /* calculate checksum */ + tcphdr->chksum = ip_chksum_pseudo(p, + IP_PROTO_TCP, p->tot_len, &src_ip, &dst_ip); + + pbuf_header(p, sizeof(struct ip_hdr)); + + return p; +} +#endif + +static struct pbuf * +test_udp_create_test_packet(u16_t length, u16_t dst_port, u16_t src_port, u32_t dst_addr, u32_t src_addr) +{ + err_t err; + u8_t ret; + struct udp_hdr *uh; + struct ip_hdr *ih; + struct pbuf *p; + const u8_t test_data[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; + + p = pbuf_alloc(PBUF_TRANSPORT, length, PBUF_POOL); + fail_unless(p != NULL); + if (p == NULL) { + return NULL; + } + fail_unless(p->next == NULL); + err = pbuf_take(p, test_data, length); + fail_unless(err == ERR_OK); + + /* add UDP header */ + ret = pbuf_add_header(p, sizeof(struct udp_hdr)); + fail_unless(!ret); + uh = (struct udp_hdr *)p->payload; + uh->chksum = 0; + uh->dest = lwip_htons(dst_port); + uh->src = lwip_htons(src_port); + uh->len = lwip_htons(p->tot_len); + /* add IPv4 header */ + ret = pbuf_add_header(p, sizeof(struct ip_hdr)); + fail_unless(!ret); + ih = (struct ip_hdr *)p->payload; + memset(ih, 0, sizeof(*ih)); + ih->dest.addr = dst_addr; + ih->src.addr = src_addr; + ih->_len = lwip_htons(p->tot_len); + ih->_ttl = 32; + ih->_proto = IP_PROTO_UDP; + IPH_VHL_SET(ih, 4, sizeof(struct ip_hdr) / 4); + IPH_CHKSUM_SET(ih, inet_chksum(ih, sizeof(struct ip_hdr))); + return p; +} + + +static void +send_to_netif(struct netif *input_netif, struct pbuf *p) +{ + err_t err; + + if (p != NULL) { + err = ip4_input(p, input_netif); + fail_unless(err == ERR_OK); + } +} + +static err_t +testif_init_sta(struct netif *netif) +{ + netif->name[0] = 's'; + netif->name[1] = 't'; + netif->output = sta_output; + netif->linkoutput = sta_tx_func; + netif->mtu = 1500; + netif->hwaddr_len = 6; + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP | NETIF_FLAG_MLD6 | NETIF_FLAG_LINK_UP; + + netif->hwaddr[0] = 0x02; + netif->hwaddr[1] = 0x03; + netif->hwaddr[2] = 0x04; + netif->hwaddr[3] = 0x05; + netif->hwaddr[4] = 0x06; + netif->hwaddr[5] = 0x08; + + return ERR_OK; +} + +static err_t +testif_init_ap(struct netif *netif) +{ + netif->name[0] = 'a'; + netif->name[1] = 'p'; + netif->output = ap_output; + netif->linkoutput = ap_tx_func; + netif->mtu = 1500; + netif->hwaddr_len = 6; + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP | NETIF_FLAG_MLD6 | NETIF_FLAG_LINK_UP; + + netif->hwaddr[0] = 0x02; + netif->hwaddr[1] = 0x03; + netif->hwaddr[2] = 0x04; + netif->hwaddr[3] = 0x05; + netif->hwaddr[4] = 0x06; + netif->hwaddr[5] = 0x07; + + return ERR_OK; +} + + +/* Test functions */ +START_TEST(test_ip4_route_netif_forward) +{ +#define UDP_PORT 1234 + ip4_addr_t addr, src_addr; + ip4_addr_t netmask; + ip4_addr_t gw; + struct pbuf *p; + LWIP_UNUSED_ARG(_i); + + /* setup station */ + IP4_ADDR(&addr, 1, 2, 4, 4); + IP4_ADDR(&netmask, 255, 255, 255, 0); + IP4_ADDR(&gw, 1, 2, 4, 10); + netif_add(&sta, &addr, &netmask, &gw, &sta, testif_init_sta, ethernet_input); + netif_set_up(&sta); + + /* setup access point */ + IP4_ADDR(&addr, 1, 2, 3, 5); + IP4_ADDR(&netmask, 255, 255, 255, 0); + IP4_ADDR(&gw, 1, 2, 3, 4); + netif_add(&ap, &addr, &netmask, &gw, &ap, testif_init_ap, ethernet_input); + netif_set_up(&ap); + + /* create packet and send it to the AP */ + IP4_ADDR(&addr, 1, 2, 4, 100); + IP4_ADDR(&src_addr, 1, 2, 3, 5); + p = test_udp_create_test_packet(16, UDP_PORT, UDP_PORT, addr.addr, src_addr.addr); + send_to_netif(&ap, p); + + /* cleanup */ + netif_set_down(&ap); + netif_remove(&ap); + netif_set_down(&sta); + netif_remove(&sta); + + fail_unless(ap_cnt == 0); +#if IP_FORWARD + fail_unless(sta_cnt == 1); + fail_unless(last_src_port == lwip_ntohs(UDP_PORT)); + fail_unless(last_src_addr == src_addr.addr); +#else + /* if IP FORWARD disabled, no packet to be found on the other interface */ + fail_unless(sta_cnt == 0); +#endif +#undef UDP_PORT +} +END_TEST + +#if IP_NAPT +START_TEST(test_ip4_route_netif_napt_udp) +{ +#define UDP_PORT 1234 + ip4_addr_t addr, src_addr, sta_addr; + ip4_addr_t netmask; + ip4_addr_t gw; + struct pbuf *p; + LWIP_UNUSED_ARG(_i); + + /* setup station */ + IP4_ADDR(&sta_addr, 1, 2, 4, 4); + IP4_ADDR(&netmask, 255, 255, 255, 0); + IP4_ADDR(&gw, 1, 2, 4, 10); + netif_add(&sta, &sta_addr, &netmask, &gw, &sta, testif_init_sta, ethernet_input); + netif_set_up(&sta); + + /* setup access point */ + IP4_ADDR(&addr, 10, 0, 0, 1); + IP4_ADDR(&netmask, 255, 255, 0, 0); + IP4_ADDR(&gw, 10, 0, 0, 1); + netif_add(&ap, &addr, &netmask, &gw, &ap, testif_init_ap, ethernet_input); + netif_set_up(&ap); + ip_napt_enable(addr.addr, 1); + + /* create packet and send it to the AP */ + IP4_ADDR(&addr, 1, 2, 4, 100); + IP4_ADDR(&src_addr, 10, 0, 0, 2); + p = test_udp_create_test_packet(16, UDP_PORT, UDP_PORT, addr.addr, src_addr.addr); + + random_mock = 1; + send_to_netif(&ap, p); + + /* expect to see a random port and translated source address to be station address */ + fail_unless(last_src_port == lwip_ntohs(IP_NAPT_PORT_RANGE_START+random_mock)); + fail_unless(last_src_addr == sta_addr.addr); + fail_unless(ap_cnt == 0); + fail_unless(sta_cnt == 1); + + p = test_udp_create_test_packet(16, IP_NAPT_PORT_RANGE_START+random_mock, UDP_PORT, sta_addr.addr, addr.addr); + send_to_netif(&sta, p); + fail_unless(ap_cnt == 1); + fail_unless(sta_cnt == 1); + /* expect to see a random port and translated source address to be station address */ + fail_unless(last_src_port == lwip_ntohs(UDP_PORT)); + fail_unless(last_dst_addr == src_addr.addr); + + /* cleanup */ + netif_set_down(&ap); + netif_remove(&ap); + netif_set_down(&sta); + netif_remove(&sta); + + IP4_ADDR(&addr, 10, 0, 0, 1); + ip_napt_enable(addr.addr, 0); + +#undef UDP_PORT +} +END_TEST + +START_TEST(test_ip4_route_netif_napt_tcp) +{ +#define TCP_PORT 2222 + ip4_addr_t addr, src_addr, sta_addr; + ip4_addr_t netmask; + ip4_addr_t gw; + struct pbuf *p; + LWIP_UNUSED_ARG(_i); + + /* setup station */ + IP4_ADDR(&sta_addr, 1, 2, 4, 4); + IP4_ADDR(&netmask, 255, 255, 255, 0); + IP4_ADDR(&gw, 1, 2, 4, 10); + netif_add(&sta, &sta_addr, &netmask, &gw, &sta, testif_init_sta, ethernet_input); + netif_set_up(&sta); + + /* setup access point */ + IP4_ADDR(&addr, 10, 0, 0, 1); + IP4_ADDR(&netmask, 255, 255, 0, 0); + IP4_ADDR(&gw, 10, 0, 0, 1); + netif_add(&ap, &addr, &netmask, &gw, &ap, testif_init_ap, ethernet_input); + netif_set_up(&ap); + ip_napt_enable_no(ap.num, 1); + + /* create packet and send it to the AP */ + IP4_ADDR(&addr, 1, 2, 4, 100); + IP4_ADDR(&src_addr, 10, 0, 0, 2); + p = test_create_tcp_packet(src_addr.addr, addr.addr, TCP_PORT, TCP_PORT, 0, 0, TCP_SYN, 0); + + random_mock = 2; + send_to_netif(&ap, p); + + /* expect to see a random port and translated source address to be station address */ + fail_unless(last_src_port == lwip_ntohs(IP_NAPT_PORT_RANGE_START+random_mock)); + fail_unless(last_src_addr == sta_addr.addr); + fail_unless(ap_cnt == 0); + fail_unless(sta_cnt == 1); + + p = test_create_tcp_packet(addr.addr, sta_addr.addr, TCP_PORT, IP_NAPT_PORT_RANGE_START+random_mock, 0, 0, TCP_SYN|TCP_ACK, 0); + send_to_netif(&sta, p); + fail_unless(ap_cnt == 1); + fail_unless(sta_cnt == 1); + /* expect to see a random port and translated source address to be station address */ + fail_unless(last_src_port == lwip_ntohs(TCP_PORT)); + fail_unless(last_dst_addr == src_addr.addr); + + /* cleanup */ + ip_napt_enable_no(ap.num, 0); + netif_set_down(&ap); + netif_remove(&ap); + netif_set_down(&sta); + netif_remove(&sta); + +#undef TCP_PORT +} +END_TEST + +START_TEST(test_ip4_route_netif_max_napt) +{ +#define TCP_PORT 2222 + ip4_addr_t addr, src_addr, sta_addr; + ip4_addr_t netmask; + ip4_addr_t gw; + struct pbuf *p; + int i; + LWIP_UNUSED_ARG(_i); + + /* setup station */ + IP4_ADDR(&sta_addr, 1, 2, 4, 4); + IP4_ADDR(&netmask, 255, 255, 255, 0); + IP4_ADDR(&gw, 1, 2, 4, 10); + netif_add(&sta, &sta_addr, &netmask, &gw, &sta, testif_init_sta, ethernet_input); + netif_set_up(&sta); + + /* setup access point */ + IP4_ADDR(&addr, 10, 0, 0, 1); + IP4_ADDR(&netmask, 255, 255, 0, 0); + IP4_ADDR(&gw, 10, 0, 0, 1); + netif_add(&ap, &addr, &netmask, &gw, &ap, testif_init_ap, ethernet_input); + netif_set_up(&ap); + ip_napt_enable_no(ap.num, 1); + + /* create packet and send it to the AP */ + IP4_ADDR(&addr, 1, 2, 4, 100); + IP4_ADDR(&src_addr, 10, 0, 0, 2); + for (i=0; i expect to see an outgoing packet */ + fail_unless(last_src_port == lwip_ntohs(IP_NAPT_PORT_RANGE_START+random_mock)); + fail_unless(last_src_addr == sta_addr.addr); + fail_unless(sta_cnt == 1+i); + p = test_create_tcp_packet(addr.addr, sta_addr.addr, TCP_PORT+i, IP_NAPT_PORT_RANGE_START+random_mock, 0, 0, TCP_SYN | TCP_ACK, 0); + send_to_netif(&sta, p); + + } else { + /* if more than NAPT_MAX, no more packet to be forwarded */ + fail_unless(sta_cnt == IP_NAPT_MAX); + } + } + + /* moves time forward to test releasing: */ + lwip_sys_now += IP_NAPT_TIMEOUT_MS_TCP_DISCON + 1; + p = test_create_tcp_packet(addr.addr, sta_addr.addr, TCP_PORT, IP_NAPT_PORT_RANGE_START+0, 0, 0, TCP_PSH, 0); + send_to_netif(&sta, p); + p = test_create_tcp_packet(src_addr.addr, addr.addr, TCP_PORT + IP_NAPT_MAX*2, TCP_PORT + IP_NAPT_MAX*2, 0, 0, TCP_PSH | TCP_ACK, 0); + sta_cnt = 0; + send_to_netif(&ap, p); + /* should not be released yet, since all the TCP connections are active */ + fail_unless(sta_cnt == 0); /* expect no packet forwarded */ + + /* FIN the first connection so it could be released */ + p = test_create_tcp_packet(addr.addr, sta_addr.addr, TCP_PORT+0, IP_NAPT_PORT_RANGE_START+0, 0, 0, TCP_FIN, 0); + send_to_netif(&sta, p); + p = test_create_tcp_packet(src_addr.addr, addr.addr, TCP_PORT+0, TCP_PORT + 0, 0, 0, TCP_FIN | TCP_ACK, 0); + send_to_netif(&ap, p); + p = test_create_tcp_packet(addr.addr, sta_addr.addr, TCP_PORT+0, IP_NAPT_PORT_RANGE_START+0, 0, 0, TCP_ACK, 0); + send_to_netif(&sta, p); + /* moves time forward to test releasing: */ + lwip_sys_now += IP_NAPT_TIMEOUT_MS_TCP_DISCON + 1; + + /* now sending a new packet with max port, that should be forwarded */ + p = test_create_tcp_packet(src_addr.addr, addr.addr, TCP_PORT + IP_NAPT_MAX*2, TCP_PORT + IP_NAPT_MAX*2, 0, 0, TCP_SYN, 0); + random_mock = 0; + sta_cnt = 0; + send_to_netif(&ap, p); + + fail_unless(last_src_port == lwip_ntohs(IP_NAPT_PORT_RANGE_START+0)); + fail_unless(last_src_addr == sta_addr.addr); + fail_unless(sta_cnt == 1); /* expect this packet gets forwarded */ + + /* cleanup */ + netif_set_down(&ap); + ip_napt_enable_no(ap.num, 0); + netif_remove(&ap); + netif_set_down(&sta); + netif_remove(&sta); + +#undef TCP_PORT +} +END_TEST +#endif + +/** Create the suite including all tests for this module */ +Suite * +ip4route_suite(void) +{ + testfunc tests[] = { + TESTFUNC(test_ip4_route_netif_forward), +#if IP_NAPT + TESTFUNC(test_ip4_route_netif_napt_udp), + TESTFUNC(test_ip4_route_netif_napt_tcp), + TESTFUNC(test_ip4_route_netif_max_napt), + +#endif + }; + return create_suite("IP4_ROUTE", tests, sizeof(tests)/sizeof(testfunc), ip4route_setup, ip4route_teardown); +} diff --git a/test/unit/core/test_ip4_route.h b/test/unit/core/test_ip4_route.h new file mode 100644 index 00000000..3db90eff --- /dev/null +++ b/test/unit/core/test_ip4_route.h @@ -0,0 +1,8 @@ +#ifndef LWIP_HDR_TEST_IP4ROUTE_H +#define LWIP_HDR_TEST_IP4ROUTE_H + +#include "../lwip_check.h" + +Suite *ip4route_suite(void); + +#endif diff --git a/test/unit/lwip_unittests.c b/test/unit/lwip_unittests.c index 58e603ae..cd6b5851 100644 --- a/test/unit/lwip_unittests.c +++ b/test/unit/lwip_unittests.c @@ -9,6 +9,7 @@ #include "core/test_def.h" #include "core/test_mem.h" #include "core/test_netif.h" +#include "core/test_ip4_route.h" #include "core/test_pbuf.h" #include "core/test_timers.h" #include "etharp/test_etharp.h" @@ -63,6 +64,7 @@ int main(void) SRunner *sr; size_t i; suite_getter_fn* suites[] = { +#ifndef ESP_TEST_DEBUG ip4_suite, ip6_suite, udp_suite, @@ -78,7 +80,9 @@ int main(void) dhcp_suite, mdns_suite, mqtt_suite, - sockets_suite + sockets_suite, +#endif + ip4route_suite }; size_t num = sizeof(suites)/sizeof(void*); LWIP_ASSERT("No suites defined", num > 0); diff --git a/test/unit/lwipopts.h b/test/unit/lwipopts.h index e2d815fd..449e88c0 100644 --- a/test/unit/lwipopts.h +++ b/test/unit/lwipopts.h @@ -86,4 +86,19 @@ /* Enable Espressif specific options */ #define ESP_LWIP 1 +#ifdef IP_NAPT +#define IP_NAPT_MAX 16 +#undef LWIP_RAND +#define LWIP_RAND() (esp_random()) +#include "lwip/arch.h" +u32_t esp_random(void); +#endif /* IP_NAPT */ +/* ESP debug options */ +#ifdef ESP_TEST_DEBUG +#define NAPT_DEBUG LWIP_DBG_ON +#define IP_DEBUG LWIP_DBG_ON +#define UDP_DEBUG LWIP_DBG_ON +#define TCP_DEBUG LWIP_DBG_ON +#endif /* ESP_TEST_DEBUG */ + #endif /* LWIP_HDR_LWIPOPTS_H */