npf_nat_test.c revision 1.9 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