1 /* SPDX-License-Identifier: LGPL-2.1-only */
2
3 #include "nl-default.h"
4
5 #include "nl-test-util.h"
6
7 #include <fcntl.h>
8 #include <fnmatch.h>
9 #include <sched.h>
10 #include <stdio.h>
11 #include <sys/mount.h>
12
13 #include <netlink/netlink.h>
14 #include <netlink/route/link.h>
15 #include <netlink/route/route.h>
16 #include <netlink/socket.h>
17
18 #include "lib/route/nl-route.h"
19
20 #include "nl-aux-route/nl-route.h"
21
22 /*****************************************************************************/
23
_nltst_get_urandom(void * ptr,size_t len)24 void _nltst_get_urandom(void *ptr, size_t len)
25 {
26 int fd;
27 ssize_t nread;
28
29 ck_assert_int_gt(len, 0);
30 ck_assert_ptr_nonnull(ptr);
31
32 fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY);
33 _nltst_assert_errno(fd >= 0);
34
35 nread = read(fd, ptr, len);
36 _nltst_assert_errno(nread >= 0 && ((size_t)nread) == len);
37
38 _nltst_close(fd);
39 }
40
_nltst_rand_u32(void)41 uint32_t _nltst_rand_u32(void)
42 {
43 _nl_thread_local static unsigned short entropy[3];
44 _nl_thread_local static bool has_entropy = false;
45
46 if (!has_entropy) {
47 unsigned long long seed;
48 uint64_t seed64;
49 const char *s;
50 char *s_end;
51
52 memset(entropy, 0, sizeof(entropy));
53 s = getenv("NLTST_SEED_RAND");
54 if (!s)
55 seed = 0;
56 else if (s[0] != '\0') {
57 errno = 0;
58 seed = strtoull(s, &s_end, 10);
59 if (errno != 0 || s_end[0] != '\0') {
60 ck_assert_msg(
61 0,
62 "invalid NLTST_SEED_RAND=\"%s\". Must be an integer to seed the random numbers",
63 s);
64 }
65 } else
66 _nltst_get_urandom(&seed, sizeof(seed));
67
68 seed64 = seed;
69 printf("runs with NLTST_SEED_RAND=%" PRIu64 "\n", seed64);
70
71 entropy[0] = (seed64 >> 0) ^ (seed64 >> 48);
72 entropy[1] = (seed64 >> 16) ^ (seed64 >> 0);
73 entropy[2] = (seed64 >> 32) ^ (seed64 >> 16);
74 has_entropy = true;
75 }
76
77 _NL_STATIC_ASSERT(sizeof(long) >= sizeof(uint32_t));
78 return jrand48(entropy);
79 }
80
81 /*****************************************************************************/
82
83 #define _CANARY 539339
84
85 struct nltst_netns {
86 int canary;
87 };
88
89 /*****************************************************************************/
90
91 #define _assert_nltst_netns(nsdata) \
92 do { \
93 const struct nltst_netns *_nsdata = (nsdata); \
94 \
95 ck_assert_ptr_nonnull(_nsdata); \
96 ck_assert_int_eq(_nsdata->canary, _CANARY); \
97 } while (0)
98
99 static struct {
100 struct nltst_netns *nsdata;
101 } _netns_fixture_global;
102
nltst_netns_fixture_setup(void)103 void nltst_netns_fixture_setup(void)
104 {
105 ck_assert(!_netns_fixture_global.nsdata);
106
107 _netns_fixture_global.nsdata = nltst_netns_enter();
108 _assert_nltst_netns(_netns_fixture_global.nsdata);
109 }
110
nltst_netns_fixture_teardown(void)111 void nltst_netns_fixture_teardown(void)
112 {
113 _assert_nltst_netns(_netns_fixture_global.nsdata);
114 _nl_clear_pointer(&_netns_fixture_global.nsdata, nltst_netns_leave);
115 }
116
117 /*****************************************************************************/
118
unshare_user(void)119 static void unshare_user(void)
120 {
121 const uid_t uid = geteuid();
122 const gid_t gid = getegid();
123 FILE *f;
124 int r;
125
126 /* Become a root in new user NS. */
127 r = unshare(CLONE_NEWUSER);
128 _nltst_assert_errno(r == 0);
129
130 /* Since Linux 3.19 we have to disable setgroups() in order to map users.
131 * Just proceed if the file is not there. */
132 f = fopen("/proc/self/setgroups", "we");
133 if (f) {
134 r = fprintf(f, "deny");
135 _nltst_assert_errno(r > 0);
136 _nltst_fclose(f);
137 }
138
139 /* Map current UID to root in NS to be created. */
140 f = fopen("/proc/self/uid_map", "we");
141 if (!f) {
142 if (errno == EACCES) {
143 /* Accessing uid_map might fail due to sandboxing.
144 * We ignore that error and proceed with the test. It will probably
145 * still work. */
146 return;
147 }
148 _nltst_assert_errno(f);
149 }
150 r = fprintf(f, "0 %d 1", uid);
151 _nltst_assert_errno(r > 0);
152 _nltst_fclose(f);
153
154 /* Map current GID to root in NS to be created. */
155 f = fopen("/proc/self/gid_map", "we");
156 _nltst_assert_errno(f);
157 r = fprintf(f, "0 %d 1", gid);
158 _nltst_assert_errno(r > 0);
159 _nltst_fclose(f);
160 }
161
nltst_netns_enter(void)162 struct nltst_netns *nltst_netns_enter(void)
163 {
164 struct nltst_netns *nsdata;
165 int r;
166
167 nsdata = calloc(1, sizeof(struct nltst_netns));
168 ck_assert(nsdata);
169
170 nsdata->canary = _CANARY;
171
172 unshare_user();
173
174 r = unshare(CLONE_NEWNET | CLONE_NEWNS);
175 _nltst_assert_errno(r == 0);
176
177 /* We need a read-only /sys so that the platform knows there's no udev. */
178 mount(NULL, "/sys", "sysfs", MS_SLAVE, NULL);
179 r = mount("sys", "/sys", "sysfs", MS_RDONLY, NULL);
180 _nltst_assert_errno(r == 0);
181
182 return nsdata;
183 }
184
nltst_netns_leave(struct nltst_netns * nsdata)185 void nltst_netns_leave(struct nltst_netns *nsdata)
186 {
187 ck_assert(nsdata);
188 ck_assert_int_eq(nsdata->canary, _CANARY);
189
190 /* nltst_netns_leave() was supposed to enter the original namespaces again
191 * and undo enter.
192 *
193 * However, I could get it to work (setns() always fails with EPERM)
194 * and valgrind on current Ubuntu seems not to support setns() call.
195 *
196 * So, do nothing. It's not really a problem, because the next test
197 * either should unshare yet another namespace, or not care about
198 * such things. */
199
200 free(nsdata);
201 }
202
203 /*****************************************************************************/
204
_nltst_object_identical(const void * a,const void * b)205 void _nltst_object_identical(const void *a, const void *b)
206 {
207 struct nl_object *o_a = (void *)a;
208 struct nl_object *o_b = (void *)b;
209
210 ck_assert(a);
211 ck_assert(b);
212
213 ck_assert_int_eq(nl_object_identical(o_a, o_b), 1);
214 ck_assert_int_eq(nl_object_identical(o_b, o_a), 1);
215 ck_assert_int_eq(nl_object_diff64(o_b, o_a), 0);
216 ck_assert_int_eq(nl_object_diff64(o_a, o_b), 0);
217 ck_assert_int_eq(nl_object_diff(o_a, o_b), 0);
218 ck_assert_int_eq(nl_object_diff(o_b, o_a), 0);
219 }
220
221 /*****************************************************************************/
222
_nltst_object_to_string(const struct nl_object * obj)223 char *_nltst_object_to_string(const struct nl_object *obj)
224 {
225 size_t L = 1024;
226 size_t l;
227 char *s;
228 struct nl_dump_params dp;
229 char canary;
230
231 if (!obj)
232 return strdup("(null)");
233
234 s = malloc(L);
235 ck_assert_ptr_nonnull(s);
236
237 canary = _nltst_rand_u32();
238 s[L - 1u] = canary;
239
240 dp = (struct nl_dump_params){
241 .dp_type = NL_DUMP_LINE,
242 .dp_buf = s,
243 .dp_buflen = L - 1u,
244 };
245
246 nl_object_dump((struct nl_object *)obj, &dp);
247
248 l = strlen(s);
249 ck_assert_int_ge(l, 2);
250 ck_assert_int_lt(l, L);
251 ck_assert(canary == s[L - 1u]);
252 s = realloc(s, l + 1);
253 ck_assert_ptr_nonnull(s);
254
255 ck_assert_msg(s[l - 1u] == '\n',
256 "expects newline after dump. Got \"%s\"", s);
257 s[l - 1u] = '\0';
258
259 ck_assert_msg(!strchr(s, '\n'),
260 "no further newline expected. Got \"%s\"", s);
261
262 return s;
263 }
264
265 struct cache_get_all_data {
266 struct nl_object **arr;
267 size_t len;
268 size_t idx;
269 };
270
_cache_get_all_fcn(struct nl_object * obj,void * user_data)271 static void _cache_get_all_fcn(struct nl_object *obj, void *user_data)
272 {
273 struct cache_get_all_data *data = user_data;
274 size_t i;
275
276 ck_assert(obj);
277 ck_assert_int_lt(data->idx, data->len);
278
279 for (i = 0; i < data->idx; i++)
280 ck_assert_ptr_ne(data->arr[i], obj);
281
282 data->arr[data->idx++] = obj;
283 }
284
_nltst_cache_get_all(struct nl_cache * cache,size_t * out_len)285 struct nl_object **_nltst_cache_get_all(struct nl_cache *cache, size_t *out_len)
286 {
287 int nitems;
288 struct cache_get_all_data data = {
289 .idx = 0,
290 .len = 0,
291 };
292 size_t len2 = 0;
293 size_t i;
294 size_t j;
295
296 ck_assert(cache);
297
298 nitems = nl_cache_nitems(cache);
299 ck_assert_int_ge(nitems, 0);
300
301 data.len = nitems;
302 data.arr = malloc(sizeof(struct nl_object *) * (data.len + 1));
303 ck_assert_ptr_nonnull(data.arr);
304
305 nl_cache_foreach(cache, _cache_get_all_fcn, &data);
306
307 ck_assert_int_eq(data.idx, data.len);
308
309 ck_assert_int_le(data.len, SSIZE_MAX);
310
311 data.arr[data.len] = NULL;
312 if (out_len)
313 *out_len = data.len;
314
315 /* double check the result. */
316 for (struct nl_object *obj = nl_cache_get_first(cache); obj;
317 obj = nl_cache_get_next(obj)) {
318 ck_assert_ptr_eq(data.arr[len2], obj);
319 len2++;
320 }
321 ck_assert_ptr_null(data.arr[len2]);
322
323 for (i = 0; i < data.len; i++) {
324 ck_assert_ptr_nonnull(data.arr[i]);
325 for (j = i + 1; j < data.len; j++)
326 ck_assert_ptr_ne(data.arr[i], data.arr[j]);
327 }
328
329 return data.arr;
330 }
331
_nltst_cache_get_link(struct nl_cache * cache,const char * ifname)332 struct rtnl_link *_nltst_cache_get_link(struct nl_cache *cache,
333 const char *ifname)
334 {
335 _nl_auto_free struct nl_object **objs = NULL;
336 struct rtnl_link *link = NULL;
337 size_t i;
338
339 ck_assert_ptr_nonnull(cache);
340 ck_assert_ptr_nonnull(ifname);
341
342 objs = _nltst_cache_get_all(cache, NULL);
343 for (i = 0; objs[i]; i++) {
344 if (_nl_streq(rtnl_link_get_name((struct rtnl_link *)objs[i]),
345 ifname)) {
346 ck_assert_ptr_null(link);
347 link = (struct rtnl_link *)objs[i];
348 }
349 }
350
351 if (_nltst_rand_u32_range(5) == 0) {
352 _nl_auto_rtnl_link struct rtnl_link *link2 = NULL;
353
354 link2 = rtnl_link_get_by_name(cache, ifname);
355 ck_assert_ptr_eq(link2, link);
356 }
357
358 return link;
359 }
360
361 /*****************************************************************************/
362
_nltst_socket(int protocol)363 struct nl_sock *_nltst_socket(int protocol)
364 {
365 struct nl_sock *sk;
366 int r;
367
368 sk = nl_socket_alloc();
369 ck_assert(sk);
370
371 r = nl_connect(sk, protocol);
372 ck_assert_int_eq(r, 0);
373
374 if (_nltst_rand_u32_range(5) == 0)
375 nl_cache_free(_nltst_rtnl_link_alloc_cache(sk, AF_UNSPEC, 0));
376
377 if (_nltst_rand_u32_range(5) == 0)
378 nl_cache_free(_nltst_rtnl_route_alloc_cache(
379 sk, _nltst_rand_select(AF_UNSPEC, AF_INET, AF_INET6)));
380
381 return sk;
382 }
383
_nltst_add_link(struct nl_sock * sk,const char * ifname,const char * kind,int * out_ifindex)384 void _nltst_add_link(struct nl_sock *sk, const char *ifname, const char *kind,
385 int *out_ifindex)
386 {
387 _nl_auto_nl_socket struct nl_sock *sk_free = NULL;
388 _nl_auto_rtnl_link struct rtnl_link *link = NULL;
389 _nl_auto_nl_cache struct nl_cache *cache = NULL;
390 struct rtnl_link *link2;
391 int ifindex;
392 int r;
393
394 ck_assert(ifname);
395 ck_assert(kind);
396
397 if (_nltst_rand_u32_range(5) == 0)
398 _nltst_assert_link_not_exists(ifname);
399
400 if (!sk) {
401 sk = _nltst_socket(NETLINK_ROUTE);
402 sk_free = sk;
403 }
404
405 link = rtnl_link_alloc();
406 ck_assert(link);
407
408 r = rtnl_link_set_type(link, kind);
409 ck_assert_int_eq(r, 0);
410
411 rtnl_link_set_name(link, ifname);
412
413 r = rtnl_link_add(sk, link, NLM_F_CREATE);
414 ck_assert_int_eq(r, 0);
415
416 if (!out_ifindex && _nltst_rand_u32_range(5) != 0)
417 return;
418
419 cache = _nltst_rtnl_link_alloc_cache(sk, AF_UNSPEC, 0);
420
421 link2 = _nltst_cache_get_link(cache, ifname);
422 ck_assert_ptr_nonnull(link2);
423
424 ifindex = rtnl_link_get_ifindex(link2);
425 ck_assert_int_gt(ifindex, 0);
426
427 if (out_ifindex)
428 *out_ifindex = ifindex;
429 }
430
_nltst_delete_link(struct nl_sock * sk,const char * ifname)431 void _nltst_delete_link(struct nl_sock *sk, const char *ifname)
432 {
433 _nl_auto_nl_socket struct nl_sock *sk_free = NULL;
434 _nl_auto_rtnl_link struct rtnl_link *link = NULL;
435
436 _nltst_assert_link_exists(ifname);
437
438 if (!sk) {
439 sk = _nltst_socket(NETLINK_ROUTE);
440 sk_free = sk;
441 }
442
443 if (_nltst_rand_u32_range(5) == 0) {
444 _nl_auto_nl_cache struct nl_cache *cache = NULL;
445
446 cache = _nltst_rtnl_link_alloc_cache(sk, AF_UNSPEC, 0);
447 ck_assert_ptr_nonnull(_nltst_cache_get_link(cache, ifname));
448 }
449
450 link = rtnl_link_alloc();
451 ck_assert_ptr_nonnull(link);
452
453 rtnl_link_set_name(link, ifname);
454
455 _nltst_assert_retcode(rtnl_link_delete(sk, link));
456
457 _nltst_assert_link_not_exists(ifname);
458 }
459
_nltst_get_link(struct nl_sock * sk,const char * ifname,int * out_ifindex,struct rtnl_link ** out_link)460 void _nltst_get_link(struct nl_sock *sk, const char *ifname, int *out_ifindex,
461 struct rtnl_link **out_link)
462 {
463 _nl_auto_nl_cache struct nl_cache *cache = NULL;
464 struct rtnl_link *link;
465
466 if (_nltst_rand_u32_range(5) == 0)
467 _nltst_assert_link_exists(ifname);
468
469 cache = _nltst_rtnl_link_alloc_cache(sk, AF_UNSPEC, 0);
470
471 link = _nltst_cache_get_link(cache, ifname);
472 ck_assert(link);
473
474 if (out_ifindex)
475 *out_ifindex = rtnl_link_get_ifindex(link);
476
477 if (out_link) {
478 nl_object_get((struct nl_object *)link);
479 *out_link = link;
480 }
481 }
482
_nltst_rtnl_link_alloc_cache(struct nl_sock * sk,int addr_family,unsigned flags)483 struct nl_cache *_nltst_rtnl_link_alloc_cache(struct nl_sock *sk,
484 int addr_family, unsigned flags)
485 {
486 _nl_auto_nl_socket struct nl_sock *sk_free = NULL;
487 struct nl_cache *cache;
488 int r;
489
490 if (!sk) {
491 sk = _nltst_socket(NETLINK_ROUTE);
492 sk_free = sk;
493 }
494
495 if (flags == 0 && _nltst_rand_bool())
496 r = rtnl_link_alloc_cache(sk, addr_family, &cache);
497 else
498 r = rtnl_link_alloc_cache_flags(sk, addr_family, &cache, flags);
499
500 _nltst_assert_retcode(r);
501
502 if (_nltst_rand_u32_range(5) == 0)
503 free(_nltst_cache_get_all(cache, NULL));
504
505 return _nltst_assert(cache);
506 }
507
_nltst_rtnl_route_alloc_cache(struct nl_sock * sk,int addr_family)508 struct nl_cache *_nltst_rtnl_route_alloc_cache(struct nl_sock *sk,
509 int addr_family)
510 {
511 struct nl_cache *cache;
512
513 ck_assert_ptr_nonnull(sk);
514 ck_assert(addr_family == AF_UNSPEC || addr_family == AF_INET ||
515 addr_family == AF_INET6);
516
517 _nltst_assert_retcode(
518 rtnl_route_alloc_cache(sk, addr_family, 0, &cache));
519
520 if (_nltst_rand_u32_range(5) == 0)
521 free(_nltst_cache_get_all(cache, NULL));
522
523 return _nltst_assert(cache);
524 }
525
526 /*****************************************************************************/
527
_nltst_strtok(const char ** p_str)528 char *_nltst_strtok(const char **p_str)
529 {
530 const char *str;
531 _nl_auto_free char *dst = NULL;
532 size_t dst_len = 0;
533 size_t dst_alloc = 0;
534 size_t i;
535
536 ck_assert_ptr_nonnull(p_str);
537
538 str = _nltst_str_skip_space(*p_str);
539
540 if (str[0] == '\0') {
541 *p_str = str;
542 return NULL;
543 }
544
545 dst_len = 0;
546 dst_alloc = 10;
547 dst = malloc(dst_alloc);
548 ck_assert_ptr_nonnull(dst);
549
550 i = 0;
551 while (true) {
552 char ch1 = '\0';
553 char ch2 = '\0';
554
555 /* We take the first word, up until whitespace. Note that backslash
556 * escape is honored, so you can backslash escape spaces. The returned
557 * string will NOT have backslashes removed. */
558
559 if (str[i] == '\0') {
560 *p_str = &str[i];
561 break;
562 }
563 if (_nltst_char_is_space(str[i])) {
564 *p_str = _nltst_str_skip_space(&str[i + 1]);
565 break;
566 }
567 ch1 = str[i];
568 if (str[i] == '\\') {
569 if (str[i + 1] != '\0') {
570 ch2 = str[i + 1];
571 i += 2;
572 } else
573 i += 1;
574 } else
575 i += 1;
576
577 if (dst_len + 3 >= dst_alloc) {
578 dst_alloc *= 2;
579 dst = realloc(dst, dst_alloc);
580 ck_assert_ptr_nonnull(dst);
581 }
582 dst[dst_len++] = ch1;
583 if (ch2 != '\0')
584 dst[dst_len++] = ch2;
585 }
586
587 ck_assert_int_gt(dst_len, 0);
588 return strndup(dst, dst_len);
589 }
590
_nltst_strtokv(const char * str)591 char **_nltst_strtokv(const char *str)
592 {
593 _nl_auto_free char *s = NULL;
594 _nltst_auto_strfreev char **result = NULL;
595 size_t r_len = 0;
596 size_t r_alloc = 0;
597
598 if (!str)
599 return NULL;
600
601 r_alloc = 4;
602 result = malloc(sizeof(char *) * r_alloc);
603 ck_assert_ptr_nonnull(result);
604
605 while ((s = _nltst_strtok(&str))) {
606 if (r_len + 2 >= r_alloc) {
607 r_alloc *= 2;
608 result = realloc(result, sizeof(char *) * r_alloc);
609 ck_assert_ptr_nonnull(result);
610 }
611 result[r_len++] = _nl_steal_pointer(&s);
612 }
613 ck_assert_int_lt(r_len, r_alloc);
614 result[r_len] = NULL;
615 return _nl_steal_pointer(&result);
616 }
617
618 /*****************************************************************************/
619
_nltst_assert_link_exists_full(const char * ifname,bool exists)620 void _nltst_assert_link_exists_full(const char *ifname, bool exists)
621 {
622 _nl_auto_nl_cache struct nl_cache *cache = NULL;
623 _nl_auto_rtnl_link struct rtnl_link *link_clone = NULL;
624 struct rtnl_link *link;
625 char path[100];
626 struct stat st;
627 int rnd;
628 int r;
629
630 ck_assert_pstr_ne(ifname, NULL);
631 ck_assert_int_lt(strlen(ifname), IFNAMSIZ);
632
633 strcpy(path, "/sys/class/net/");
634 strcat(path, ifname);
635 ck_assert_int_lt(strlen(path), sizeof(path));
636
637 r = stat(path, &st);
638 if (exists) {
639 if (r != 0) {
640 const int errsv = (errno);
641
642 ck_assert_msg(
643 0,
644 "link(%s) does not exist (stat(%s) failed with r=%d, errno=%d (%s)",
645 ifname, path, r, errsv, strerror(errsv));
646 }
647 } else {
648 if (r != -1 && errno != ENOENT) {
649 const int errsv = (errno);
650
651 ck_assert_msg(
652 0,
653 "link(%s) should not exist but stat(%s) gave r=%d, errno=%d (%s)",
654 ifname, path, r, errsv, strerror(errsv));
655 }
656 }
657
658 rnd = _nltst_rand_u32_range(3);
659
660 if (rnd == 0)
661 return;
662
663 cache = _nltst_rtnl_link_alloc_cache(NULL, AF_UNSPEC, 0);
664
665 link = _nltst_cache_get_link(cache, ifname);
666 if (exists)
667 ck_assert_ptr_nonnull(link);
668 else
669 ck_assert_ptr_null(link);
670
671 if (!link || rnd == 1)
672 return;
673
674 link_clone =
675 (struct rtnl_link *)nl_object_clone((struct nl_object *)link);
676 ck_assert(link_clone);
677
678 /* FIXME: we would expect that the cloned object is identical. It is not. */
679 /* _nltst_object_identical(link, link_clone); */
680 }
681
682 /*****************************************************************************/
683
_nltst_in_ci(void)684 bool _nltst_in_ci(void)
685 {
686 return _nl_streq0(getenv("NLTST_IN_CI"), "1");
687 }
688
689 /*****************************************************************************/
690
_nltst_has_iproute2(void)691 bool _nltst_has_iproute2(void)
692 {
693 static int has = -1;
694
695 if (has == -1)
696 has = (system("ip link &>/dev/null") == 0);
697
698 return has;
699 }
700
_nltst_skip_no_iproute2(const char * msg)701 bool _nltst_skip_no_iproute2(const char *msg)
702 {
703 if (_nltst_has_iproute2())
704 return false;
705
706 ck_assert_msg(
707 !_nltst_in_ci(),
708 "We seem to not have iproute2, but we are in NLTST_IN_CI=1. This is fatal.");
709
710 printf("skip test due to missing iproute2%s%s%s\n", msg ? " (" : "",
711 msg ?: "", msg ? ")" : "");
712 return true;
713 }
714
715 /*****************************************************************************/
716
_nltst_select_route_clear(NLTstSelectRoute * select_route)717 void _nltst_select_route_clear(NLTstSelectRoute *select_route)
718 {
719 _nltst_assert_select_route(select_route);
720
721 _nl_clear_free(&select_route->addr);
722 _nl_clear_free(&select_route->addr_pattern);
723 }
724
_nltst_select_route_cmp(const NLTstSelectRoute * select_route1,const NLTstSelectRoute * select_route2)725 int _nltst_select_route_cmp(const NLTstSelectRoute *select_route1,
726 const NLTstSelectRoute *select_route2)
727 {
728 _NL_CMP_SELF(select_route1, select_route2);
729 _NL_CMP_FIELD_STR0(select_route1, select_route2, addr);
730 _NL_CMP_FIELD_STR0(select_route1, select_route2, addr_pattern);
731 _NL_CMP_FIELD(select_route1, select_route2, addr_family);
732 _NL_CMP_FIELD(select_route1, select_route2, ifindex);
733 _NL_CMP_FIELD(select_route1, select_route2, plen);
734 return 0;
735 }
736
_nltst_select_route_to_string(const NLTstSelectRoute * select_route)737 char *_nltst_select_route_to_string(const NLTstSelectRoute *select_route)
738 {
739 char buf[1024];
740 const char *family;
741 char b_plen[100];
742
743 _nltst_assert_select_route(select_route);
744
745 if (select_route->addr_family == AF_INET)
746 family = "4 ";
747 else if (select_route->addr_family == AF_INET6)
748 family = "6 ";
749 else
750 family = "";
751
752 b_plen[0] = '\0';
753 if (select_route->plen != -1)
754 _nltst_sprintf_arr(b_plen, "/%d", select_route->plen);
755
756 _nltst_sprintf_arr(buf,
757 "%s"
758 "%s"
759 "%s"
760 "",
761 family,
762 select_route->addr_pattern ?: select_route->addr,
763 b_plen);
764 return _nltst_strdup(buf);
765 }
766
_nltst_select_route_parse(const char * str,NLTstSelectRoute * out_select_route)767 void _nltst_select_route_parse(const char *str,
768 NLTstSelectRoute *out_select_route)
769 {
770 _nltst_auto_strfreev char **tokens0 = NULL;
771 const char *const *tokens;
772 int addr_family = AF_UNSPEC;
773 int addr_family2 = AF_UNSPEC;
774 NLTstIPAddr addr;
775 int plen = -1;
776 _nl_auto_free char *addr_free = NULL;
777 const char *s_addr_pattern;
778 const char *s_addr = NULL;
779 const char *s;
780 const char *s1;
781
782 ck_assert_ptr_nonnull(str);
783 _nltst_assert_select_route(out_select_route);
784
785 tokens0 = _nltst_strtokv(str);
786 tokens = (const char *const *)tokens0;
787
788 s = tokens[0];
789 if (!s)
790 ck_abort_msg("invalid empty route pattern \"%s\"", str);
791 if (_nl_streq(s, "4") || _nl_streq(s, "inet") ||
792 _nl_streq(s, "inet4")) {
793 addr_family = AF_INET;
794 tokens++;
795 } else if (_nl_streq(s, "6") || _nl_streq(s, "inet6")) {
796 addr_family = AF_INET6;
797 tokens++;
798 }
799
800 s_addr_pattern = tokens[0];
801 if (!s_addr_pattern) {
802 ck_abort_msg(
803 "the route pattern \"%s\" is invalid and contains no destination address",
804 str);
805 }
806 tokens++;
807
808 s = strchr(s_addr_pattern, '/');
809 if (s) {
810 long int plen2;
811
812 if (s == s_addr_pattern) {
813 ck_abort_msg(
814 "the route pattern \"%s\" contains no valid destination address",
815 str);
816 }
817 addr_free = strndup(s_addr_pattern, s - s_addr_pattern);
818 s_addr_pattern = addr_free;
819 s++;
820
821 errno = 0;
822 plen2 = strtol(s, (char **)&s1, 10);
823 if (errno != 0 || s1[0] != '\0' || plen2 < 0 || plen2 > 128 ||
824 ((_nltst_str_find_first_not_from_charset(
825 s, "0123456789"))[0] != '\0')) {
826 ck_abort_msg(
827 "the route pattern \"%s\" contains no valid destination address",
828 str);
829 }
830 plen = plen2;
831 }
832 if ((_nltst_str_find_first_not_from_charset(
833 s_addr_pattern, "abcdefABCDEF0123456789:.?*"))[0] != '\0') {
834 ck_abort_msg(
835 "the route pattern \"%s\" contains no valid destination address",
836 str);
837 }
838 if (_nltst_inet_pton(addr_family, s_addr_pattern, &addr_family2,
839 &addr)) {
840 free(addr_free);
841 addr_free = _nltst_inet_ntop_dup(addr_family2, &addr);
842 s_addr_pattern = addr_free;
843 addr_family = addr_family2;
844 } else {
845 if (addr_family == AF_UNSPEC) {
846 ck_abort_msg(
847 "the route pattern \"%s\" contains a wild card address, it requires the address family",
848 str);
849 }
850 }
851
852 ck_assert(addr_family == AF_INET || addr_family == AF_INET6);
853
854 if (plen > (addr_family == AF_INET ? 32 : 128)) {
855 ck_abort_msg(
856 "the route pattern \"%s\" contains no valid destination address (prefix length too large)",
857 str);
858 }
859 ck_assert_int_ge(plen, -1);
860
861 s = tokens[0];
862 if (s) {
863 ck_abort_msg("the route pattern \"%s\" contains extra tokens",
864 str);
865 }
866
867 if (_nltst_inet_valid(addr_family, s_addr_pattern))
868 _NL_SWAP(&s_addr, &s_addr_pattern);
869
870 _nltst_select_route_clear(out_select_route);
871 memset(out_select_route, 0, sizeof(*out_select_route));
872 *out_select_route = (NLTstSelectRoute){
873 .addr_family = addr_family,
874 .plen = plen,
875 .ifindex = 0,
876 .addr = s_addr ? strdup(s_addr) : NULL,
877 .addr_pattern = s_addr_pattern ? strdup(s_addr_pattern) : NULL,
878 };
879 ck_assert(!s_addr || out_select_route->addr);
880 ck_assert(!s_addr_pattern || out_select_route->addr_pattern);
881 _nltst_assert_select_route(out_select_route);
882 }
883
_nltst_select_route_match(struct nl_object * route,const NLTstSelectRoute * select_route,bool do_assert)884 bool _nltst_select_route_match(struct nl_object *route,
885 const NLTstSelectRoute *select_route,
886 bool do_assert)
887 {
888 struct nl_addr *addr;
889 struct rtnl_route *route_;
890 int i;
891 char sbuf1[200];
892
893 ck_assert_ptr_nonnull(route);
894 ck_assert_str_eq(nl_object_get_type(route), "route/route");
895
896 if (!select_route)
897 return true;
898
899 route_ = (struct rtnl_route *)route;
900
901 _nltst_assert_select_route(select_route);
902
903 #define _check(cond, msg, ...) \
904 do { \
905 if (do_assert) { \
906 _nl_auto_free char *s1 = NULL; \
907 _nl_auto_free char *s2 = NULL; \
908 \
909 ck_assert_msg( \
910 (cond), \
911 "Checking condition \"%s\" for route \"%s\" (expected \"%s\") failed (msg: " msg \
912 ")", \
913 #cond, (s1 = _nltst_object_to_string(route)), \
914 (s2 = _nltst_select_route_to_string( \
915 select_route)), \
916 ##__VA_ARGS__); \
917 } else if (cond) { \
918 } else { \
919 return false; \
920 } \
921 } while (0)
922
923 if (select_route->addr_family != AF_UNSPEC) {
924 _check(rtnl_route_get_family(route_) ==
925 select_route->addr_family,
926 "mismatching address family");
927 }
928
929 if (select_route->ifindex != 0) {
930 struct nl_list_head *list;
931 struct rtnl_nexthop *nh;
932 size_t n;
933 struct rtnl_nexthop *nh2;
934
935 list = rtnl_route_get_nexthops(route_);
936 _check(list, "no nexthops for ifindex");
937
938 n = 0;
939 nl_list_for_each_entry(nh, list, rtnh_list) {
940 nh2 = nh;
941 n++;
942 }
943 _check(n == 1, "expects one nexthop for ifindex but got %zu",
944 n);
945 ck_assert_ptr_nonnull(nh2);
946
947 i = rtnl_route_nh_get_ifindex(nh2);
948 _check(i == select_route->ifindex,
949 "route has unexpected ifindex %d for next hop", i);
950 }
951
952 addr = rtnl_route_get_dst(route_);
953
954 if (addr) {
955 if (select_route->addr_family != AF_UNSPEC) {
956 _check(nl_addr_get_family(addr) ==
957 select_route->addr_family,
958 "unexecpted address family of dst");
959 }
960 }
961
962 if (select_route->plen != -1) {
963 _check(addr, "missing address");
964 _check(nl_addr_get_prefixlen(addr) ==
965 (unsigned)select_route->plen,
966 "unexpected prefix length");
967 }
968 if (select_route->addr || select_route->addr_pattern) {
969 _check(addr, "missing address");
970
971 _nl_inet_ntop(nl_addr_get_family(addr),
972 nl_addr_get_binary_addr(addr), sbuf1);
973
974 ck_assert(strlen(sbuf1) > 0);
975 ck_assert(strlen(sbuf1) < sizeof(sbuf1));
976
977 if (select_route->addr) {
978 _check(_nl_streq(sbuf1, select_route->addr),
979 "unexpected address, \"%s\" does not match \"%s\"",
980 sbuf1, select_route->addr);
981 }
982 if (select_route->addr_pattern) {
983 _check(fnmatch(select_route->addr_pattern, sbuf1, 0) ==
984 0,
985 "unexpected address, \"%s\" does not match pattern \"%s\"",
986 sbuf1, select_route->addr_pattern);
987 }
988 }
989
990 #undef _check
991
992 return false;
993 }
994
995 /*****************************************************************************/
996
_nltst_assert_route_list(struct nl_object * const * objs,ssize_t len,const char * const * expected_routes)997 void _nltst_assert_route_list(struct nl_object *const *objs, ssize_t len,
998 const char *const *expected_routes)
999 {
1000 size_t l;
1001 size_t i;
1002
1003 if (len < 0) {
1004 l = 0;
1005 if (objs) {
1006 while (objs[l])
1007 l++;
1008 }
1009 } else
1010 l = len;
1011
1012 for (i = 0; i < l; i++) {
1013 struct nl_object *route = objs[i];
1014 _nltst_auto_clear_select_route NLTstSelectRoute select_route = {
1015 0
1016 };
1017 _nl_auto_free char *s = _nltst_object_to_string(route);
1018
1019 if (!expected_routes[i]) {
1020 ck_abort_msg(
1021 "No more expected route, but have route %zu (of %zu) as %s",
1022 i + 1, l, s);
1023 }
1024
1025 _nltst_select_route_parse(expected_routes[i], &select_route);
1026
1027 _nltst_select_route_match(route, &select_route, true);
1028 }
1029 }
1030
_nltst_assert_route_cache_v(struct nl_cache * cache,const char * const * expected_routes)1031 void _nltst_assert_route_cache_v(struct nl_cache *cache,
1032 const char *const *expected_routes)
1033 {
1034 _nl_auto_free struct nl_object **objs = NULL;
1035 size_t len;
1036
1037 ck_assert(cache);
1038 ck_assert(expected_routes);
1039
1040 objs = _nltst_cache_get_all(cache, &len);
1041
1042 _nltst_assert_route_list(objs, len, expected_routes);
1043 }
1044