xref: /netbsd-src/usr.sbin/npf/npftest/libnpftest/npf_nat_test.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
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