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