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