summaryrefslogtreecommitdiffstats
path: root/tests/nl-test-util.h
blob: 02f8bf7b724f017df2fe29fb045e4168a438380a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
/* SPDX-License-Identifier: LGPL-2.1-only */

#ifndef __NL_TEST_UTIL_H__
#define __NL_TEST_UTIL_H__

#include <errno.h>
#include <sys/stat.h>
#include <check.h>
#include <string.h>
#include <stdbool.h>
#include <arpa/inet.h>

#include "netlink/object.h"
#include "netlink/cache.h"

#include "netlink-private/nl-auto.h"
#include "netlink-private/utils.h"

/*****************************************************************************/

static inline void _nltst_strfreev(char **strv)
{
	size_t i;

	if (strv) {
		for (i = 0; strv[i]; i++)
			free(strv[i]);
		free(strv);
	}
}

#define _nltst_auto_strfreev _nl_auto(_nltst_auto_strfreev_fcn)
_NL_AUTO_DEFINE_FCN_TYPED0(char **, _nltst_auto_strfreev_fcn, _nltst_strfreev);

/*****************************************************************************/

#ifndef ck_assert_ptr_nonnull
#define ck_assert_ptr_nonnull(ptr) ck_assert(ptr)
#endif

/*****************************************************************************/

void _nltst_get_urandom(void *ptr, size_t len);

uint32_t _nltst_rand_u32(void);

static inline uint32_t _nltst_rand_bool(void)
{
	return _nltst_rand_u32() % 2 == 0;
}

/*****************************************************************************/

