/* * NPF NAT tests. * * Public Domain. */ #ifdef _KERNEL #include #endif #include "npf_impl.h" #include "npf_test.h" #define RESULT_PASS 0 #define RESULT_BLOCK ENETUNREACH #define NPF_BINAT (NPF_NATIN | NPF_NATOUT) #define RANDOM_PORT 18791 static const struct test_case { const char * src; in_port_t sport; const char * dst; in_port_t dport; int ttype; const char * ifname; int di; int ret; int af; const char * taddr; in_port_t tport; } test_cases[] = { /* * Traditional NAPT (outbound NAT): * map $ext_if dynamic $local_net -> $pub_ip1 */ { LOCAL_IP1, 15000, REMOTE_IP1, 7000, NPF_NATOUT, IFNAME_EXT, PFIL_OUT, RESULT_PASS, AF_INET, PUB_IP1, RANDOM_PORT }, { LOCAL_IP1, 15000, REMOTE_IP1, 7000, NPF_NATOUT, IFNAME_EXT, PFIL_OUT, RESULT_PASS, AF_INET, PUB_IP1, RANDOM_PORT }, { LOCAL_IP1, 15000, REMOTE_IP1, 7000, NPF_NATOUT, IFNAME_EXT, PFIL_IN, RESULT_BLOCK, AF_INET, NULL, 0 }, { REMOTE_IP1, 7000, LOCAL_IP1, 15000, NPF_NATOUT, IFNAME_EXT, PFIL_IN, RESULT_BLOCK, AF_INET, NULL, 0 }, { REMOTE_IP1, 7000, PUB_IP1, RANDOM_PORT, NPF_NATOUT, IFNAME_INT, PFIL_IN, RESULT_BLOCK, AF_INET, NULL, 0 }, { REMOTE_IP1, 7000, PUB_IP1, RANDOM_PORT, NPF_NATOUT, IFNAME_EXT, PFIL_IN, RESULT_PASS, AF_INET, LOCAL_IP1, 15000 }, /* * NAT redirect (inbound NAT): * map $ext_if dynamic $local_ip1 port 6000 <- $pub_ip1 port 8000 */ { REMOTE_IP2, 16000, PUB_IP1, 8000, NPF_NATIN, IFNAME_EXT, PFIL_IN, RESULT_PASS, AF_INET, LOCAL_IP1, 6000 }, { LOCAL_IP1, 6000, REMOTE_IP2, 16000, NPF_NATIN, IFNAME_EXT, PFIL_OUT, RESULT_PASS, AF_INET, PUB_IP1, 8000 }, /* * Bi-directional NAT (inbound + outbound NAT): * map $ext_if dynamic $local_ip2 <-> $pub_ip2 */ { REMOTE_IP2, 17000, PUB_IP2, 9000, NPF_BINAT, IFNAME_EXT, PFIL_IN, RESULT_PASS, AF_INET, LOCAL_IP2, 9000 }, { LOCAL_IP2, 9000, REMOTE_IP2, 17000, NPF_BINAT, IFNAME_EXT, PFIL_OUT, RESULT_PASS, AF_INET, PUB_IP2, 9000 }, { LOCAL_IP2, 18000, REMOTE_IP2, 9000, NPF_BINAT, IFNAME_EXT, PFIL_OUT, RESULT_PASS, AF_INET, PUB_IP2, 18000 }, { REMOTE_IP2, 9000, PUB_IP2, 18000, NPF_BINAT, IFNAME_EXT, PFIL_IN, RESULT_PASS, AF_INET, LOCAL_IP2, 18000 }, /* * Static NAT: plain translation both ways. * map $ext_if static $local_ip3 <-> $pub_ip3 */ { LOCAL_IP3, 19000, REMOTE_IP3, 10000, NPF_BINAT, IFNAME_EXT, PFIL_OUT, RESULT_PASS, AF_INET, PUB_IP3, 19000 }, { REMOTE_IP3, 10000, PUB_IP3, 19000, NPF_BINAT, IFNAME_EXT, PFIL_IN, RESULT_PASS, AF_INET, LOCAL_IP3, 19000 }, /* * NETMAP case: * map $ext_if static algo netmap $net_a <-> $net_b */ { NET_A_IP1, 12345, REMOTE_IP4, 12345, NPF_BINAT, IFNAME_EXT, PFIL_OUT, RESULT_PASS, AF_INET, NET_B_IP1, 12345 }, /* * NPTv6 case: * map $ext_if static algo npt66 $net6_inner <-> $net6_outer */ { LOCAL_IP6, 1000, REMOTE_IP6, 1001, NPF_BINAT, IFNAME_EXT, PFIL_OUT, RESULT_PASS, AF_INET6, EXPECTED_IP6, 1000 }, { REMOTE_IP6, 1001, EXPECTED_IP6, 1000, NPF_BINAT, IFNAME_EXT, PFIL_IN, RESULT_PASS, AF_INET6, LOCAL_IP6, 1000 }, }; static bool match_addr(int af, const char *saddr, const npf_addr_t *addr2) { npf_addr_t addr1; size_t len; npf_inet_pton(af, saddr, &addr1); len = af == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr); return memcmp(&addr1, addr2, len) == 0; } static bool checkresult(bool verbose, unsigned i, struct mbuf *m, ifnet_t *ifp, int error) { const struct test_case *t = &test_cases[i]; const int af = t->af; npf_cache_t npc; nbuf_t nbuf; if (verbose) { printf("packet %d (expected %d ret %d)\n", i+1, t->ret, error); } if (error) { return error == t->ret; } nbuf_init(npf_getkernctx(), &nbuf, m, ifp); memset(&npc, 0, sizeof(npf_cache_t)); npc.npc_ctx = npf_getkernctx(); npc.npc_nbuf = &nbuf; if (!npf_cache_all(&npc)) { printf("error: could not fetch the packet data"); return false; } const struct udphdr *uh = npc.npc_l4.udp; if (verbose) { char sbuf[64], dbuf[64]; npf_inet_ntop(af, npc.npc_ips[NPF_SRC], sbuf, sizeof(sbuf)); npf_inet_ntop(af, npc.npc_ips[NPF_DST], dbuf, sizeof(dbuf)); printf("\tpost-translation: "); printf("src %s (%d) ", sbuf, ntohs(uh->uh_sport)); printf("dst %s (%d)\n", dbuf, ntohs(uh->uh_dport)); } if (error != t->ret) { return false; } const bool forw = t->di == PFIL_OUT; const char *saddr = forw ? t->taddr : t->src; const char *daddr = forw ? t->dst : t->taddr; in_addr_t sport = forw ? t->tport : t->sport; in_addr_t dport = forw ? t->dport : t->tport; CHECK_TRUE(match_addr(af, saddr, npc.npc_ips[NPF_SRC])); CHECK_TRUE(sport == ntohs(uh->uh_sport)); CHECK_TRUE(match_addr(af, daddr, npc.npc_ips[NPF_DST])); CHECK_TRUE(dport == ntohs(uh->uh_dport)); return true; } bool npf_nat_test(bool verbose) { npf_t *npf = npf_getkernctx(); for (unsigned i = 0; i < __arraycount(test_cases); i++) { const struct test_case *t = &test_cases[i]; ifnet_t *ifp = npf_test_getif(t->ifname); struct mbuf *m; int error; bool ret; if (ifp == NULL) { printf("Interface %s is not configured.\n", t->ifname); return false; } m = mbuf_get_pkt(t->af, IPPROTO_UDP, t->src, t->dst, t->sport, t->dport); error = npfk_packet_handler(npf, &m, ifp, t->di); ret = checkresult(verbose, i, m, ifp, error); if (m) { m_freem(m); } CHECK_TRUE(ret); } return true; }