summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2022-05-06 12:06:55 +0200
committerThomas Haller <thaller@redhat.com>2022-05-09 09:02:28 +0200
commit7efeca2d575678c037850e8f27de9b10969c508f (patch)
tree7e62e5bcc6033f8ca384f289ae4b845017b509c8
parentf6f4d36ce15b0d7ceba5e98c122d3d70beba744a (diff)
downloadlibnl-7efeca2d575678c037850e8f27de9b10969c508f.tar.gz
tests: add test utils
-rw-r--r--tests/cksuite-all-attr.c26
-rw-r--r--tests/cksuite-all-netns.c9
-rw-r--r--tests/nl-test-util.c412
-rw-r--r--tests/nl-test-util.h336
4 files changed, 777 insertions, 6 deletions
diff --git a/tests/cksuite-all-attr.c b/tests/cksuite-all-attr.c
index 3b511f6e..824a5960 100644
--- a/tests/cksuite-all-attr.c
+++ b/tests/cksuite-all-attr.c
@@ -117,6 +117,31 @@ START_TEST(clone_cls_u32)
}
END_TEST
+/*****************************************************************************/
+
+START_TEST(test_nltst_strtok)
+{
+#define _assert_strtok(str, ...) \
+ do { \
+ const char *const _expected[] = { NULL, ##__VA_ARGS__, NULL }; \
+ _nltst_auto_strfreev char **_tokens = NULL; \
+ \
+ _tokens = _nltst_strtokv(str); \
+ _nltst_assert_strv_equal(_tokens, &_expected[1]); \
+ } while (0)
+
+ _assert_strtok("");
+ _assert_strtok(" \n");
+ _assert_strtok("a", "a");
+ _assert_strtok(" a ", "a");
+ _assert_strtok(" a\\ b", "a\\ ", "b");
+ _assert_strtok(" a\\ b cc\\d", "a\\ ", "b", "cc\\d");
+ _assert_strtok(" a\\ b\\ cc\\d", "a\\ ", "b\\ ", "cc\\d");
+}
+END_TEST
+
+/*****************************************************************************/
+
Suite *make_nl_attr_suite(void)
{
Suite *suite = suite_create("Netlink attributes");
@@ -125,6 +150,7 @@ Suite *make_nl_attr_suite(void)
tcase_add_test(tc, attr_size);
tcase_add_test(tc, msg_construct);
tcase_add_test(tc, clone_cls_u32);
+ tcase_add_test(tc, test_nltst_strtok);
suite_add_tcase(suite, tc);
return suite;
diff --git a/tests/cksuite-all-netns.c b/tests/cksuite-all-netns.c
index 872c54e9..48aacf1b 100644
--- a/tests/cksuite-all-netns.c
+++ b/tests/cksuite-all-netns.c
@@ -45,7 +45,8 @@ START_TEST(cache_and_clone)
for (i = 0; i < _NL_N_ELEMENTS(links); i++) {
if (links[i].add)
- _nltst_add_link(NULL, links[i].ifname, links[i].kind);
+ _nltst_add_link(NULL, links[i].ifname, links[i].kind,
+ NULL);
}
sk = _nltst_socket(NETLINK_ROUTE);
@@ -57,11 +58,11 @@ START_TEST(cache_and_clone)
ck_assert_int_eq(r, 0);
for (i = 0; i < _NL_N_ELEMENTS(links); i++) {
- _nl_auto_rtnl_link struct rtnl_link *link = NULL;
_nl_auto_rtnl_link struct rtnl_link *link_clone = NULL;
+ struct rtnl_link *link;
- link = rtnl_link_get_by_name(link_cache, links[i].ifname);
- ck_assert(link);
+ link = _nltst_cache_get_link(link_cache, links[i].ifname);
+ ck_assert_ptr_nonnull(link);
ck_assert_str_eq(rtnl_link_get_name(link), links[i].ifname);
diff --git a/tests/nl-test-util.c b/tests/nl-test-util.c
index 5f5059fa..1f67ac88 100644
--- a/tests/nl-test-util.c
+++ b/tests/nl-test-util.c
@@ -3,6 +3,8 @@
#include "nl-test-util.h"
#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
#include <sched.h>
#include <stdbool.h>
#include <stdio.h>
@@ -13,10 +15,70 @@
#include "netlink-private/utils.h"
#include "netlink/netlink.h"
#include "netlink/route/link.h"
+#include "netlink/route/route.h"
#include "netlink/socket.h"
/*****************************************************************************/
+void _nltst_get_urandom(void *ptr, size_t len)
+{
+ int fd;
+ ssize_t nread;
+
+ ck_assert_int_gt(len, 0);
+ ck_assert_ptr_nonnull(ptr);
+
+ fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY);
+ _nltst_assert_errno(fd >= 0);
+
+ nread = read(fd, ptr, len);
+ _nltst_assert_errno(nread == len);
+
+ _nltst_close(fd);
+}
+
+uint32_t _nltst_rand_u32(void)
+{
+ _nl_thread_local static unsigned short entropy[3];
+ _nl_thread_local static bool has_entropy = false;
+
+ if (!has_entropy) {
+ unsigned long long seed;
+ uint64_t seed64;
+ const char *s;
+ char *s_end;
+
+ memset(entropy, 0, sizeof(entropy));
+ s = getenv("NLTST_SEED_RAND");
+ if (!s)
+ seed = 0;
+ else if (s[0] != '\0') {
+ errno = 0;
+ seed = strtoull(s, &s_end, 10);
+ if (errno != 0 || s_end[0] != '\0') {
+ ck_assert_msg(
+ 0,
+ "invalid NLTST_SEED_RAND=\"%s\". Must be an integer to seed the random numbers",
+ s);
+ }
+ } else
+ _nltst_get_urandom(&seed, sizeof(seed));
+
+ seed64 = seed;
+ printf("runs with NLTST_SEED_RAND=%" PRIu64 "\n", seed64);
+
+ entropy[0] = (seed64 >> 0) ^ (seed64 >> 48);
+ entropy[1] = (seed64 >> 16) ^ (seed64 >> 0);
+ entropy[2] = (seed64 >> 32) ^ (seed64 >> 16);
+ has_entropy = true;
+ }
+
+ _NL_STATIC_ASSERT(sizeof(long) >= sizeof(uint32_t));
+ return jrand48(entropy);
+}
+
+/*****************************************************************************/
+
#define _CANARY 539339
struct nltst_netns {
@@ -157,6 +219,26 @@ void _nltst_object_identical(const void *a, const void *b)
/*****************************************************************************/
+char *_nltst_object_to_string(struct nl_object *obj)
+{
+ size_t L = 1024;
+ size_t l;
+ char *s;
+
+ if (!obj)
+ return strdup("(null)");
+
+ s = malloc(L);
+ ck_assert_ptr_nonnull(s);
+
+ nl_object_dump_buf(obj, s, L);
+ l = strlen(s);
+ ck_assert_int_lt(l, L);
+ s = realloc(s, l + 1);
+ ck_assert_ptr_nonnull(s);
+ return s;
+}
+
struct cache_get_all_data {
struct nl_object **arr;
size_t len;
@@ -184,6 +266,9 @@ struct nl_object **_nltst_cache_get_all(struct nl_cache *cache, size_t *out_len)
.idx = 0,
.len = 0,
};
+ size_t len2 = 0;
+ size_t i;
+ size_t j;
ck_assert(cache);
@@ -198,12 +283,58 @@ struct nl_object **_nltst_cache_get_all(struct nl_cache *cache, size_t *out_len)
ck_assert_int_eq(data.idx, data.len);
+ ck_assert_int_le(data.len, SSIZE_MAX);
+
data.arr[data.len] = NULL;
if (out_len)
*out_len = data.len;
+
+ /* double check the result. */
+ for (struct nl_object *obj = nl_cache_get_first(cache); obj;
+ obj = nl_cache_get_next(obj)) {
+ ck_assert_ptr_eq(data.arr[len2], obj);
+ len2++;
+ }
+ ck_assert_ptr_null(data.arr[len2]);
+
+ for (i = 0; i < data.len; i++) {
+ ck_assert_ptr_nonnull(data.arr[i]);
+ for (j = i + 1; j < data.len; j++)
+ ck_assert_ptr_ne(data.arr[i], data.arr[j]);
+ }
+
return data.arr;
}
+struct rtnl_link *_nltst_cache_get_link(struct nl_cache *cache,
+ const char *ifname)
+{
+ _nl_auto_free struct nl_object **objs = NULL;
+ struct rtnl_link *link = NULL;
+ size_t i;
+
+ ck_assert_ptr_nonnull(cache);
+ ck_assert_ptr_nonnull(ifname);
+
+ objs = _nltst_cache_get_all(cache, NULL);
+ for (i = 0; objs[i]; i++) {
+ if (_nl_streq(rtnl_link_get_name((struct rtnl_link *)objs[i]),
+ ifname)) {
+ ck_assert_ptr_null(link);
+ link = (struct rtnl_link *)objs[i];
+ }
+ }
+
+ if (_nltst_rand_u32_range(5) == 0) {
+ _nl_auto_rtnl_link struct rtnl_link *link2 = NULL;
+
+ link2 = rtnl_link_get_by_name(cache, ifname);
+ ck_assert_ptr_eq(link2, link);
+ }
+
+ return link;
+}
+
/*****************************************************************************/
struct nl_sock *_nltst_socket(int protocol)
@@ -217,18 +348,32 @@ struct nl_sock *_nltst_socket(int protocol)
r = nl_connect(sk, protocol);
ck_assert_int_eq(r, 0);
+ if (_nltst_rand_u32_range(5) == 0)
+ nl_cache_free(_nltst_rtnl_link_alloc_cache(sk, AF_UNSPEC, 0));
+
+ if (_nltst_rand_u32_range(5) == 0)
+ nl_cache_free(_nltst_rtnl_route_alloc_cache(
+ sk, _nltst_rand_select(AF_UNSPEC, AF_INET, AF_INET6)));
+
return sk;
}
-void _nltst_add_link(struct nl_sock *sk, const char *ifname, const char *kind)
+void _nltst_add_link(struct nl_sock *sk, const char *ifname, const char *kind,
+ int *out_ifindex)
{
_nl_auto_nl_socket struct nl_sock *sk_free = NULL;
_nl_auto_rtnl_link struct rtnl_link *link = NULL;
+ _nl_auto_nl_cache struct nl_cache *cache = NULL;
+ struct rtnl_link *link2;
+ int ifindex;
int r;
ck_assert(ifname);
ck_assert(kind);
+ if (_nltst_rand_u32_range(5) == 0)
+ _nltst_assert_link_not_exists(ifname);
+
if (!sk) {
sk = _nltst_socket(NETLINK_ROUTE);
sk_free = sk;
@@ -244,4 +389,269 @@ void _nltst_add_link(struct nl_sock *sk, const char *ifname, const char *kind)
r = rtnl_link_add(sk, link, NLM_F_CREATE);
ck_assert_int_eq(r, 0);
+
+ if (!out_ifindex && _nltst_rand_u32_range(5) != 0)
+ return;
+
+ cache = _nltst_rtnl_link_alloc_cache(sk, AF_UNSPEC, 0);
+
+ link2 = _nltst_cache_get_link(cache, ifname);
+ ck_assert_ptr_nonnull(link2);
+
+ ifindex = rtnl_link_get_ifindex(link2);
+ ck_assert_int_gt(ifindex, 0);
+
+ if (out_ifindex)
+ *out_ifindex = ifindex;
+}
+
+void _nltst_delete_link(struct nl_sock *sk, const char *ifname)
+{
+ _nl_auto_nl_socket struct nl_sock *sk_free = NULL;
+ _nl_auto_rtnl_link struct rtnl_link *link = NULL;
+
+ _nltst_assert_link_exists(ifname);
+
+ if (!sk) {
+ sk = _nltst_socket(NETLINK_ROUTE);
+ sk_free = sk;
+ }
+
+ if (_nltst_rand_u32_range(5) == 0) {
+ _nl_auto_nl_cache struct nl_cache *cache = NULL;
+
+ cache = _nltst_rtnl_link_alloc_cache(sk, AF_UNSPEC, 0);
+ ck_assert_ptr_nonnull(_nltst_cache_get_link(cache, ifname));
+ }
+
+ link = rtnl_link_alloc();
+ ck_assert_ptr_nonnull(link);
+
+ rtnl_link_set_name(link, ifname);
+
+ _nltst_assert_retcode(rtnl_link_delete(sk, link));
+
+ _nltst_assert_link_not_exists(ifname);
+}
+
+void _nltst_get_link(struct nl_sock *sk, const char *ifname, int *out_ifindex,
+ struct rtnl_link **out_link)
+{
+ _nl_auto_nl_cache struct nl_cache *cache = NULL;
+ struct rtnl_link *link;
+
+ if (_nltst_rand_u32_range(5) == 0)
+ _nltst_assert_link_exists(ifname);
+
+ cache = _nltst_rtnl_link_alloc_cache(sk, AF_UNSPEC, 0);
+
+ link = _nltst_cache_get_link(cache, ifname);
+ ck_assert(link);
+
+ if (out_ifindex)
+ *out_ifindex = rtnl_link_get_ifindex(link);
+
+ if (out_link) {
+ nl_object_get((struct nl_object *)link);
+ *out_link = link;
+ }
+}
+
+struct nl_cache *_nltst_rtnl_link_alloc_cache(struct nl_sock *sk,
+ int addr_family, unsigned flags)
+{
+ _nl_auto_nl_socket struct nl_sock *sk_free = NULL;
+ struct nl_cache *cache;
+ int r;
+
+ if (!sk) {
+ sk = _nltst_socket(NETLINK_ROUTE);
+ sk_free = sk;
+ }
+
+ if (flags == 0 && _nltst_rand_bool())
+ r = rtnl_link_alloc_cache(sk, addr_family, &cache);
+ else
+ r = rtnl_link_alloc_cache_flags(sk, addr_family, &cache, flags);
+
+ _nltst_assert_retcode(r);
+
+ if (_nltst_rand_u32_range(5) == 0)
+ free(_nltst_cache_get_all(cache, NULL));
+
+ return _nltst_assert(cache);
+}
+
+struct nl_cache *_nltst_rtnl_route_alloc_cache(struct nl_sock *sk,
+ int addr_family)
+{
+ struct nl_cache *cache;
+
+ ck_assert_ptr_nonnull(sk);
+ ck_assert(addr_family == AF_UNSPEC || addr_family == AF_INET ||
+ addr_family == AF_INET6);
+
+ _nltst_assert_retcode(
+ rtnl_route_alloc_cache(sk, addr_family, 0, &cache));
+
+ if (_nltst_rand_u32_range(5) == 0)
+ free(_nltst_cache_get_all(cache, NULL));
+
+ return _nltst_assert(cache);
+}
+
+/*****************************************************************************/
+
+char *_nltst_strtok(const char **p_str)
+{
+ const char *str;
+ _nl_auto_free char *dst = NULL;
+ size_t dst_len = 0;
+ size_t dst_alloc = 0;
+ size_t i;
+
+ ck_assert_ptr_nonnull(p_str);
+
+ str = _nltst_str_skip_space(*p_str);
+
+ if (str[0] == '\0') {
+ *p_str = str;
+ return NULL;
+ }
+
+ dst_len = 0;
+ dst_alloc = 10;
+ dst = malloc(dst_alloc);
+ ck_assert_ptr_nonnull(dst);
+
+ i = 0;
+ while (true) {
+ char ch1 = '\0';
+ char ch2 = '\0';
+
+ /* We take the first word, up until whitespace. Note that backslash
+ * escape is honored, so you can backslash escape spaces. The returned
+ * string will NOT have backslashes removed. */
+
+ if (str[i] == '\0') {
+ *p_str = &str[i];
+ break;
+ }
+ if (_nltst_char_is_space(str[i])) {
+ *p_str = _nltst_str_skip_space(&str[i + 1]);
+ break;
+ }
+ ch1 = str[i];
+ if (str[i] == '\\') {
+ if (str[i + 1] != '\0') {
+ ch2 = str[i + 1];
+ i += 2;
+ } else
+ i += 1;
+ } else
+ i += 1;
+
+ if (dst_len + 3 >= dst_alloc) {
+ dst_alloc *= 2;
+ dst = realloc(dst, dst_alloc);
+ ck_assert_ptr_nonnull(dst);
+ }
+ dst[dst_len++] = ch1;
+ if (ch2 != '\0')
+ dst[dst_len++] = ch2;
+ }
+
+ ck_assert_int_gt(dst_len, 0);
+ return strndup(dst, dst_len);
+}
+
+char **_nltst_strtokv(const char *str)
+{
+ _nl_auto_free char *s = NULL;
+ _nltst_auto_strfreev char **result = NULL;
+ size_t r_len = 0;
+ size_t r_alloc = 0;
+
+ if (!str)
+ return NULL;
+
+ r_alloc = 4;
+ result = malloc(sizeof(char *) * r_alloc);
+ ck_assert_ptr_nonnull(result);
+
+ while ((s = _nltst_strtok(&str))) {
+ if (r_len + 2 >= r_alloc) {
+ r_alloc *= 2;
+ result = realloc(result, sizeof(char *) * r_alloc);
+ ck_assert_ptr_nonnull(result);
+ }
+ result[r_len++] = _nl_steal_pointer(&s);
+ }
+ ck_assert_int_lt(r_len, r_alloc);
+ result[r_len] = NULL;
+ return _nl_steal_pointer(&result);
+}
+
+/*****************************************************************************/
+
+void _nltst_assert_link_exists_full(const char *ifname, bool exists)
+{
+ _nl_auto_nl_cache struct nl_cache *cache = NULL;
+ _nl_auto_rtnl_link struct rtnl_link *link_clone = NULL;
+ struct rtnl_link *link;
+ char path[100];
+ struct stat st;
+ int rnd;
+ 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, &st);
+ 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));
+ }
+ }
+
+ rnd = _nltst_rand_u32_range(3);
+
+ if (rnd == 0)
+ return;
+
+ cache = _nltst_rtnl_link_alloc_cache(NULL, AF_UNSPEC, 0);
+
+ link = _nltst_cache_get_link(cache, ifname);
+ if (exists)
+ ck_assert_ptr_nonnull(link);
+ else
+ ck_assert_ptr_null(link);
+
+ if (!link || rnd == 1)
+ return;
+
+ link_clone =
+ (struct rtnl_link *)nl_object_clone((struct nl_object *)link);
+ ck_assert(link_clone);
+
+ /* FIXME: we would expect that the cloned object is identical. It is not. */
+ /* _nltst_object_identical(link, link_clone); */
}
diff --git a/tests/nl-test-util.h b/tests/nl-test-util.h
index 259ab59b..0ff97528 100644
--- a/tests/nl-test-util.h
+++ b/tests/nl-test-util.h
@@ -4,17 +4,102 @@
#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) { \
@@ -26,6 +111,23 @@
} \
} 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; \
@@ -42,6 +144,221 @@
_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);
@@ -56,11 +373,28 @@ 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);
+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__ */