#define _nltst_assert(expr)                                                    \
	({                                                                     \
		typeof(expr) _expr = (expr);                                   \
                                                                               \
		if (!_expr) {                                                  \
			ck_assert_msg(0, "assert(%s) failed", #expr);          \
		}                                                              \
		_expr;                                                         \
	})

#define _nltst_assert_errno(expr)                                              \
	do {                                                                   \
		if (expr) {                                                    \
		} else {                                                       \
			const int _errno = (errno);                            \
                                                                               \
			ck_assert_msg(0, "assert(%s) failed (errno=%d, %s)",   \
				      #expr, _errno, strerror(_errno));        \
		}                                                              \
	} while (0)

#define _nltst_assert_retcode(expr)                                                   \
	do {                                                                          \
		const int _r = (expr);                                                \
                                                                                      \
		if (_r < 0) {                                                         \
			ck_assert_msg(                                                \
				0, "command(%s) failed with return code %d",          \
				#expr, _r);                                           \
		}                                                                     \
		if (_r > 0) {                                                         \
			ck_assert_msg(                                                \
				0,                                                    \
				"command(%s) has unexpected positive return code %d", \
				#expr, _r);                                           \
		}                                                                     \
	} while (0)

#define _nltst_close(fd)                                                       \
	do {                                                                   \
		int _r;                                                        \
                                                                               \
		_r = _nl_close((fd));                                          \
		_nltst_assert_errno(_r == 0);                                  \
	} while (0)

#define _nltst_fclose(f)                                                       \
	do {                                                                   \
		int _r;                                                        \
                                                                               \
		_r = fclose((f));                                              \
		_nltst_assert_errno(_r == 0);                                  \
	} while (0)

#define _nltst_assert_link_exists_full(ifname, exists)                                                       \
	do {                                                                                                 \
		const bool _exists = (exists);                                                               \
		const char *_ifname = (ifname);                                                              \
		char _path[100];                                                                             \
		struct stat _stat;                                                                           \
		int _r;                                                                                      \
                                                                                                             \
		ck_assert_pstr_ne(_ifname, NULL);                                                            \
		ck_assert_int_lt(strlen(_ifname), IFNAMSIZ);                                                 \
                                                                                                             \
		strcpy(_path, "/sys/class/net/");                                                            \
		strcat(_path, _ifname);                                                                      \
		ck_assert_int_lt(strlen(_path), sizeof(_path));                                              \
                                                                                                             \
		_r = stat(_path, &_stat);                                                                    \
		if (_exists) {                                                                               \
			if (_r != 0) {                                                                       \
				const int _errsv = (errno);                                                  \
                                                                                                             \
				ck_assert_msg(                                                               \
					0,                                                                   \
					"link(%s) does not exist (stat(%s) failed with r=%d, errno=%d (%s)", \
					_ifname, _path, _r, _errsv,                                          \
					strerror(_errsv));                                                   \
			}                                                                                    \
		} else {                                                                                     \
			if (_r != -1 && errno != ENOENT) {                                                   \
				const int _errsv = (errno);                                                  \
                                                                                                             \
				ck_assert_msg(                                                               \
					0,                                                                   \
					"link(%s) should not exist but stat(%s) gave r=%d, errno=%d (%s)",   \
					_ifname, _path, _r, _errsv,                                          \
					strerror(_errsv));                                                   \
			}                                                                                    \
		}                                                                                            \
	} while (0);

#define _nltst_assert_link_exists(ifname)                                      \
	_nltst_assert_link_exists_full((ifname), true)

#define _nltst_assert_link_not_exists(ifname)                                  \
	_nltst_assert_link_exists_full((ifname), false)

/*****************************************************************************/

typedef union {
	in_addr_t addr4;
	struct in_addr a4;
	struct in6_addr a6;
} NLTstIPAddr;

static inline char *_nltst_inet_ntop(int addr_family, const void *addr,
				     char buf[static INET_ADDRSTRLEN])
{
	char *r;

	ck_assert(addr_family == AF_INET || addr_family == AF_INET6);
	ck_assert(addr);

	r = (char *)inet_ntop(addr_family, addr, buf,
			      (addr_family == AF_INET) ? INET_ADDRSTRLEN :
							       INET6_ADDRSTRLEN);
	ck_assert_ptr_eq(r, buf);
	ck_assert_int_lt(strlen(r), (addr_family == AF_INET) ?
					    INET_ADDRSTRLEN :
						  INET6_ADDRSTRLEN);
	return r;
}

static inline char *_nltst_inet_ntop_dup(int addr_family, const void *addr)
{
	return (char *)_nltst_inet_ntop(addr_family, addr,
					malloc((addr_family == AF_INET) ?
						       INET_ADDRSTRLEN :
							     INET6_ADDRSTRLEN));
}

static inline bool _nltst_inet_pton(int addr_family, const char *str,
				    int *out_addr_family, void *out_addr)
{
	NLTstIPAddr a;
	int r;

	ck_assert(addr_family == AF_UNSPEC || addr_family == AF_INET ||
		  addr_family == AF_INET6);

	/* when requesting @out_addr, then the addr-family must either be
	 * pre-determined or requested too. */
	ck_assert(!out_addr || out_addr_family || addr_family != AF_UNSPEC);

	if (!str)
		return false;

	if (addr_family == AF_UNSPEC)
		addr_family = strchr(str, ':') ? AF_INET6 : AF_INET;

	r = inet_pton(addr_family, str, &a);
	if (r != 1)
		return false;

	if (out_addr) {
		memcpy(out_addr, &a,
		       addr_family == AF_INET ? sizeof(in_addr_t) :
						      sizeof(struct in6_addr));
	}
	if (out_addr_family)
		*out_addr_family = addr_family;

	return true;
}

static inline bool _nltst_inet_valid(int addr_family, const char *addr)
{
	return _nltst_inet_pton(addr_family, addr, NULL, NULL);
}

static inline int _nltst_inet_addr_family(int addr_family, const char *addr)
{
	if (!_nltst_inet_pton(addr_family, addr, &addr_family, NULL))
		return AF_UNSPEC;
	return addr_family;
}

static inline char *_nltst_inet_normalize(int addr_family, const char *addr,
					  char buf[static INET_ADDRSTRLEN])
{
	NLTstIPAddr a;

	buf[0] = '\0';
	if (!_nltst_inet_pton(addr_family, addr, &addr_family, &a))
		return NULL;
	return _nltst_inet_ntop(addr_family, &a, buf);
}

/*****************************************************************************/

char *_nltst_strtok(const char **p_str);

char **_nltst_strtokv(const char *str);

#define _nltst_assert_strv_equal(strv1, strv2)                                 \
	do {                                                                   \
		typeof(strv1) _strv1 = (strv1);                                \
		typeof(strv2) _strv2 = (strv2);                                \
		_nl_unused const void *_strv1_typecheck1 = _strv1;             \
		_nl_unused const void *_strv2_typecheck1 = _strv2;             \
		_nl_unused const char *_strv1_typecheck2 =                     \
			_strv1 ? _strv1[0] : NULL;                             \
		_nl_unused const char *_strv2_typecheck2 =                     \
			_strv2 ? _strv2[0] : NULL;                             \
		size_t _i;                                                     \
                                                                               \
		ck_assert_int_eq(!!_strv1, !!_strv2);                          \
		if (_strv1) {                                                  \
			for (_i = 0; _strv1[_i] || _strv2[_i]; _i++) {         \
				ck_assert_str_eq(_strv1[_i], _strv2[_i]);      \
			}                                                      \
		}                                                              \
	} while (0)

#define _NLTST_CHARSET_SPACE " \n\r\t"

#define _nltst_char_is(ch, charset) (!!(strchr("" charset "", (ch))))

#define _nltst_char_is_space(ch) _nltst_char_is(ch, _NLTST_CHARSET_SPACE)

#define _nltst_str_skip_predicate(s, ch, predicate)                            \
	({                                                                     \
		typeof(s) _s1 = (s);                                           \
		_nl_unused const char *_s1_typecheck = (_s1);                  \
                                                                               \
		if (_s1) {                                                     \
			while (({                                              \
				const char ch = _s1[0];                        \
                                                                               \
				(ch != '\0') && (predicate);                   \
			}))                                                    \
				_s1++;                                         \
		}                                                              \
		_s1;                                                           \
	})

#define _nltst_str_skip_charset(s, charset)                                    \
	_nltst_str_skip_predicate(s, _ch, _nltst_char_is(_ch, "" charset ""))

#define _nltst_str_skip_space(s)                                               \
	_nltst_str_skip_charset(s, _NLTST_CHARSET_SPACE)

#define _nltst_str_has_prefix_and_space(s, prefix)                             \
	({                                                                     \
		typeof(s) _s2 = (s);                                           \
		_nl_unused const char *_s2_typecheck = (_s2);                  \
		const size_t _l = strlen("" prefix "");                        \
                                                                               \
		if (_s2) {                                                     \
			if ((strncmp(_s2, "" prefix "", _l)) == 0 &&           \
			    _nltst_char_is_space(_s2[_l]))                     \
				_s2 = _nltst_str_skip_space(&_s2[_l + 1]);     \
			else                                                   \
				_s2 = NULL;                                    \
		}                                                              \
		_s2;                                                           \
	})

#define _nltst_str_find_first_not_from_charset(s, charset)                     \
	({                                                                     \
		typeof(s) _s3 = (s);                                           \
		_nl_unused const char *_s3_typecheck = (_s3);                  \
		size_t _l3;                                                    \
                                                                               \
		_l3 = strspn(_s3, "" charset "");                              \
                                                                               \
		&_s3[_l3];                                                     \
	})

#define _nltst_str_find_first_from_charset(s, charset)                         \
	({                                                                     \
		typeof(s) _s3 = (s);                                           \
		_nl_unused const char *_s3_typecheck = (_s3);                  \
		size_t _l3;                                                    \
                                                                               \
		_l3 = strcspn(_s3, "" charset "");                             \
                                                                               \
		&_s3[_l3];                                                     \
	})

/*****************************************************************************/

void nltst_netns_fixture_setup(void);
void nltst_netns_fixture_teardown(void);

struct nltst_netns;

struct nltst_netns *nltst_netns_enter(void);
void nltst_netns_leave(struct nltst_netns *nsdata);

/*****************************************************************************/

typedef struct {
	int addr_family;
	int ifindex;
	int plen;
	char *addr;
	char *addr_pattern;
} NLTstSelectRoute;

#define _nltst_assert_select_route(select_route)                               \
	do {                                                                   \
		const NLTstSelectRoute *_select_route = (select_route);        \
                                                                               \
		ck_assert_ptr_nonnull(_select_route);                          \
		_nl_assert_addr_family_or_unspec(_select_route->addr_family);  \
		ck_assert_int_ge(_select_route->ifindex, 0);                   \
		ck_assert_int_ge(_select_route->plen, -1);                     \
		ck_assert_int_le(_select_route->plen,                          \
				 _select_route->addr_family == AF_INET ? 32 :  \
									       128); \
		ck_assert(!_select_route->addr || ({                           \
			char _buf[INET6_ADDRSTRLEN];                           \
			const char *_s2;                                       \
                                                                               \
			_s2 = _nltst_inet_normalize(                           \
				_select_route->addr_family,                    \
				_select_route->addr, _buf);                    \
			(_select_route->addr_family != AF_UNSPEC && _s2 &&     \
			 _nl_streq(_s2, _select_route->addr));                 \
		}));                                                           \
		ck_assert(!_select_route->addr_pattern ||                      \
			  !_select_route->addr);                               \
		ck_assert(!_select_route->addr_pattern ||                      \
			  _select_route->addr_family != AF_UNSPEC);            \
	} while (0)

void _nltst_select_route_clear(NLTstSelectRoute *select_route);

#define _nltst_auto_clear_select_route                                         \
	_nl_auto(_nltst_auto_clear_select_route_fcn)
_NL_AUTO_DEFINE_FCN_STRUCT(NLTstSelectRoute, _nltst_auto_clear_select_route_fcn,
			   _nltst_select_route_clear);

bool _nltst_select_route(struct nl_object *route,
			 const NLTstSelectRoute *selector);

void _nltst_expected_routes_parse(const char *str,
				  NLTstSelectRoute *out_select_route);

void _nltst_object_identical(const void *a, const void *b);

char *_nltst_object_to_string(struct nl_object *obj);

struct nl_object **_nltst_cache_get_all(struct nl_cache *cache,
					size_t *out_len);

struct nl_cache *_nltst_rtnl_link_alloc_cache(struct nl_sock *sk,
					      int addr_family, unsigned flags);

struct nl_cache *_nltst_rtnl_route_alloc_cache(struct nl_sock *sk,
					       int addr_family);

struct nl_sock *_nltst_socket(int protocol);

void _nltst_add_link(struct nl_sock *sk, const char *ifname, const char *kind);

void _nltst_delete_link(struct nl_sock *sk, const char *ifname);

void _nltst_assert_route_list(struct nl_object *const *objs, ssize_t len,
			      const char *const *expected_routes);

void _nltst_assert_route_cache_v(struct nl_cache *cache,
				 const char *const *expected_routes);

#define _nltst_assert_route_cache(cache, ...)                                  \
	_nltst_assert_route_cache_v(                                           \
		cache, ((const char *const[]){ __VA_ARGS__, NULL }))

#endif /* __NL_TEST_UTIL_H__ */