401 lines
15 KiB
C
401 lines
15 KiB
C
|
|
/* 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
|
||
|
|
|
||
|
|
#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__ */
|