1 /* $NetBSD: npf_nat_test.c,v 1.10 2016/12/26 23:05:05 christos Exp $ */ 2 3 /* 4 * NPF NAT test. 5 * 6 * Public Domain. 7 */ 8 9 #ifdef _KERNEL 10 #include <sys/types.h> 11 #endif 12 13 #include "npf_impl.h" 14 #include "npf_test.h" 15 16 #define RESULT_PASS 0 17 #define RESULT_BLOCK ENETUNREACH 18 19 #define NPF_BINAT (NPF_NATIN | NPF_NATOUT) 20 21 #define RANDOM_PORT 53472 22 23 static const struct test_case { 24 const char * src; 25 in_port_t sport; 26 const char * dst; 27 in_port_t dport; 28 int ttype; 29 const char * ifname; 30 int di; 31 int ret; 32 int af; 33 const char * taddr; 34 in_port_t tport; 35 } test_cases[] = { 36 37 /* 38 * Traditional NAPT (outbound NAT): 39 * map $ext_if dynamic $local_net -> $pub_ip1 40 */ 41 { 42 LOCAL_IP1, 15000, REMOTE_IP1, 7000, 43 NPF_NATOUT, IFNAME_EXT, PFIL_OUT, 44 RESULT_PASS, AF_INET, PUB_IP1, RANDOM_PORT 45 }, 46 { 47 LOCAL_IP1, 15000, REMOTE_IP1, 7000, 48 NPF_NATOUT, IFNAME_EXT, PFIL_OUT, 49 RESULT_PASS, AF_INET, PUB_IP1, RANDOM_PORT 50 }, 51 { 52 LOCAL_IP1, 15000, REMOTE_IP1, 7000, 53 NPF_NATOUT, IFNAME_EXT, PFIL_IN, 54 RESULT_BLOCK, AF_INET, NULL, 0 55 }, 56 { 57 REMOTE_IP1, 7000, LOCAL_IP1, 15000, 58 NPF_NATOUT, IFNAME_EXT, PFIL_IN, 59 RESULT_BLOCK, AF_INET, NULL, 0 60 }, 61 { 62 REMOTE_IP1, 7000, PUB_IP1, RANDOM_PORT, 63 NPF_NATOUT, IFNAME_INT, PFIL_IN, 64 RESULT_BLOCK, AF_INET, NULL, 0 65 }, 66 { 67 REMOTE_IP1, 7000, PUB_IP1, RANDOM_PORT, 68 NPF_NATOUT, IFNAME_EXT, PFIL_IN, 69 RESULT_PASS, AF_INET, LOCAL_IP1, 15000 70 }, 71 72 /* 73 * NAT redirect (inbound NAT): 74 * map $ext_if dynamic $local_ip1 port 6000 <- $pub_ip1 port 8000 75 */ 76 { 77 REMOTE_IP2, 16000, PUB_IP1, 8000, 78 NPF_NATIN, IFNAME_EXT, PFIL_IN, 79 RESULT_PASS, AF_INET, LOCAL_IP1, 6000 80 }, 81 { 82 LOCAL_IP1, 6000, REMOTE_IP2, 16000, 83 NPF_NATIN, IFNAME_EXT, PFIL_OUT, 84 RESULT_PASS, AF_INET, PUB_IP1, 8000 85 }, 86 87 /* 88 * Bi-directional NAT (inbound + outbound NAT): 89 * map $ext_if dynamic $local_ip2 <-> $pub_ip2 90 */ 91 { 92 REMOTE_IP2, 17000, PUB_IP2, 9000, 93 NPF_BINAT, IFNAME_EXT, PFIL_IN, 94 RESULT_PASS, AF_INET, LOCAL_IP2, 9000 95 }, 96 { 97 LOCAL_IP2, 9000, REMOTE_IP2, 17000, 98 NPF_BINAT, IFNAME_EXT, PFIL_OUT, 99 RESULT_PASS, AF_INET, PUB_IP2, 9000 100 }, 101 { 102 LOCAL_IP2, 18000, REMOTE_IP2, 9000, 103 NPF_BINAT, IFNAME_EXT, PFIL_OUT, 104 RESULT_PASS, AF_INET, PUB_IP2, 18000 105 }, 106 { 107 REMOTE_IP2, 9000, PUB_IP2, 18000, 108 NPF_BINAT, IFNAME_EXT, PFIL_IN, 109 RESULT_PASS, AF_INET, LOCAL_IP2, 18000 110 }, 111 112 /* 113 * Static NAT: plain translation both ways. 114 * map $ext_if static $local_ip3 <-> $pub_ip3 115 */ 116 { 117 LOCAL_IP3, 19000, REMOTE_IP3, 10000, 118 NPF_BINAT, IFNAME_EXT, PFIL_OUT, 119 RESULT_PASS, AF_INET, PUB_IP3, 19000 120 }, 121 { 122 REMOTE_IP3, 10000, PUB_IP3, 19000, 123 NPF_BINAT, IFNAME_EXT, PFIL_IN, 124 RESULT_PASS, AF_INET, LOCAL_IP3, 19000 125 }, 126 127 /* 128 * NPTv6 case: 129 * map $ext_if static algo npt66 $net6_inner <-> $net6_outer 130 */ 131 { 132 LOCAL_IP6, 1000, REMOTE_IP6, 1001, 133 NPF_BINAT, IFNAME_EXT, PFIL_OUT, 134 RESULT_PASS, AF_INET6, EXPECTED_IP6, 1000 135 }, 136 { 137 REMOTE_IP6, 1001, EXPECTED_IP6, 1000, 138 NPF_BINAT, IFNAME_EXT, PFIL_IN, 139 RESULT_PASS, AF_INET6, LOCAL_IP6, 1000 140 }, 141 142 }; 143 144 static bool 145 nmatch_addr(int af, const char *saddr, const npf_addr_t *addr2) 146 { 147 npf_addr_t addr1; 148 size_t len; 149 150 npf_inet_pton(af, saddr, &addr1); 151 len = af == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr); 152 return memcmp(&addr1, addr2, len) != 0; 153 } 154 155 static bool 156 checkresult(bool verbose, unsigned i, struct mbuf *m, ifnet_t *ifp, int error) 157 { 158 const struct test_case *t = &test_cases[i]; 159 npf_cache_t npc = { .npc_info = 0, .npc_ctx = npf_getkernctx() }; 160 const int af = t->af; 161 nbuf_t nbuf; 162 163 if (verbose) { 164 printf("packet %d (expected %d ret %d)\n", i+1, t->ret, error); 165 } 166 if (error) { 167 return error == t->ret; 168 } 169 170 nbuf_init(npf_getkernctx(), &nbuf, m, ifp); 171 npc.npc_nbuf = &nbuf; 172 if (!npf_cache_all(&npc)) { 173 printf("error: could not fetch the packet data"); 174 return false; 175 } 176 177 const struct udphdr *uh = npc.npc_l4.udp; 178 179 if (verbose) { 180 char sbuf[64], dbuf[64]; 181 182 npf_inet_ntop(af, npc.npc_ips[NPF_SRC], sbuf, sizeof(sbuf)); 183 npf_inet_ntop(af, npc.npc_ips[NPF_DST], dbuf, sizeof(dbuf)); 184 185 printf("\tpost-translation:"); 186 printf("src %s (%d) ", sbuf, ntohs(uh->uh_sport)); 187 printf("dst %s (%d)\n", dbuf, ntohs(uh->uh_dport)); 188 } 189 if (error != t->ret) { 190 return false; 191 } 192 193 const bool forw = t->di == PFIL_OUT; 194 const char *saddr = forw ? t->taddr : t->src; 195 const char *daddr = forw ? t->dst : t->taddr; 196 in_addr_t sport = forw ? t->tport : t->sport; 197 in_addr_t dport = forw ? t->dport : t->tport; 198 199 bool defect = false; 200 defect |= nmatch_addr(af, saddr, npc.npc_ips[NPF_SRC]); 201 defect |= sport != ntohs(uh->uh_sport); 202 defect |= nmatch_addr(af, daddr, npc.npc_ips[NPF_DST]); 203 defect |= dport != ntohs(uh->uh_dport); 204 205 return !defect; 206 } 207 208 static struct mbuf * 209 fill_packet(const struct test_case *t) 210 { 211 struct mbuf *m; 212 void *ipsrc, *ipdst; 213 struct udphdr *uh; 214 215 if (t->af == AF_INET6) { 216 struct ip6_hdr *ip6; 217 218 m = mbuf_construct6(IPPROTO_UDP); 219 uh = mbuf_return_hdrs6(m, &ip6); 220 ipsrc = &ip6->ip6_src, ipdst = &ip6->ip6_dst; 221 } else { 222 struct ip *ip; 223 224 m = mbuf_construct(IPPROTO_UDP); 225 uh = mbuf_return_hdrs(m, false, &ip); 226 ipsrc = &ip->ip_src.s_addr, ipdst = &ip->ip_dst.s_addr; 227 } 228 229 npf_inet_pton(t->af, t->src, ipsrc); 230 npf_inet_pton(t->af, t->dst, ipdst); 231 uh->uh_sport = htons(t->sport); 232 uh->uh_dport = htons(t->dport); 233 return m; 234 } 235 236 bool 237 npf_nat_test(bool verbose) 238 { 239 npf_t *npf = npf_getkernctx(); 240 241 for (unsigned i = 0; i < __arraycount(test_cases); i++) { 242 const struct test_case *t = &test_cases[i]; 243 ifnet_t *ifp = npf_test_getif(t->ifname); 244 struct mbuf *m = fill_packet(t); 245 int error; 246 bool ret; 247 248 if (ifp == NULL) { 249 printf("Interface %s is not configured.\n", t->ifname); 250 return false; 251 } 252 error = npf_packet_handler(npf, &m, ifp, t->di); 253 ret = checkresult(verbose, i, m, ifp, error); 254 if (m) { 255 m_freem(m); 256 } 257 if (!ret) { 258 return false; 259 } 260 } 261 return true; 262 } 263