1 /* $NetBSD: npf_nat_test.c,v 1.4 2013/09/24 02:04:21 rmind Exp $ */ 2 3 /* 4 * NPF NAT test. 5 * 6 * Public Domain. 7 */ 8 9 #include <sys/types.h> 10 11 #include "npf_impl.h" 12 #include "npf_test.h" 13 14 #define RESULT_PASS 0 15 #define RESULT_BLOCK ENETUNREACH 16 17 #define NPF_BINAT (NPF_NATIN | NPF_NATOUT) 18 19 static const struct test_case { 20 const char * src; 21 in_port_t sport; 22 const char * dst; 23 in_port_t dport; 24 int ttype; 25 const char * ifname; 26 int di; 27 int ret; 28 const char * taddr; 29 in_port_t tport; 30 } test_cases[] = { 31 32 /* 33 * Traditional NAPT (outbound NAT): 34 * map $ext_if dynamic $local_net -> $pub_ip1 35 */ 36 { 37 LOCAL_IP1, 15000, REMOTE_IP1, 7000, 38 NPF_NATOUT, IFNAME_EXT, PFIL_OUT, 39 RESULT_PASS, PUB_IP1, 53472 40 }, 41 { 42 LOCAL_IP1, 15000, REMOTE_IP1, 7000, 43 NPF_NATOUT, IFNAME_EXT, PFIL_OUT, 44 RESULT_PASS, PUB_IP1, 53472 45 }, 46 { 47 LOCAL_IP1, 15000, REMOTE_IP1, 7000, 48 NPF_NATOUT, IFNAME_EXT, PFIL_IN, 49 RESULT_BLOCK, NULL, 0 50 }, 51 { 52 REMOTE_IP1, 7000, LOCAL_IP1, 15000, 53 NPF_NATOUT, IFNAME_EXT, PFIL_IN, 54 RESULT_BLOCK, NULL, 0 55 }, 56 { 57 REMOTE_IP1, 7000, PUB_IP1, 53472, 58 NPF_NATOUT, IFNAME_INT, PFIL_IN, 59 RESULT_BLOCK, NULL, 0 60 }, 61 { 62 REMOTE_IP1, 7000, PUB_IP1, 53472, 63 NPF_NATOUT, IFNAME_EXT, PFIL_IN, 64 RESULT_PASS, LOCAL_IP1, 15000 65 }, 66 67 /* 68 * NAT redirect (inbound NAT): 69 * map $ext_if dynamic $local_ip1 port 8000 <- $pub_ip1 port 8000 70 */ 71 { 72 REMOTE_IP2, 16000, PUB_IP1, 8000, 73 NPF_NATIN, IFNAME_EXT, PFIL_IN, 74 RESULT_PASS, LOCAL_IP1, 6000 75 }, 76 { 77 LOCAL_IP1, 6000, REMOTE_IP2, 16000, 78 NPF_NATIN, IFNAME_EXT, PFIL_OUT, 79 RESULT_PASS, PUB_IP1, 8000 80 }, 81 82 /* 83 * Bi-directional NAT (inbound + outbound NAT): 84 * map $ext_if dynamic $local_ip2 <-> $pub_ip2 85 */ 86 { 87 REMOTE_IP2, 17000, PUB_IP2, 9000, 88 NPF_BINAT, IFNAME_EXT, PFIL_IN, 89 RESULT_PASS, LOCAL_IP2, 9000 90 }, 91 { 92 LOCAL_IP2, 9000, REMOTE_IP2, 17000, 93 NPF_BINAT, IFNAME_EXT, PFIL_OUT, 94 RESULT_PASS, PUB_IP2, 9000 95 }, 96 { 97 LOCAL_IP2, 18000, REMOTE_IP2, 9000, 98 NPF_BINAT, IFNAME_EXT, PFIL_OUT, 99 RESULT_PASS, PUB_IP2, 18000 100 }, 101 { 102 REMOTE_IP2, 9000, PUB_IP2, 18000, 103 NPF_BINAT, IFNAME_EXT, PFIL_IN, 104 RESULT_PASS, LOCAL_IP2, 18000 105 }, 106 107 }; 108 109 static bool 110 nmatch_addr(const char *saddr, const struct in_addr *addr2) 111 { 112 const in_addr_t addr1 = inet_addr(saddr); 113 return memcmp(&addr1, &addr2->s_addr, sizeof(in_addr_t)) != 0; 114 } 115 116 static bool 117 checkresult(bool verbose, unsigned i, struct mbuf *m, ifnet_t *ifp, int error) 118 { 119 const struct test_case *t = &test_cases[i]; 120 npf_cache_t npc = { .npc_info = 0 }; 121 nbuf_t nbuf; 122 123 if (verbose) { 124 printf("packet %d (expected %d ret %d)\n", i+1, t->ret, error); 125 } 126 if (error) { 127 return error == t->ret; 128 } 129 130 nbuf_init(&nbuf, m, ifp); 131 if (!npf_cache_all(&npc, &nbuf)) { 132 printf("error: could not fetch the packet data"); 133 return false; 134 } 135 136 const struct ip *ip = npc.npc_ip.v4; 137 const struct udphdr *uh = npc.npc_l4.udp; 138 139 if (verbose) { 140 printf("\tpost-translation: src %s (%d)", 141 inet_ntoa(ip->ip_src), ntohs(uh->uh_sport)); 142 printf(" dst %s (%d)\n", 143 inet_ntoa(ip->ip_dst), ntohs(uh->uh_dport)); 144 } 145 if (error != t->ret) { 146 return false; 147 } 148 149 const bool forw = t->di == PFIL_OUT; 150 const char *saddr = forw ? t->taddr : t->src; 151 const char *daddr = forw ? t->dst : t->taddr; 152 in_addr_t sport = forw ? t->tport : t->sport; 153 in_addr_t dport = forw ? t->dport : t->tport; 154 155 bool defect = false; 156 defect |= nmatch_addr(saddr, &ip->ip_src); 157 defect |= sport != ntohs(uh->uh_sport); 158 defect |= nmatch_addr(daddr, &ip->ip_dst); 159 defect |= dport != ntohs(uh->uh_dport); 160 return !defect; 161 } 162 163 static struct mbuf * 164 fill_packet(const struct test_case *t) 165 { 166 struct mbuf *m; 167 struct ip *ip; 168 struct udphdr *uh; 169 170 m = mbuf_construct(IPPROTO_UDP); 171 uh = mbuf_return_hdrs(m, false, &ip); 172 ip->ip_src.s_addr = inet_addr(t->src); 173 ip->ip_dst.s_addr = inet_addr(t->dst); 174 uh->uh_sport = htons(t->sport); 175 uh->uh_dport = htons(t->dport); 176 return m; 177 } 178 179 bool 180 npf_nat_test(bool verbose) 181 { 182 for (unsigned i = 0; i < __arraycount(test_cases); i++) { 183 const struct test_case *t = &test_cases[i]; 184 ifnet_t *ifp = ifunit(t->ifname); 185 struct mbuf *m = fill_packet(t); 186 int error; 187 bool ret; 188 189 if (ifp == NULL) { 190 printf("Interface %s is not configured.\n", t->ifname); 191 return false; 192 } 193 error = npf_packet_handler(NULL, &m, ifp, t->di); 194 ret = checkresult(verbose, i, m, ifp, error); 195 if (m) { 196 m_freem(m); 197 } 198 if (!ret) { 199 return false; 200 } 201 } 202 return true; 203 } 204