xref: /netbsd-src/usr.sbin/npf/npftest/libnpftest/npf_table_test.c (revision f3cfa6f6ce31685c6c4a758bc430e69eb99f50a4)
1 /*
2  * NPF tableset tests.
3  *
4  * Public Domain.
5  */
6 
7 #ifdef _KERNEL
8 #include <sys/types.h>
9 #include <sys/kmem.h>
10 #endif
11 
12 #ifdef __linux__
13 #include <endian.h>
14 #else
15 #include <sys/endian.h>
16 #endif
17 
18 #include "npf_impl.h"
19 #include "npf_test.h"
20 
21 static const char *ip_list[] = {
22 	"192.168.1.1",
23 	"10.0.0.1",
24 	"192.168.2.1",
25 	"10.1.0.1",
26 	"192.168.100.253",
27 	"10.0.5.1",
28 	"192.168.128.127",
29 	"10.0.0.2",
30 };
31 
32 static const uint8_t ip6_list[][16] = {
33 	{
34 		0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35 		0x02, 0xa0, 0xc0, 0xff, 0xfe, 0x10, 0x12, 0x34
36 	},
37 	{
38 		0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
39 		0x02, 0xa0, 0xc0, 0xff, 0x00, 0x00, 0x00, 0x00,
40 	},
41 	{
42 		0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
43 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
44 	},
45 	{
46 		0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
47 		0x02, 0xa0, 0xc0, 0xff, 0xfe, 0x10, 0x12, 0x30
48 	}
49 };
50 
51 #define	CHECK_TRUE(x)	\
52     if (!(x)) { printf("FAIL: %s line %d\n", __func__, __LINE__); return 0; }
53 
54 #define	IPSET_TID		0
55 #define	IPSET_NAME		"ipset-table"
56 
57 #define	LPM_TID			1
58 #define	LPM_NAME		"lpm-table"
59 
60 #define	CDB_TID			2
61 #define	CDB_NAME		"cdb-table"
62 
63 #define	IFADDR_TID		3
64 #define	IFADDR_NAME		".ifaddr-eth0"
65 
66 ///////////////////////////////////////////////////////////////////////////
67 
68 static bool
69 check_ip4(const npf_addr_t *addr, const char *ipstr)
70 {
71 	npf_addr_t addr_storage, *test_addr = &addr_storage;
72 	const int alen = sizeof(struct in_addr);
73 	test_addr->word32[0] = inet_addr(ipstr);
74 	return memcmp(addr, test_addr, alen) == 0;
75 }
76 
77 static bool
78 ip4list_insert_lookup(npf_table_t *t, unsigned i)
79 {
80 	npf_addr_t addr_storage, *addr = &addr_storage;
81 	const int alen = sizeof(struct in_addr);
82 	int error;
83 
84 	addr->word32[0] = inet_addr(ip_list[i]);
85 	error = npf_table_insert(t, alen, addr, NPF_NO_NETMASK);
86 	CHECK_TRUE(error == 0);
87 	error = npf_table_lookup(t, alen, addr);
88 	CHECK_TRUE(error == 0);
89 	return true;
90 }
91 
92 static bool
93 fill_with_ip4(npf_tableset_t *tblset)
94 {
95 	npf_addr_t addr_storage, *addr = &addr_storage;
96 	const int alen = sizeof(struct in_addr);
97 	const int nm = NPF_NO_NETMASK;
98 
99 	for (unsigned i = 0; i < __arraycount(ip_list); i++) {
100 		npf_table_t *t;
101 		int error;
102 
103 		addr->word32[0] = inet_addr(ip_list[i]);
104 
105 		t = npf_tableset_getbyname(tblset, IPSET_NAME);
106 		error = npf_table_insert(t, alen, addr, nm);
107 		CHECK_TRUE(error == 0);
108 		error = npf_table_insert(t, alen, addr, nm);
109 		CHECK_TRUE(error != 0); // duplicate
110 
111 		t = npf_tableset_getbyname(tblset, LPM_NAME);
112 		error = npf_table_insert(t, alen, addr, nm);
113 		CHECK_TRUE(error == 0);
114 		error = npf_table_insert(t, alen, addr, nm);
115 		CHECK_TRUE(error != 0); // duplicate
116 	}
117 	return true;
118 }
119 
120 static bool
121 verify_ip4(npf_tableset_t *tblset)
122 {
123 	npf_addr_t addr_storage, *addr = &addr_storage;
124 	const size_t alen = sizeof(struct in_addr);
125 	const int nm = NPF_NO_NETMASK;
126 	npf_table_t *t;
127 	int error;
128 
129 	/* Attempt to add duplicates - should fail. */
130 	addr->word32[0] = inet_addr(ip_list[0]);
131 
132 	t = npf_tableset_getbyname(tblset, IPSET_NAME);
133 	error = npf_table_insert(t, alen, addr, nm);
134 	CHECK_TRUE(error != 0);
135 
136 	t = npf_tableset_getbyname(tblset, LPM_NAME);
137 	error = npf_table_insert(t, alen, addr, nm);
138 	CHECK_TRUE(error != 0);
139 
140 	/* Match (validate) each IP entry. */
141 	for (unsigned i = 0; i < __arraycount(ip_list); i++) {
142 		addr->word32[0] = inet_addr(ip_list[i]);
143 
144 		t = npf_tableset_getbyname(tblset, IPSET_NAME);
145 		error = npf_table_lookup(t, alen, addr);
146 		CHECK_TRUE(error == 0);
147 
148 		t = npf_tableset_getbyname(tblset, LPM_NAME);
149 		error = npf_table_lookup(t, alen, addr);
150 		CHECK_TRUE(error == 0);
151 	}
152 	return true;
153 }
154 
155 static bool
156 clear_ip4(npf_tableset_t *tblset)
157 {
158 	npf_addr_t addr_storage, *addr = &addr_storage;
159 	const int alen = sizeof(struct in_addr);
160 	const int nm = NPF_NO_NETMASK;
161 
162 	for (unsigned i = 0; i < __arraycount(ip_list); i++) {
163 		npf_table_t *t;
164 		int error;
165 
166 		addr->word32[0] = inet_addr(ip_list[i]);
167 
168 		t = npf_tableset_getbyname(tblset, IPSET_NAME);
169 		error = npf_table_remove(t, alen, addr, nm);
170 		CHECK_TRUE(error == 0);
171 
172 		error = npf_table_remove(t, alen, addr, nm);
173 		CHECK_TRUE(error != 0);
174 
175 		t = npf_tableset_getbyname(tblset, LPM_NAME);
176 		error = npf_table_remove(t, alen, addr, nm);
177 		CHECK_TRUE(error == 0);
178 
179 		error = npf_table_remove(t, alen, addr, nm);
180 		CHECK_TRUE(error != 0);
181 	}
182 	return true;
183 }
184 
185 ///////////////////////////////////////////////////////////////////////////
186 
187 static bool
188 test_basic(npf_tableset_t *tblset)
189 {
190 	npf_addr_t addr_storage, *addr = &addr_storage;
191 	const int alen = sizeof(struct in_addr);
192 	npf_table_t *t;
193 	int error;
194 
195 	/* Basic IP set. */
196 	t = npf_table_create(IPSET_NAME, IPSET_TID, NPF_TABLE_IPSET, NULL, 0);
197 	CHECK_TRUE(t != NULL);
198 	error = npf_tableset_insert(tblset, t);
199 	CHECK_TRUE(error == 0);
200 
201 	/* Check for double-insert. */
202 	error = npf_tableset_insert(tblset, t);
203 	CHECK_TRUE(error != 0);
204 
205 	/* Longest-prefix match (LPM). */
206 	t = npf_table_create(LPM_NAME, LPM_TID, NPF_TABLE_LPM, NULL, 0);
207 	CHECK_TRUE(t != NULL);
208 	error = npf_tableset_insert(tblset, t);
209 	CHECK_TRUE(error == 0);
210 
211 	/* Table for interface addresses. */
212 	t = npf_table_create(IFADDR_NAME, IFADDR_TID, NPF_TABLE_IFADDR, NULL, 0);
213 	CHECK_TRUE(t != NULL);
214 	error = npf_tableset_insert(tblset, t);
215 	CHECK_TRUE(error == 0);
216 
217 	/*
218 	 * Attempt to match some non-existing entries - should fail.
219 	 */
220 	addr->word32[0] = inet_addr(ip_list[0]);
221 
222 	t = npf_tableset_getbyname(tblset, IPSET_NAME);
223 	error = npf_table_lookup(t, alen, addr);
224 	CHECK_TRUE(error != 0);
225 
226 	t = npf_tableset_getbyname(tblset, LPM_NAME);
227 	error = npf_table_lookup(t, alen, addr);
228 	CHECK_TRUE(error != 0);
229 
230 	return true;
231 }
232 
233 static bool
234 test_nocopy(npf_tableset_t *tblset)
235 {
236 	const int alen = sizeof(struct in_addr);
237 	const char *tables[] = { IPSET_NAME, LPM_NAME, IFADDR_NAME };
238 	npf_addr_t *addr, lookup_addr;
239 
240 	for (unsigned i = 0; i < __arraycount(tables); i++) {
241 		npf_table_t *t;
242 		int error;
243 
244 		addr = kmem_zalloc(sizeof(npf_addr_t), KM_SLEEP);
245 		assert(addr != NULL);
246 		addr->word32[0] = inet_addr("172.16.90.10");
247 
248 		t = npf_tableset_getbyname(tblset, tables[i]);
249 		(void)npf_table_flush(t);
250 
251 		error = npf_table_insert(t, alen, addr, NPF_NO_NETMASK);
252 		CHECK_TRUE(error == 0);
253 
254 		memcpy(&lookup_addr, addr, alen);
255 		memset(addr, 0xa5, alen); // explicit memset
256 
257 		error = npf_table_lookup(t, alen, &lookup_addr);
258 		CHECK_TRUE(error == 0);
259 
260 		CHECK_TRUE(*(volatile unsigned char *)addr == 0xa5);
261 		kmem_free(addr, sizeof(npf_addr_t));
262 	}
263 	return true;
264 }
265 
266 static bool
267 test_ip6(npf_tableset_t *tblset)
268 {
269 	npf_addr_t addr_storage, *addr = &addr_storage;
270 	const size_t alen = sizeof(struct in6_addr);
271 	const int nm = NPF_NO_NETMASK;
272 	npf_table_t *t;
273 	int error;
274 
275 	/* IPv6 addresses. */
276 	memcpy(addr, ip6_list[0], sizeof(ip6_list[0]));
277 
278 	t = npf_tableset_getbyname(tblset, IPSET_NAME);
279 	error = npf_table_insert(t, alen, addr, nm);
280 	CHECK_TRUE(error == 0);
281 	error = npf_table_lookup(t, alen, addr);
282 	CHECK_TRUE(error == 0);
283 	error = npf_table_remove(t, alen, addr, nm);
284 	CHECK_TRUE(error == 0);
285 
286 	t = npf_tableset_getbyname(tblset, LPM_NAME);
287 	error = npf_table_insert(t, alen, addr, nm);
288 	CHECK_TRUE(error == 0);
289 	error = npf_table_lookup(t, alen, addr);
290 	CHECK_TRUE(error == 0);
291 	error = npf_table_remove(t, alen, addr, nm);
292 	CHECK_TRUE(error == 0);
293 
294 	return true;
295 }
296 
297 static bool
298 test_lpm_masks4(npf_tableset_t *tblset)
299 {
300 	npf_table_t *t = npf_tableset_getbyname(tblset, LPM_NAME);
301 	npf_addr_t addr_storage, *addr = &addr_storage;
302 	const size_t alen = sizeof(struct in_addr);
303 	int error;
304 
305 	addr->word32[0] = inet_addr("172.16.90.0");
306 	error = npf_table_insert(t, alen, addr, 25);
307 	CHECK_TRUE(error == 0);
308 
309 	addr->word32[0] = inet_addr("172.16.90.126");
310 	error = npf_table_lookup(t, alen, addr);
311 	CHECK_TRUE(error == 0);
312 
313 	addr->word32[0] = inet_addr("172.16.90.128");
314 	error = npf_table_lookup(t, alen, addr);
315 	CHECK_TRUE(error != 0);
316 
317 	return true;
318 }
319 
320 static bool
321 test_lpm_masks6(npf_tableset_t *tblset)
322 {
323 	npf_table_t *t = npf_tableset_getbyname(tblset, LPM_NAME);
324 	npf_addr_t addr_storage, *addr = &addr_storage;
325 	const size_t alen = sizeof(struct in6_addr);
326 	int error;
327 
328 	/*
329 	 * 96
330 	 */
331 	memcpy(addr, ip6_list[1], sizeof(ip6_list[1]));
332 	error = npf_table_insert(t, alen, addr, 96);
333 	CHECK_TRUE(error == 0);
334 
335 	memcpy(addr, ip6_list[0], sizeof(ip6_list[0]));
336 	error = npf_table_lookup(t, alen, addr);
337 	CHECK_TRUE(error == 0);
338 
339 	memcpy(addr, ip6_list[1], sizeof(ip6_list[1]));
340 	error = npf_table_remove(t, alen, addr, 96);
341 	CHECK_TRUE(error == 0);
342 
343 	/*
344 	 * 32
345 	 */
346 	memcpy(addr, ip6_list[2], sizeof(ip6_list[2]));
347 	error = npf_table_insert(t, alen, addr, 32);
348 	CHECK_TRUE(error == 0);
349 
350 	memcpy(addr, ip6_list[0], sizeof(ip6_list[0]));
351 	error = npf_table_lookup(t, alen, addr);
352 	CHECK_TRUE(error == 0);
353 
354 	memcpy(addr, ip6_list[2], sizeof(ip6_list[2]));
355 	error = npf_table_remove(t, alen, addr, 32);
356 	CHECK_TRUE(error == 0);
357 
358 	/*
359 	 * 126
360 	 */
361 	memcpy(addr, ip6_list[3], sizeof(ip6_list[3]));
362 	error = npf_table_insert(t, alen, addr, 126);
363 	CHECK_TRUE(error == 0);
364 
365 	memcpy(addr, ip6_list[0], sizeof(ip6_list[0]));
366 	error = npf_table_lookup(t, alen, addr);
367 	CHECK_TRUE(error != 0);
368 
369 	memcpy(addr, ip6_list[3], sizeof(ip6_list[3]));
370 	error = npf_table_remove(t, alen, addr, 126);
371 	CHECK_TRUE(error == 0);
372 
373 	return true;
374 }
375 
376 static bool
377 test_const_table(npf_tableset_t *tblset, void *blob, size_t size)
378 {
379 	npf_addr_t addr_storage, *addr = &addr_storage;
380 	const int alen = sizeof(struct in_addr);
381 	npf_table_t *t;
382 	int error;
383 
384 	t = npf_table_create(CDB_NAME, CDB_TID, NPF_TABLE_CONST, blob, size);
385 	CHECK_TRUE(t != NULL);
386 
387 	error = npf_tableset_insert(tblset, t);
388 	CHECK_TRUE(error == 0);
389 
390 	addr->word32[0] = inet_addr(ip_list[0]);
391 	error = npf_table_lookup(t, alen, addr);
392 	CHECK_TRUE(error == 0);
393 
394 	for (unsigned i = 1; i < __arraycount(ip_list) - 1; i++) {
395 		addr->word32[0] = inet_addr(ip_list[i]);
396 		error = npf_table_lookup(t, alen, addr);
397 		CHECK_TRUE(error != 0);
398 	}
399 	return true;
400 }
401 
402 static bool
403 test_ifaddr_table(npf_tableset_t *tblset)
404 {
405 	npf_addr_t addr_storage, *addr = &addr_storage;
406 	npf_table_t *t = npf_tableset_getbyname(tblset, IFADDR_NAME);
407 	int error;
408 	bool ok;
409 
410 	/* Two IPv4 addresses. */
411 	ok = ip4list_insert_lookup(t, 0);
412 	CHECK_TRUE(ok);
413 
414 	ok = ip4list_insert_lookup(t, 1);
415 	CHECK_TRUE(ok);
416 
417 	/* And one IPv6 address. */
418 	memcpy(addr, ip6_list[0], sizeof(ip6_list[0]));
419 	error = npf_table_insert(t, sizeof(struct in6_addr), addr, NPF_NO_NETMASK);
420 	CHECK_TRUE(error == 0);
421 
422 	/*
423 	 * Get IPv4 addresses.
424 	 */
425 	addr = npf_table_getsome(t, sizeof(struct in_addr), 0);
426 	ok = check_ip4(addr, "192.168.1.1");
427 	CHECK_TRUE(ok);
428 
429 	addr = npf_table_getsome(t, sizeof(struct in_addr), 1);
430 	ok = check_ip4(addr, "10.0.0.1");
431 	CHECK_TRUE(ok);
432 
433 	addr = npf_table_getsome(t, sizeof(struct in_addr), 2);
434 	ok = check_ip4(addr, "192.168.1.1");
435 	CHECK_TRUE(ok);
436 
437 	return true;
438 }
439 
440 static void
441 test_ipset_gc(npf_tableset_t *tblset)
442 {
443 	npf_table_t *t = npf_tableset_getbyname(tblset, IPSET_NAME);
444 	npf_t *npf = npf_getkernctx();
445 
446 	npf_config_enter(npf);
447 	npf_table_gc(npf, t);
448 	npf_table_flush(t);
449 	npf_config_exit(npf);
450 }
451 
452 bool
453 npf_table_test(bool verbose, void *blob, size_t size)
454 {
455 	npf_tableset_t *tblset;
456 	bool ok;
457 
458 	(void)verbose;
459 
460 	tblset = npf_tableset_create(4);
461 	CHECK_TRUE(tblset != NULL);
462 
463 	ok = test_basic(tblset);
464 	CHECK_TRUE(ok);
465 
466 	/*
467 	 * Fill IPSET and LPM tables with IPv4 addresses.
468 	 * Keep them in the table during the other tests.
469 	 */
470 	ok = fill_with_ip4(tblset);
471 	CHECK_TRUE(ok);
472 
473 	ok = verify_ip4(tblset);
474 	CHECK_TRUE(ok);
475 
476 	ok = test_ip6(tblset);
477 	CHECK_TRUE(ok);
478 
479 	ok = test_lpm_masks4(tblset);
480 	CHECK_TRUE(ok);
481 
482 	ok = test_lpm_masks6(tblset);
483 	CHECK_TRUE(ok);
484 
485 	ok = test_const_table(tblset, blob, size);
486 	CHECK_TRUE(ok);
487 
488 	ok = test_ifaddr_table(tblset);
489 	CHECK_TRUE(ok);
490 
491 	/*
492 	 * Remove the above IPv4 addresses -- they must have been untouched.
493 	 */
494 	ok = clear_ip4(tblset);
495 	CHECK_TRUE(ok);
496 
497 	ok = test_nocopy(tblset);
498 	CHECK_TRUE(ok);
499 
500 	test_ipset_gc(tblset);
501 
502 	npf_tableset_destroy(tblset);
503 	return true;
504 }
505