xref: /aosp_15_r20/external/libnl/tests/nl-test-util.c (revision 4dc78e53d49367fa8e61b07018507c90983a077d)
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