summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2022-05-09 16:34:18 +0200
committerThomas Haller <thaller@redhat.com>2022-05-09 16:34:18 +0200
commitdf6058cfa60f45b44b73ed77ef09a621c41a9375 (patch)
treecf157731cbddcf2e0d28525dd9776966e7350ab1
parent9b0493646c20f3eecdf664e7e00c6c9162e04fba (diff)
parent9772c1def0038d0814a4c558ed809fdaafb65784 (diff)
downloadlibnl-df6058cfa60f45b44b73ed77ef09a621c41a9375.tar.gz
tests: merge branch 'th/test-link'
https://github.com/thom311/libnl/pull/314
-rw-r--r--.github/workflows/ci.yml11
-rw-r--r--include/netlink-private/nl-auto.h8
-rw-r--r--include/netlink-private/utils.h17
-rw-r--r--tests/check-direct.c15
-rw-r--r--tests/cksuite-all-addr.c99
-rw-r--r--tests/cksuite-all-attr.c109
-rw-r--r--tests/cksuite-all-ematch-tree-clone.c11
-rw-r--r--tests/cksuite-all-netns.c222
-rw-r--r--tests/nl-test-util.c412
-rw-r--r--tests/nl-test-util.h336
10 files changed, 1133 insertions, 107 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3f5eeb0d..3b2d7068 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -43,11 +43,19 @@ jobs:
run: make -j 5 check-progs
- name: Run Unit Tests
- run: make -j 5 check
+ run: |
+ set -x
+ export NLTST_SEED_RAND=
+ for i in `seq 1 5`; do
+ tests/check-all
+ tests/check-direct
+ make -j check
+ done
- name: Run Unit Tests w/Valgrind
run: |
set -x
+ export NLTST_SEED_RAND=
CK_FORK=no libtool --mode=execute valgrind --error-exitcode=66 --leak-check=full -s --show-leak-kinds=all ./tests/check-all
CK_FORK=no libtool --mode=execute valgrind --error-exitcode=66 --leak-check=full -s --show-leak-kinds=all ./tests/check-direct
shell: bash
@@ -88,6 +96,7 @@ jobs:
../configure --disable-static
make -j 5
make -j 5 check-progs
+ export NLTST_SEED_RAND=
make -j 5 check
- run: echo "🍏 This job's status is ${{ job.status }}."
diff --git a/include/netlink-private/nl-auto.h b/include/netlink-private/nl-auto.h
index 4434a5b8..4092782b 100644
--- a/include/netlink-private/nl-auto.h
+++ b/include/netlink-private/nl-auto.h
@@ -15,6 +15,14 @@ static inline void name(void *v) \
} \
struct _nl_dummy_for_tailing_semicolon
+#define _NL_AUTO_DEFINE_FCN_STRUCT(CastType, name, func) \
+static inline void name(CastType *v) \
+{ \
+ if (v) \
+ func(v); \
+} \
+struct _nl_dummy_for_tailing_semicolon
+
#define _NL_AUTO_DEFINE_FCN_TYPED0(CastType, name, func) \
static inline void name(CastType *v) \
{ \
diff --git a/include/netlink-private/utils.h b/include/netlink-private/utils.h
index dca05b93..93a04c94 100644
--- a/include/netlink-private/utils.h
+++ b/include/netlink-private/utils.h
@@ -72,6 +72,23 @@
/*****************************************************************************/
+#ifdef thread_local
+#define _nl_thread_local thread_local
+/*
+ * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__
+ * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769
+ */
+#elif __STDC_VERSION__ >= 201112L && \
+ !(defined(__STDC_NO_THREADS__) || \
+ (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && \
+ __GLIBC_MINOR__ < 16))
+#define _nl_thread_local _Thread_local
+#else
+#define _nl_thread_local __thread
+#endif
+
+/*****************************************************************************/
+
#define _NL_STATIC_ASSERT(cond) ((void) sizeof (char[(cond) ? 1 : -1]))
/*****************************************************************************/
diff --git a/tests/check-direct.c b/tests/check-direct.c
index 90803696..f40bcee9 100644
--- a/tests/check-direct.c
+++ b/tests/check-direct.c
@@ -14,21 +14,24 @@ START_TEST(static_checks)
char strbuf[100];
_NL_STATIC_ASSERT(RTNL_LINK_RX_PACKETS == 0);
- assert(_nltst_map_stat_id_from_IPSTATS_MIB_v2[0] == RTNL_LINK_RX_PACKETS);
+ assert(_nltst_map_stat_id_from_IPSTATS_MIB_v2[0] ==
+ RTNL_LINK_RX_PACKETS);
for (i = 1; i < __IPSTATS_MIB_MAX; i++) {
assert(_nltst_map_stat_id_from_IPSTATS_MIB_v2[i] > 0);
- assert(_nltst_map_stat_id_from_IPSTATS_MIB_v2[i] < __RTNL_LINK_STATS_MAX);
+ assert(_nltst_map_stat_id_from_IPSTATS_MIB_v2[i] <
+ __RTNL_LINK_STATS_MAX);
for (j = 1; j < i; j++)
- assert(_nltst_map_stat_id_from_IPSTATS_MIB_v2[i] != _nltst_map_stat_id_from_IPSTATS_MIB_v2[j]);
+ assert(_nltst_map_stat_id_from_IPSTATS_MIB_v2[i] !=
+ _nltst_map_stat_id_from_IPSTATS_MIB_v2[j]);
}
for (i = 0; i <= RTNL_LINK_STATS_MAX + 1; i++) {
const char *s;
s = rtnl_link_stat2str(i, strbuf, sizeof(strbuf));
- assert (s);
- assert (s == strbuf);
- assert (strlen (s) < sizeof(strbuf));
+ assert(s);
+ assert(s == strbuf);
+ assert(strlen(s) < sizeof(strbuf));
if (strncmp(s, "0x", 2) == 0) {
assert(i == RTNL_LINK_STATS_MAX + 1);
ck_assert_int_eq(strtoll(&s[2], NULL, 16), i);
diff --git a/tests/cksuite-all-addr.c b/tests/cksuite-all-addr.c
index 3f95821b..f395351b 100644
--- a/tests/cksuite-all-addr.c
+++ b/tests/cksuite-all-addr.c
@@ -14,34 +14,34 @@ START_TEST(addr_alloc)
struct nl_addr *addr;
addr = nl_addr_alloc(16);
- ck_assert_msg(addr,
- "Allocation should not return NULL");
+ ck_assert_msg(addr, "Allocation should not return NULL");
ck_assert_msg(nl_addr_iszero(addr) != 0,
- "New empty address should be all zeros");
+ "New empty address should be all zeros");
ck_assert_msg(nl_addr_get_family(addr) == AF_UNSPEC,
- "New empty address should have family AF_UNSPEC");
+ "New empty address should have family AF_UNSPEC");
ck_assert_msg(nl_addr_get_prefixlen(addr) == 0,
- "New empty address should have prefix length 0");
+ "New empty address should have prefix length 0");
ck_assert_msg(!nl_addr_shared(addr),
- "New empty address should not be shared");
+ "New empty address should not be shared");
ck_assert_msg(nl_addr_get(addr) == addr,
- "nl_addr_get() should return pointer to address");
+ "nl_addr_get() should return pointer to address");
ck_assert_msg(nl_addr_shared(addr) != 0,
- "Address should be shared after call to nl_addr_get()");
+ "Address should be shared after call to nl_addr_get()");
nl_addr_put(addr);
- ck_assert_msg(!nl_addr_shared(addr),
+ ck_assert_msg(
+ !nl_addr_shared(addr),
"Address should not be shared after call to nl_addr_put()");
ck_assert_msg(nl_addr_fill_sockaddr(addr, NULL, 0) != 0,
- "Socket address filling should fail for empty address");
+ "Socket address filling should fail for empty address");
nl_addr_put(addr);
}
@@ -54,40 +54,41 @@ START_TEST(addr_binary_addr)
char baddr2[6] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 };
addr = nl_addr_alloc(4);
- ck_assert_msg(addr != NULL,
- "Allocation should not return NULL");
+ ck_assert_msg(addr != NULL, "Allocation should not return NULL");
ck_assert_msg(nl_addr_set_binary_addr(addr, baddr, 4) >= 0,
- "Valid binary address should be settable");
+ "Valid binary address should be settable");
- ck_assert_msg(nl_addr_get_prefixlen(addr) == 0,
+ ck_assert_msg(
+ nl_addr_get_prefixlen(addr) == 0,
"Prefix length should be unchanged after nl_addr_set_binary_addr()");
- ck_assert_msg(nl_addr_get_len(addr) == 4,
- "Address length should be 4");
+ ck_assert_msg(nl_addr_get_len(addr) == 4, "Address length should be 4");
- ck_assert_msg(nl_addr_set_binary_addr(addr, baddr2, 6) != 0,
+ ck_assert_msg(
+ nl_addr_set_binary_addr(addr, baddr2, 6) != 0,
"Should not be able to set binary address exceeding maximum length");
ck_assert_msg(nl_addr_get_len(addr) == 4,
- "Address length should still be 4");
+ "Address length should still be 4");
- ck_assert_msg(nl_addr_guess_family(addr) == AF_INET,
+ ck_assert_msg(
+ nl_addr_guess_family(addr) == AF_INET,
"Binary address of length 4 should be guessed as AF_INET");
ck_assert_msg(memcmp(baddr, nl_addr_get_binary_addr(addr), 4) == 0,
- "Binary address mismatches");
+ "Binary address mismatches");
addr2 = nl_addr_build(AF_UNSPEC, baddr, 4);
- ck_assert_msg(addr2 != NULL,
- "Building of address should not fail");
+ ck_assert_msg(addr2 != NULL, "Building of address should not fail");
nl_addr_set_prefixlen(addr, 32);
- ck_assert_msg(nl_addr_get_prefixlen(addr) == 32,
+ ck_assert_msg(
+ nl_addr_get_prefixlen(addr) == 32,
"Prefix length should be successful changed after nl_addr_set_prefixlen()");
ck_assert_msg(!nl_addr_cmp(addr, addr2),
- "Addresses built from same binary address should match");
+ "Addresses built from same binary address should match");
nl_addr_put(addr);
nl_addr_put(addr2);
@@ -103,31 +104,32 @@ START_TEST(addr_parse4)
char buf[128];
ck_assert_msg(nl_addr_parse(addr_str, AF_INET6, &addr4) != 0,
- "Should not be able to parse IPv4 address in IPv6 mode");
+ "Should not be able to parse IPv4 address in IPv6 mode");
ck_assert_msg(nl_addr_parse(addr_str, AF_UNSPEC, &addr4) == 0,
- "Should be able to parse \"%s\"", addr_str);
+ "Should be able to parse \"%s\"", addr_str);
ck_assert_msg(nl_addr_get_family(addr4) == AF_INET,
- "Address family should be AF_INET");
+ "Address family should be AF_INET");
ck_assert_msg(nl_addr_get_prefixlen(addr4) == 16,
- "Prefix length should be 16");
+ "Prefix length should be 16");
ck_assert_msg(!nl_addr_iszero(addr4),
- "Address should not be all zeroes");
+ "Address should not be all zeroes");
clone = nl_addr_clone(addr4);
- ck_assert_msg(clone != NULL,
- "Cloned address should not be NULL");
+ ck_assert_msg(clone != NULL, "Cloned address should not be NULL");
ck_assert_msg(nl_addr_cmp(addr4, clone) == 0,
- "Cloned address should not mismatch original");
+ "Cloned address should not mismatch original");
- ck_assert_msg(nl_addr_fill_sockaddr(addr4, (struct sockaddr *) &sin, &len) == 0,
- "Should be able to fill socketaddr");
+ ck_assert_msg(nl_addr_fill_sockaddr(addr4, (struct sockaddr *)&sin,
+ &len) == 0,
+ "Should be able to fill socketaddr");
- ck_assert_msg(!strcmp(nl_addr2str(addr4, buf, sizeof(buf)), addr_str),
+ ck_assert_msg(
+ !strcmp(nl_addr2str(addr4, buf, sizeof(buf)), addr_str),
"Address translated back to string does not match original");
nl_addr_put(addr4);
@@ -144,31 +146,32 @@ START_TEST(addr_parse6)
char buf[128];
ck_assert_msg(nl_addr_parse(addr_str, AF_INET, &addr6) != 0,
- "Should not be able to parse IPv6 address in IPv4 mode");
+ "Should not be able to parse IPv6 address in IPv4 mode");
ck_assert_msg(nl_addr_parse(addr_str, AF_UNSPEC, &addr6) == 0,
- "Should be able to parse \"%s\"", addr_str);
+ "Should be able to parse \"%s\"", addr_str);
ck_assert_msg(nl_addr_get_family(addr6) == AF_INET6,
- "Address family should be AF_INET6");
+ "Address family should be AF_INET6");
ck_assert_msg(nl_addr_get_prefixlen(addr6) == 64,
- "Prefix length should be 64");
+ "Prefix length should be 64");
ck_assert_msg(!nl_addr_iszero(addr6),
- "Address should not be all zeroes");
+ "Address should not be all zeroes");
clone = nl_addr_clone(addr6);
- ck_assert_msg(clone != NULL,
- "Cloned address should not be NULL");
+ ck_assert_msg(clone != NULL, "Cloned address should not be NULL");
ck_assert_msg(nl_addr_cmp(addr6, clone) == 0,
- "Cloned address should not mismatch original");
+ "Cloned address should not mismatch original");
- ck_assert_msg(nl_addr_fill_sockaddr(addr6, (struct sockaddr *) &sin, &len) == 0,
- "Should be able to fill socketaddr");
+ ck_assert_msg(nl_addr_fill_sockaddr(addr6, (struct sockaddr *)&sin,
+ &len) == 0,
+ "Should be able to fill socketaddr");
- ck_assert_msg(!strcmp(nl_addr2str(addr6, buf, sizeof(buf)), addr_str),
+ ck_assert_msg(
+ !strcmp(nl_addr2str(addr6, buf, sizeof(buf)), addr_str),
"Address translated back to string does not match original");
nl_addr_put(addr6);
@@ -183,10 +186,10 @@ START_TEST(addr_info)
struct addrinfo *result;
ck_assert_msg(nl_addr_parse(addr_str, AF_UNSPEC, &addr) == 0,
- "Parsing of valid address should not fail");
+ "Parsing of valid address should not fail");
ck_assert_msg(nl_addr_info(addr, &result) == 0,
- "getaddrinfo() on loopback address should work");
+ "getaddrinfo() on loopback address should work");
freeaddrinfo(result);
nl_addr_put(addr);
diff --git a/tests/cksuite-all-attr.c b/tests/cksuite-all-attr.c
index ca6f7c2a..824a5960 100644
--- a/tests/cksuite-all-attr.c
+++ b/tests/cksuite-all-attr.c
@@ -16,35 +16,35 @@
START_TEST(attr_size)
{
ck_assert_msg(nla_attr_size(0) == NLA_HDRLEN,
- "Length of empty attribute should match header size");
+ "Length of empty attribute should match header size");
ck_assert_msg(nla_attr_size(1) == NLA_HDRLEN + 1,
- "Length of 1 bytes payload should be NLA_HDRLEN + 1");
+ "Length of 1 bytes payload should be NLA_HDRLEN + 1");
ck_assert_msg(nla_attr_size(2) == NLA_HDRLEN + 2,
- "Length of 2 bytes payload should be NLA_HDRLEN + 2");
+ "Length of 2 bytes payload should be NLA_HDRLEN + 2");
ck_assert_msg(nla_attr_size(3) == NLA_HDRLEN + 3,
- "Length of 3 bytes payload should be NLA_HDRLEN + 3");
+ "Length of 3 bytes payload should be NLA_HDRLEN + 3");
ck_assert_msg(nla_attr_size(4) == NLA_HDRLEN + 4,
- "Length of 4 bytes payload should be NLA_HDRLEN + 4");
+ "Length of 4 bytes payload should be NLA_HDRLEN + 4");
ck_assert_msg(nla_total_size(1) == NLA_HDRLEN + 4,
- "Total size of 1 bytes payload should result in 8 bytes");
+ "Total size of 1 bytes payload should result in 8 bytes");
ck_assert_msg(nla_total_size(2) == NLA_HDRLEN + 4,
- "Total size of 2 bytes payload should result in 8 bytes");
+ "Total size of 2 bytes payload should result in 8 bytes");
ck_assert_msg(nla_total_size(3) == NLA_HDRLEN + 4,
- "Total size of 3 bytes payload should result in 8 bytes");
+ "Total size of 3 bytes payload should result in 8 bytes");
ck_assert_msg(nla_total_size(4) == NLA_HDRLEN + 4,
- "Total size of 4 bytes payload should result in 8 bytes");
+ "Total size of 4 bytes payload should result in 8 bytes");
ck_assert_msg(nla_padlen(1) == 3,
- "2 bytes of payload should result in 3 padding bytes");
+ "2 bytes of payload should result in 3 padding bytes");
ck_assert_msg(nla_padlen(2) == 2,
- "2 bytes of payload should result in 2 padding bytes");
+ "2 bytes of payload should result in 2 padding bytes");
ck_assert_msg(nla_padlen(3) == 1,
- "3 bytes of payload should result in 1 padding bytes");
+ "3 bytes of payload should result in 1 padding bytes");
ck_assert_msg(nla_padlen(4) == 0,
- "4 bytes of payload should result in 0 padding bytes");
+ "4 bytes of payload should result in 0 padding bytes");
ck_assert_msg(nla_padlen(5) == 3,
- "5 bytes of payload should result in 3 padding bytes");
+ "5 bytes of payload should result in 3 padding bytes");
}
END_TEST
@@ -59,16 +59,17 @@ START_TEST(msg_construct)
ck_assert_msg(msg, "Unable to allocate netlink message");
for (i = 1; i < 256; i++) {
- ck_assert_msg(nla_put_u32(msg, i, i+1) == 0,
- "Unable to add attribute %d", i);
+ ck_assert_msg(nla_put_u32(msg, i, i + 1) == 0,
+ "Unable to add attribute %d", i);
}
nlh = nlmsg_hdr(msg);
i = 1;
- nlmsg_for_each_attr(a, nlh, 0, rem) {
+ nlmsg_for_each_attr (a, nlh, 0, rem) {
ck_assert_msg(nla_type(a) == i, "Expected attribute %d", i);
i++;
- ck_assert_msg(nla_get_u32(a) == i, "Expected attribute value %d", i);
+ ck_assert_msg(nla_get_u32(a) == i,
+ "Expected attribute value %d", i);
}
nlmsg_free(msg);
@@ -77,45 +78,70 @@ END_TEST
START_TEST(clone_cls_u32)
{
- _nl_auto_rtnl_link struct rtnl_link *link = NULL;
- _nl_auto_rtnl_cls struct rtnl_cls *cls = NULL;
- _nl_auto_rtnl_cls struct rtnl_cls *cls2 = NULL;
- int r;
- const uint32_t direction = 16;
+ _nl_auto_rtnl_link struct rtnl_link *link = NULL;
+ _nl_auto_rtnl_cls struct rtnl_cls *cls = NULL;
+ _nl_auto_rtnl_cls struct rtnl_cls *cls2 = NULL;
+ int r;
+ const uint32_t direction = 16;
- link = rtnl_link_alloc();
- ck_assert(link);
+ link = rtnl_link_alloc();
+ ck_assert(link);
- rtnl_link_set_ifindex(link, 5);
+ rtnl_link_set_ifindex(link, 5);
- cls = rtnl_cls_alloc();
- ck_assert(cls);
+ cls = rtnl_cls_alloc();
+ ck_assert(cls);
- rtnl_tc_set_link(TC_CAST(cls), link);
+ rtnl_tc_set_link(TC_CAST(cls), link);
- r = rtnl_tc_set_kind(TC_CAST(cls), "u32");
- ck_assert(r == 0);
+ r = rtnl_tc_set_kind(TC_CAST(cls), "u32");
+ ck_assert(r == 0);
- rtnl_cls_set_prio(cls, 1);
- rtnl_cls_set_protocol(cls, ETH_P_IP);
+ rtnl_cls_set_prio(cls, 1);
+ rtnl_cls_set_protocol(cls, ETH_P_IP);
- rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(1, 0));
+ rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(1, 0));
- rtnl_u32_set_hashtable(cls, 5);
+ rtnl_u32_set_hashtable(cls, 5);
- rtnl_u32_add_key_uint32(cls, 0x0a000914, 0xffffffff, direction, 0);
+ rtnl_u32_add_key_uint32(cls, 0x0a000914, 0xffffffff, direction, 0);
- rtnl_u32_set_hashmask(cls, 0xff000000, direction);
+ rtnl_u32_set_hashmask(cls, 0xff000000, direction);
- rtnl_u32_add_mark(cls, 55, 66);
+ rtnl_u32_add_mark(cls, 55, 66);
- rtnl_u32_set_link(cls, 44);
+ rtnl_u32_set_link(cls, 44);
- cls2 = (struct rtnl_cls *)nl_object_clone((struct nl_object *)cls);
- ck_assert(cls2);
+ cls2 = (struct rtnl_cls *)nl_object_clone((struct nl_object *)cls);
+ ck_assert(cls2);
}
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");
@@ -124,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-ematch-tree-clone.c b/tests/cksuite-all-ematch-tree-clone.c
index eb38e999..f9d17a98 100644
--- a/tests/cksuite-all-ematch-tree-clone.c
+++ b/tests/cksuite-all-ematch-tree-clone.c
@@ -8,8 +8,8 @@
#include "cksuite-all.h"
#include "netlink-private/nl-auto.h"
-#define MAX_DEPTH 6
-#define MAX_CHILDREN 5
+#define MAX_DEPTH 6
+#define MAX_CHILDREN 5
static int current_depth = 0;
static int id = 1;
@@ -25,7 +25,7 @@ static long long my_pow(long long x, long long y)
if (y < 0 || x == 0)
return 0;
- while(--y) {
+ while (--y) {
ret *= x;
}
@@ -76,7 +76,7 @@ static void dump_ematch_list(struct nl_list_head *head, int *result, int *index)
{
struct rtnl_ematch *pos = NULL;
- nl_list_for_each_entry(pos, head, e_list) {
+ nl_list_for_each_entry (pos, head, e_list) {
if (!nl_list_empty(&pos->e_childs))
dump_ematch_list(&pos->e_childs, result, index);
result[*index] = pos->e_id;
@@ -84,7 +84,8 @@ static void dump_ematch_list(struct nl_list_head *head, int *result, int *index)
}
}
-static void dump_ematch_tree(struct rtnl_ematch_tree *tree, int *result, int *index)
+static void dump_ematch_tree(struct rtnl_ematch_tree *tree, int *result,
+ int *index)
{
if (!tree)
return;
diff --git a/tests/cksuite-all-netns.c b/tests/cksuite-all-netns.c
index 872c54e9..97f3a702 100644
--- a/tests/cksuite-all-netns.c
+++ b/tests/cksuite-all-netns.c
@@ -1,4 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-only */
+/*
+ * Author: Susant Sahani <susant@redhat.com>
+ * Copyright (c) 2018 Red Hat, Inc.
+ */
#include <stdlib.h>
#include <stdbool.h>
@@ -6,9 +10,36 @@
#include "netlink-private/utils.h"
#include "netlink/route/link.h"
+#include "netlink/route/link/sit.h"
+#include <netlink/route/link/bonding.h>
+#include <netlink/route/link/bridge.h>
+#include <netlink/route/link/ip6tnl.h>
+#include <netlink/route/link/ipgre.h>
+#include <netlink/route/link/ipip.h>
+#include <netlink/route/link/ipvlan.h>
+#include <netlink/route/link/ipvti.h>
+#include <netlink/route/link/macsec.h>
+#include <netlink/route/link/macvlan.h>
+#include <netlink/route/link/macvtap.h>
+#include <netlink/route/link/veth.h>
+#include <netlink/route/link/vlan.h>
+#include <netlink/route/link/vrf.h>
+#include <netlink/route/link/vxlan.h>
#include "cksuite-all.h"
+/*****************************************************************************/
+
+static void _nltst_delete_link2(const char *ifname)
+{
+ _nltst_delete_link(NULL, ifname);
+}
+#define _nltst_auto_delete_link _nl_auto(_nltst_auto_delete_link_fcn)
+_NL_AUTO_DEFINE_FCN_TYPED0(const char *, _nltst_auto_delete_link_fcn,
+ _nltst_delete_link2);
+
+/*****************************************************************************/
+
START_TEST(cache_and_clone)
{
_nl_auto_nl_socket struct nl_sock *sk = NULL;
@@ -45,7 +76,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 +89,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);
@@ -91,6 +123,186 @@ START_TEST(cache_and_clone)
}
END_TEST
+/*****************************************************************************/
+
+START_TEST(test_create_iface)
+{
+ const int TEST_IDX = _i;
+ _nl_auto_nl_socket struct nl_sock *sk = _nltst_socket(NETLINK_ROUTE);
+ _nl_auto_rtnl_link struct rtnl_link *link = NULL;
+ _nl_auto_rtnl_link struct rtnl_link *link2 = NULL;
+ _nl_auto_rtnl_link struct rtnl_link *peer = NULL;
+ _nltst_auto_delete_link const char *IFNAME_DUMMY = NULL;
+ _nltst_auto_delete_link const char *IFNAME = "ifname";
+ int ifindex_dummy;
+ uint32_t u32;
+ int r;
+
+ switch (TEST_IDX) {
+ case 0:
+ link = _nltst_assert(rtnl_link_bridge_alloc());
+ rtnl_link_set_name(link, IFNAME);
+ break;
+ case 1:
+ link = _nltst_assert(rtnl_link_vxlan_alloc());
+ rtnl_link_set_name(link, IFNAME);
+ _nltst_assert_retcode(rtnl_link_vxlan_set_id(link, 128));
+ break;
+ case 2:
+ link = _nltst_assert(rtnl_link_alloc());
+ rtnl_link_set_type(link, "ifb");
+ rtnl_link_set_name(link, IFNAME);
+ break;
+ case 3:
+ link = _nltst_assert(rtnl_link_ipgre_alloc());
+ rtnl_link_set_name(link, IFNAME);
+ _nltst_assert_retcode(rtnl_link_ipgre_set_local(
+ link, _nltst_inet4("192.168.254.12")));
+ _nltst_assert_retcode(rtnl_link_ipgre_set_remote(
+ link, _nltst_inet4("192.168.254.13")));
+ _nltst_assert_retcode(rtnl_link_ipgre_set_ttl(link, 64));
+ break;
+ case 4:
+ link = _nltst_assert(rtnl_link_ip6_tnl_alloc());
+ rtnl_link_set_name(link, IFNAME);
+ _nltst_assert_retcode(rtnl_link_ip6_tnl_set_local(
+ link, _nltst_inet6p("2607:f0d0:1002:51::4")));
+ _nltst_assert_retcode(rtnl_link_ip6_tnl_set_remote(
+ link, _nltst_inet6p("2607:f0d0:1002:52::5")));
+ break;
+ case 5:
+ link = _nltst_assert(rtnl_link_ipgretap_alloc());
+ rtnl_link_set_name(link, IFNAME);
+ _nltst_assert_retcode(rtnl_link_ipgre_set_local(
+ link, _nltst_inet4("10.211.55.10")));
+ _nltst_assert_retcode(rtnl_link_ipgre_set_remote(
+ link, _nltst_inet4("10.133.6.33")));
+ _nltst_assert_retcode(rtnl_link_ipgre_set_ttl(link, 64));
+ break;
+ case 6:
+ link = _nltst_assert(rtnl_link_ipvti_alloc());
+ rtnl_link_set_name(link, IFNAME);
+ _nltst_assert_retcode(rtnl_link_ipvti_set_local(
+ link, _nltst_inet4("192.168.254.12")));
+ _nltst_assert_retcode(rtnl_link_ipvti_set_remote(
+ link, _nltst_inet4("192.168.254.13")));
+ break;
+ case 7:
+ link = _nltst_assert(rtnl_link_sit_alloc());
+ rtnl_link_set_name(link, IFNAME);
+ _nltst_assert_retcode(rtnl_link_sit_set_local(
+ link, _nltst_inet4("192.168.254.12")));
+ _nltst_assert_retcode(rtnl_link_sit_set_remote(
+ link, _nltst_inet4("192.168.254.13")));
+ _nltst_assert_retcode(rtnl_link_sit_set_ttl(link, 64));
+ break;
+ case 8:
+ link = _nltst_assert(rtnl_link_ipip_alloc());
+ rtnl_link_set_name(link, IFNAME);
+ _nltst_assert_retcode(rtnl_link_ipip_set_local(
+ link, _nltst_inet4("192.168.254.12")));
+ _nltst_assert_retcode(rtnl_link_ipip_set_remote(
+ link, _nltst_inet4("192.168.254.13")));
+ _nltst_assert_retcode(rtnl_link_ipip_set_ttl(link, 64));
+ break;
+ case 9:
+ link = _nltst_assert(rtnl_link_bond_alloc());
+ rtnl_link_set_name(link, IFNAME);
+ break;
+ case 10:
+ IFNAME_DUMMY = "ci-dummy";
+ _nltst_add_link(sk, IFNAME_DUMMY, "dummy", &ifindex_dummy);
+
+ link = _nltst_assert(rtnl_link_macvtap_alloc());
+ rtnl_link_set_link(link, ifindex_dummy);
+ rtnl_link_set_name(link, IFNAME);
+ _nltst_assert_retcode(rtnl_link_macvtap_set_mode(
+ link, rtnl_link_macvtap_str2mode("bridge")));
+ break;
+ case 11:
+ IFNAME_DUMMY = "ci-dummy";
+ _nltst_add_link(sk, IFNAME_DUMMY, "dummy", &ifindex_dummy);
+
+ link = _nltst_assert(rtnl_link_macvlan_alloc());
+ rtnl_link_set_link(link, ifindex_dummy);
+ rtnl_link_set_name(link, IFNAME);
+ break;
+ case 12:
+ IFNAME_DUMMY = "ci-dummy";
+ _nltst_add_link(sk, IFNAME_DUMMY, "dummy", &ifindex_dummy);
+
+ link = _nltst_assert(rtnl_link_vlan_alloc());
+ rtnl_link_set_link(link, ifindex_dummy);
+ rtnl_link_set_name(link, IFNAME);
+ _nltst_assert_retcode(rtnl_link_vlan_set_id(link, 10));
+ break;
+ case 13:
+ IFNAME_DUMMY = "ci-dummy";
+ _nltst_add_link(sk, IFNAME_DUMMY, "dummy", &ifindex_dummy);
+
+ link = _nltst_assert(rtnl_link_macsec_alloc());
+ rtnl_link_set_link(link, ifindex_dummy);
+ rtnl_link_set_name(link, IFNAME);
+ _nltst_assert_retcode(rtnl_link_macsec_set_port(link, 10));
+ _nltst_assert_retcode(rtnl_link_macsec_set_encrypt(link, 1));
+ _nltst_assert_retcode(
+ rtnl_link_macsec_set_replay_protect(link, 1));
+ _nltst_assert_retcode(rtnl_link_macsec_set_window(link, 200));
+ break;
+ case 14:
+ IFNAME_DUMMY = "ci-dummy";
+ _nltst_add_link(sk, IFNAME_DUMMY, "dummy", &ifindex_dummy);
+
+ link = _nltst_assert(rtnl_link_ipvlan_alloc());
+ rtnl_link_set_link(link, ifindex_dummy);
+ _nltst_assert_retcode(rtnl_link_ipvlan_set_mode(
+ link, rtnl_link_ipvlan_str2mode("l2")));
+ rtnl_link_set_name(link, IFNAME);
+ break;
+ case 15:
+ link = _nltst_assert(rtnl_link_vrf_alloc());
+ rtnl_link_set_name(link, IFNAME);
+ _nltst_assert_retcode(rtnl_link_vrf_set_tableid(link, 10));
+ break;
+ case 16: {
+ link = _nltst_assert(rtnl_link_veth_alloc());
+ rtnl_link_set_name(link, IFNAME);
+ peer = _nltst_assert(rtnl_link_veth_get_peer(link));
+ rtnl_link_set_name(peer, "ci-veth-peer");
+ } break;
+ default:
+ ck_assert_msg(0, "unexpected TEST_IDX=%d", _i);
+ break;
+ }
+
+ r = rtnl_link_add(sk, link, NLM_F_CREATE);
+ if (r == -NLE_OPNOTSUPP) {
+ /* Hm, no kernel module? Skip the test. */
+ _nltst_assert_link_not_exists(IFNAME);
+ IFNAME = NULL;
+ return;
+ }
+ _nltst_assert_retcode(r);
+
+ _nltst_assert_link_exists(IFNAME);
+
+ switch (TEST_IDX) {
+ case 15:
+ _nltst_get_link(sk, IFNAME, NULL, &link2);
+ _nltst_assert_retcode(rtnl_link_vrf_get_tableid(link2, &u32));
+ ck_assert_int_eq(u32, 10);
+ break;
+ case 16:
+ _nltst_assert_link_exists("ci-veth-peer");
+ if (_nltst_rand_bool())
+ IFNAME = "ci-veth-peer";
+ break;
+ }
+}
+END_TEST
+
+/*****************************************************************************/
+
Suite *make_nl_netns_suite(void)
{
Suite *suite = suite_create("netns");
@@ -99,6 +311,8 @@ Suite *make_nl_netns_suite(void)
tcase_add_checked_fixture(tc, nltst_netns_fixture_setup,
nltst_netns_fixture_teardown);
tcase_add_test(tc, cache_and_clone);
+ tcase_add_loop_test(tc, test_create_iface, 0, 17);
+
suite_add_tcase(suite, tc);
return suite;
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__ */