/* SPDX-License-Identifier: LGPL-2.1-only */ #ifndef __NL_TEST_UTIL_H__ #define __NL_TEST_UTIL_H__ #include #include #include #include #include #include #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 #ifndef ck_assert_pstr_ne #define ck_assert_pstr_ne(a, b) \ do { \ const char *_a = (a); \ const char *_b = (b); \ \ ck_assert(!(_a == _b || (_a && _b && strcmp(_a, _b) == 0))); \ } while (0) #endif #ifndef ck_assert_ptr_null #define ck_assert_ptr_null(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_u32_range(uint32_t n) { uint32_t rem; uint32_t i; if (n == 0) return _nltst_rand_u32(); if (n == 1) return 0; rem = UINT32_MAX % n; for (;;) { i = _nltst_rand_u32(); if (i < (UINT32_MAX - rem)) return i % n; } } static inline bool _nltst_rand_bool(void) { return _nltst_rand_u32() % 2 == 0; } #define _nltst_rand_select(a, ...) \ ({ \ const typeof(a) _lst[] = { (a), ##__VA_ARGS__ }; \ \ _lst[_nltst_rand_u32_range(_NL_N_ELEMENTS(_lst))]; \ }) /*****************************************************************************/ #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) void _nltst_assert_link_exists_full(const char *ifname, bool exists); #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 in_addr_t _nltst_inet4(const char *addr) { in_addr_t addr_bin = 0; _nltst_assert(_nltst_inet_pton(AF_INET, addr, NULL, &addr_bin)); return addr_bin; } static inline struct in6_addr *_nltst_inet6p(const char *addr) { _nl_thread_local static struct in6_addr addr_bin; ck_assert(_nltst_inet_pton(AF_INET6, addr, NULL, &addr_bin)); return &addr_bin; } static inline struct in6_addr _nltst_inet6(const char *addr) { struct in6_addr addr_bin; ck_assert(_nltst_inet_pton(AF_INET6, addr, NULL, &addr_bin)); return addr_bin; } 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); /*****************************************************************************/ 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 rtnl_link *_nltst_cache_get_link(struct nl_cache *cache, const char *ifname); 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, int *out_ifindex); void _nltst_delete_link(struct nl_sock *sk, const char *ifname); void _nltst_get_link(struct nl_sock *sk, const char *ifname, int *out_ifindex, struct rtnl_link **out_link); #endif /* __NL_TEST_UTIL_H__ */