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