summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/.gitignore33
-rw-r--r--tests/check-addr.c214
-rw-r--r--tests/check-all.c17
-rw-r--r--tests/check-attr.c90
-rw-r--r--tests/check-direct.c69
-rw-r--r--tests/cksuite-all-addr.c227
-rw-r--r--tests/cksuite-all-attr.c157
-rw-r--r--tests/cksuite-all-ematch-tree-clone.c (renamed from tests/check-ematch-tree-clone.c)47
-rw-r--r--tests/cksuite-all-netns.c319
-rw-r--r--tests/cksuite-all.h12
-rw-r--r--tests/nl-test-util.c657
-rw-r--r--tests/nl-test-util.h400
-rw-r--r--tests/test-complex-HTB-with-hash-filters.c8
-rw-r--r--tests/test-u32-filter-with-actions.c10
-rw-r--r--tests/util.h10
15 files changed, 1871 insertions, 399 deletions
diff --git a/tests/.gitignore b/tests/.gitignore
deleted file mode 100644
index 90af67ad..00000000
--- a/tests/.gitignore
+++ /dev/null
@@ -1,33 +0,0 @@
-/check-all
-/check-all.log
-/check-all.trs
-/test-*.log
-/test-*.trs
-/test-cache-mngr
-/test-complex-HTB-with-hash-filters
-/test-create-bond
-/test-create-bridge
-/test-create-geneve
-/test-create-ifb
-/test-create-ip6tnl
-/test-create-ipgre
-/test-create-ipgretap
-/test-create-ipip
-/test-create-ipvlan
-/test-create-ipvti
-/test-create-macsec
-/test-create-macvlan
-/test-create-macvtap
-/test-create-sit
-/test-create-veth
-/test-create-vlan
-/test-create-vrf
-/test-create-vxlan
-/test-create-xfrmi
-/test-delete-link
-/test-genl
-/test-loopback-up-down
-/test-nf-cache-mngr
-/test-socket-creation
-/test-suite.log
-/test-u32-filter-with-actions
diff --git a/tests/check-addr.c b/tests/check-addr.c
deleted file mode 100644
index 48a2d931..00000000
--- a/tests/check-addr.c
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * tests/check-addr.c nl_addr unit tests
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation version 2.1
- * of the License.
- *
- * Copyright (c) 2013 Thomas Graf <tgraf@suug.ch>
- */
-
-#include <check.h>
-#include <netlink/addr.h>
-
-#include "util.h"
-
-START_TEST(addr_alloc)
-{
- struct nl_addr *addr;
-
- addr = nl_addr_alloc(16);
- fail_if(addr == NULL,
- "Allocation should not return NULL");
-
- fail_if(nl_addr_iszero(addr) == 0,
- "New empty address should be all zeros");
-
- fail_if(nl_addr_get_family(addr) != AF_UNSPEC,
- "New empty address should have family AF_UNSPEC");
-
- fail_if(nl_addr_get_prefixlen(addr) != 0,
- "New empty address should have prefix length 0");
-
- fail_if(nl_addr_shared(addr),
- "New empty address should not be shared");
-
- fail_if(nl_addr_get(addr) != addr,
- "nl_addr_get() should return pointer to address");
-
- fail_if(nl_addr_shared(addr) == 0,
- "Address should be shared after call to nl_addr_get()");
-
- nl_addr_put(addr);
-
- fail_if(nl_addr_shared(addr),
- "Address should not be shared after call to nl_addr_put()");
-
- fail_if(nl_addr_fill_sockaddr(addr, NULL, 0) == 0,
- "Socket address filling should fail for empty address");
-
- nl_addr_put(addr);
-}
-END_TEST
-
-START_TEST(addr_binary_addr)
-{
- struct nl_addr *addr, *addr2;
- char baddr[4] = { 0x1, 0x2, 0x3, 0x4 };
- char baddr2[6] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 };
-
- addr = nl_addr_alloc(4);
- fail_if(addr == NULL,
- "Allocation should not return NULL");
-
- fail_if(nl_addr_set_binary_addr(addr, baddr, 4) < 0,
- "Valid binary address should be settable");
-
- fail_if(nl_addr_get_prefixlen(addr) != 0,
- "Prefix length should be unchanged after nl_addr_set_binary_addr()");
-
- fail_if(nl_addr_get_len(addr) != 4,
- "Address length should be 4");
-
- fail_if(nl_addr_set_binary_addr(addr, baddr2, 6) == 0,
- "Should not be able to set binary address exceeding maximum length");
-
- fail_if(nl_addr_get_len(addr) != 4,
- "Address length should still be 4");
-
- fail_if(nl_addr_guess_family(addr) != AF_INET,
- "Binary address of length 4 should be guessed as AF_INET");
-
- fail_if(memcmp(baddr, nl_addr_get_binary_addr(addr), 4) != 0,
- "Binary address mismatches");
-
- addr2 = nl_addr_build(AF_UNSPEC, baddr, 4);
- fail_if(addr2 == NULL,
- "Building of address should not fail");
-
- nl_addr_set_prefixlen(addr, 32);
- fail_if(nl_addr_get_prefixlen(addr) != 32,
- "Prefix length should be successful changed after nl_addr_set_prefixlen()");
-
- fail_if(nl_addr_cmp(addr, addr2),
- "Addresses built from same binary address should match");
-
- nl_addr_put(addr);
- nl_addr_put(addr2);
-}
-END_TEST
-
-START_TEST(addr_parse4)
-{
- struct nl_addr *addr4, *clone;
- struct sockaddr_in sin;
- socklen_t len = sizeof(sin);
- char *addr_str = "10.0.0.1/16";
- char buf[128];
-
- fail_if(nl_addr_parse(addr_str, AF_INET6, &addr4) == 0,
- "Should not be able to parse IPv4 address in IPv6 mode");
-
- fail_if(nl_addr_parse(addr_str, AF_UNSPEC, &addr4) != 0,
- "Should be able to parse \"%s\"", addr_str);
-
- fail_if(nl_addr_get_family(addr4) != AF_INET,
- "Address family should be AF_INET");
-
- fail_if(nl_addr_get_prefixlen(addr4) != 16,
- "Prefix length should be 16");
-
- fail_if(nl_addr_iszero(addr4),
- "Address should not be all zeroes");
-
- clone = nl_addr_clone(addr4);
- fail_if(clone == NULL,
- "Cloned address should not be NULL");
-
- fail_if(nl_addr_cmp(addr4, clone) != 0,
- "Cloned address should not mismatch original");
-
- fail_if(nl_addr_fill_sockaddr(addr4, (struct sockaddr *) &sin, &len) != 0,
- "Should be able to fill socketaddr");
-
- fail_if(strcmp(nl_addr2str(addr4, buf, sizeof(buf)), addr_str),
- "Address translated back to string does not match original");
-
- nl_addr_put(addr4);
- nl_addr_put(clone);
-}
-END_TEST
-
-START_TEST(addr_parse6)
-{
- struct nl_addr *addr6, *clone;
- struct sockaddr_in6 sin;
- socklen_t len = sizeof(sin);
- char *addr_str = "2001:1:2::3/64";
- char buf[128];
-
- fail_if(nl_addr_parse(addr_str, AF_INET, &addr6) == 0,
- "Should not be able to parse IPv6 address in IPv4 mode");
-
- fail_if(nl_addr_parse(addr_str, AF_UNSPEC, &addr6) != 0,
- "Should be able to parse \"%s\"", addr_str);
-
- fail_if(nl_addr_get_family(addr6) != AF_INET6,
- "Address family should be AF_INET6");
-
- fail_if(nl_addr_get_prefixlen(addr6) != 64,
- "Prefix length should be 64");
-
- fail_if(nl_addr_iszero(addr6),
- "Address should not be all zeroes");
-
- clone = nl_addr_clone(addr6);
- fail_if(clone == NULL,
- "Cloned address should not be NULL");
-
- fail_if(nl_addr_cmp(addr6, clone) != 0,
- "Cloned address should not mismatch original");
-
- fail_if(nl_addr_fill_sockaddr(addr6, (struct sockaddr *) &sin, &len) != 0,
- "Should be able to fill socketaddr");
-
- fail_if(strcmp(nl_addr2str(addr6, buf, sizeof(buf)), addr_str),
- "Address translated back to string does not match original");
-
- nl_addr_put(addr6);
- nl_addr_put(clone);
-}
-END_TEST
-
-START_TEST(addr_info)
-{
- struct nl_addr *addr;
- char *addr_str = "127.0.0.1";
- struct addrinfo *result;
-
- fail_if(nl_addr_parse(addr_str, AF_UNSPEC, &addr) != 0,
- "Parsing of valid address should not fail");
-
- fail_if(nl_addr_info(addr, &result) != 0,
- "getaddrinfo() on loopback address should work");
-
- freeaddrinfo(result);
- nl_addr_put(addr);
-}
-END_TEST
-
-Suite *make_nl_addr_suite(void)
-{
- Suite *suite = suite_create("Abstract addresses");
-
- TCase *tc_addr = tcase_create("Core");
- tcase_add_test(tc_addr, addr_alloc);
- tcase_add_test(tc_addr, addr_binary_addr);
- tcase_add_test(tc_addr, addr_parse4);
- tcase_add_test(tc_addr, addr_parse6);
- tcase_add_test(tc_addr, addr_info);
- suite_add_tcase(suite, tc_addr);
-
- return suite;
-}
diff --git a/tests/check-all.c b/tests/check-all.c
index 7b738daa..aff16cbb 100644
--- a/tests/check-all.c
+++ b/tests/check-all.c
@@ -1,17 +1,11 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
/*
- * tests/check-all.c overall unit test program
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation version 2.1
- * of the License.
- *
* Copyright (c) 2013 Thomas Graf <tgraf@suug.ch>
*/
#include <check.h>
-#include "util.h"
+#include "cksuite-all.h"
static Suite *main_suite(void)
{
@@ -24,16 +18,13 @@ int main(int argc, char *argv[])
{
SRunner *runner;
int nfailed;
-
- runner = srunner_create(main_suite());
- /* Add testsuites below */
+ runner = srunner_create(main_suite());
srunner_add_suite(runner, make_nl_addr_suite());
srunner_add_suite(runner, make_nl_attr_suite());
srunner_add_suite(runner, make_nl_ematch_tree_clone_suite());
-
- /* Do not add testsuites below this line */
+ srunner_add_suite(runner, make_nl_netns_suite());
srunner_run_all(runner, CK_ENV);
diff --git a/tests/check-attr.c b/tests/check-attr.c
deleted file mode 100644
index 0390997c..00000000
--- a/tests/check-attr.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * tests/check-attr.c nla_attr unit tests
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation version 2.1
- * of the License.
- *
- * Copyright (c) 2013 Thomas Graf <tgraf@suug.ch>
- */
-
-#include "util.h"
-#include <netlink/attr.h>
-#include <netlink/msg.h>
-
-#include <linux/netlink.h>
-
-START_TEST(attr_size)
-{
- fail_if(nla_attr_size(0) != NLA_HDRLEN,
- "Length of empty attribute should match header size");
- fail_if(nla_attr_size(1) != NLA_HDRLEN + 1,
- "Length of 1 bytes payload should be NLA_HDRLEN + 1");
- fail_if(nla_attr_size(2) != NLA_HDRLEN + 2,
- "Length of 2 bytes payload should be NLA_HDRLEN + 2");
- fail_if(nla_attr_size(3) != NLA_HDRLEN + 3,
- "Length of 3 bytes payload should be NLA_HDRLEN + 3");
- fail_if(nla_attr_size(4) != NLA_HDRLEN + 4,
- "Length of 4 bytes payload should be NLA_HDRLEN + 4");
-
- fail_if(nla_total_size(1) != NLA_HDRLEN + 4,
- "Total size of 1 bytes payload should result in 8 bytes");
- fail_if(nla_total_size(2) != NLA_HDRLEN + 4,
- "Total size of 2 bytes payload should result in 8 bytes");
- fail_if(nla_total_size(3) != NLA_HDRLEN + 4,
- "Total size of 3 bytes payload should result in 8 bytes");
- fail_if(nla_total_size(4) != NLA_HDRLEN + 4,
- "Total size of 4 bytes payload should result in 8 bytes");
-
- fail_if(nla_padlen(1) != 3,
- "2 bytes of payload should result in 3 padding bytes");
- fail_if(nla_padlen(2) != 2,
- "2 bytes of payload should result in 2 padding bytes");
- fail_if(nla_padlen(3) != 1,
- "3 bytes of payload should result in 1 padding bytes");
- fail_if(nla_padlen(4) != 0,
- "4 bytes of payload should result in 0 padding bytes");
- fail_if(nla_padlen(5) != 3,
- "5 bytes of payload should result in 3 padding bytes");
-}
-END_TEST
-
-START_TEST(msg_construct)
-{
- struct nl_msg *msg;
- struct nlmsghdr *nlh;
- struct nlattr *a;
- int i, rem;
-
- msg = nlmsg_alloc();
- fail_if(!msg, "Unable to allocate netlink message");
-
- for (i = 1; i < 256; i++) {
- fail_if(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) {
- fail_if(nla_type(a) != i, "Expected attribute %d", i);
- i++;
- fail_if(nla_get_u32(a) != i, "Expected attribute value %d", i);
- }
-
- nlmsg_free(msg);
-}
-END_TEST
-
-Suite *make_nl_attr_suite(void)
-{
- Suite *suite = suite_create("Netlink attributes");
-
- TCase *nl_attr = tcase_create("Core");
- tcase_add_test(nl_attr, attr_size);
- tcase_add_test(nl_attr, msg_construct);
- suite_add_tcase(suite, nl_attr);
-
- return suite;
-}
diff --git a/tests/check-direct.c b/tests/check-direct.c
new file mode 100644
index 00000000..f40bcee9
--- /dev/null
+++ b/tests/check-direct.c
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+
+#include <check.h>
+
+#include "linux/snmp.h"
+#include "netlink-private/utils.h"
+#include "netlink-private/route/utils.h"
+
+#include "netlink/route/link.h"
+
+START_TEST(static_checks)
+{
+ int i, j;
+ 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);
+ 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);
+ 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]);
+ }
+
+ 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));
+ if (strncmp(s, "0x", 2) == 0) {
+ assert(i == RTNL_LINK_STATS_MAX + 1);
+ ck_assert_int_eq(strtoll(&s[2], NULL, 16), i);
+ } else
+ ck_assert_int_le(i, RTNL_LINK_STATS_MAX);
+ ck_assert_int_eq(i, rtnl_link_str2stat(s));
+ }
+}
+END_TEST
+
+static Suite *make_suite(void)
+{
+ Suite *suite = suite_create("Direct");
+ TCase *tc = tcase_create("Core");
+
+ tcase_add_test(tc, static_checks);
+ suite_add_tcase(suite, tc);
+ return suite;
+}
+
+int main(int argc, char *argv[])
+{
+ SRunner *runner;
+ int nfailed;
+
+ runner = srunner_create(suite_create("main"));
+
+ srunner_add_suite(runner, make_suite());
+
+ srunner_run_all(runner, CK_ENV);
+
+ nfailed = srunner_ntests_failed(runner);
+ srunner_free(runner);
+ return nfailed != 0;
+}
diff --git a/tests/cksuite-all-addr.c b/tests/cksuite-all-addr.c
new file mode 100644
index 00000000..f395351b
--- /dev/null
+++ b/tests/cksuite-all-addr.c
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/*
+ * Copyright (c) 2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <check.h>
+#include <netlink/addr.h>
+#include <netlink/route/addr.h>
+
+#include "cksuite-all.h"
+
+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(nl_addr_iszero(addr) != 0,
+ "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");
+
+ ck_assert_msg(nl_addr_get_prefixlen(addr) == 0,
+ "New empty address should have prefix length 0");
+
+ ck_assert_msg(!nl_addr_shared(addr),
+ "New empty address should not be shared");
+
+ ck_assert_msg(nl_addr_get(addr) == addr,
+ "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()");
+
+ nl_addr_put(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");
+
+ nl_addr_put(addr);
+}
+END_TEST
+
+START_TEST(addr_binary_addr)
+{
+ struct nl_addr *addr, *addr2;
+ char baddr[4] = { 0x1, 0x2, 0x3, 0x4 };
+ 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(nl_addr_set_binary_addr(addr, baddr, 4) >= 0,
+ "Valid binary address should be settable");
+
+ 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_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");
+
+ 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");
+
+ addr2 = nl_addr_build(AF_UNSPEC, baddr, 4);
+ 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,
+ "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");
+
+ nl_addr_put(addr);
+ nl_addr_put(addr2);
+}
+END_TEST
+
+START_TEST(addr_parse4)
+{
+ struct nl_addr *addr4, *clone;
+ struct sockaddr_in sin;
+ socklen_t len = sizeof(sin);
+ char *addr_str = "10.0.0.1/16";
+ 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");
+
+ ck_assert_msg(nl_addr_parse(addr_str, AF_UNSPEC, &addr4) == 0,
+ "Should be able to parse \"%s\"", addr_str);
+
+ ck_assert_msg(nl_addr_get_family(addr4) == AF_INET,
+ "Address family should be AF_INET");
+
+ ck_assert_msg(nl_addr_get_prefixlen(addr4) == 16,
+ "Prefix length should be 16");
+
+ ck_assert_msg(!nl_addr_iszero(addr4),
+ "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(nl_addr_cmp(addr4, clone) == 0,
+ "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(
+ !strcmp(nl_addr2str(addr4, buf, sizeof(buf)), addr_str),
+ "Address translated back to string does not match original");
+
+ nl_addr_put(addr4);
+ nl_addr_put(clone);
+}
+END_TEST
+
+START_TEST(addr_parse6)
+{
+ struct nl_addr *addr6, *clone;
+ struct sockaddr_in6 sin;
+ socklen_t len = sizeof(sin);
+ char *addr_str = "2001:1:2::3/64";
+ 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");
+
+ ck_assert_msg(nl_addr_parse(addr_str, AF_UNSPEC, &addr6) == 0,
+ "Should be able to parse \"%s\"", addr_str);
+
+ ck_assert_msg(nl_addr_get_family(addr6) == AF_INET6,
+ "Address family should be AF_INET6");
+
+ ck_assert_msg(nl_addr_get_prefixlen(addr6) == 64,
+ "Prefix length should be 64");
+
+ ck_assert_msg(!nl_addr_iszero(addr6),
+ "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(nl_addr_cmp(addr6, clone) == 0,
+ "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(
+ !strcmp(nl_addr2str(addr6, buf, sizeof(buf)), addr_str),
+ "Address translated back to string does not match original");
+
+ nl_addr_put(addr6);
+ nl_addr_put(clone);
+}
+END_TEST
+
+START_TEST(addr_info)
+{
+ struct nl_addr *addr;
+ char *addr_str = "127.0.0.1";
+ struct addrinfo *result;
+
+ ck_assert_msg(nl_addr_parse(addr_str, AF_UNSPEC, &addr) == 0,
+ "Parsing of valid address should not fail");
+
+ ck_assert_msg(nl_addr_info(addr, &result) == 0,
+ "getaddrinfo() on loopback address should work");
+
+ freeaddrinfo(result);
+ nl_addr_put(addr);
+}
+END_TEST
+
+START_TEST(addr_flags2str)
+{
+ int ifa_flags = IFA_F_TENTATIVE | IFA_F_DADFAILED;
+ int ifa_flags2;
+ char buf[128];
+
+ rtnl_addr_flags2str(ifa_flags, buf, sizeof(buf));
+ ck_assert_str_eq(buf, "dadfailed,tentative");
+
+ ifa_flags2 = rtnl_addr_str2flags(buf);
+ ck_assert_int_eq(ifa_flags2, ifa_flags);
+}
+END_TEST
+
+Suite *make_nl_addr_suite(void)
+{
+ Suite *suite = suite_create("Abstract addresses");
+ TCase *tc = tcase_create("Core");
+
+ tcase_add_test(tc, addr_alloc);
+ tcase_add_test(tc, addr_binary_addr);
+ tcase_add_test(tc, addr_parse4);
+ tcase_add_test(tc, addr_parse6);
+ tcase_add_test(tc, addr_info);
+ tcase_add_test(tc, addr_flags2str);
+ suite_add_tcase(suite, tc);
+
+ return suite;
+}
diff --git a/tests/cksuite-all-attr.c b/tests/cksuite-all-attr.c
new file mode 100644
index 00000000..824a5960
--- /dev/null
+++ b/tests/cksuite-all-attr.c
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/*
+ * Copyright (c) 2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <linux/netlink.h>
+#include <linux/if_ether.h>
+
+#include "cksuite-all.h"
+#include "netlink/attr.h"
+#include "netlink/msg.h"
+#include "netlink/route/cls/u32.h"
+#include "netlink-private/route/tc-api.h"
+#include "netlink-private/nl-auto.h"
+
+START_TEST(attr_size)
+{
+ ck_assert_msg(nla_attr_size(0) == NLA_HDRLEN,
+ "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");
+ ck_assert_msg(nla_attr_size(2) == 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");
+ ck_assert_msg(nla_attr_size(4) == 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");
+ ck_assert_msg(nla_total_size(2) == NLA_HDRLEN + 4,
+ "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");
+ ck_assert_msg(nla_total_size(4) == NLA_HDRLEN + 4,
+ "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");
+ ck_assert_msg(nla_padlen(2) == 2,
+ "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");
+ ck_assert_msg(nla_padlen(4) == 0,
+ "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");
+}
+END_TEST
+
+START_TEST(msg_construct)
+{
+ struct nl_msg *msg;
+ struct nlmsghdr *nlh;
+ struct nlattr *a;
+ int i, rem;
+
+ msg = nlmsg_alloc();
+ 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);
+ }
+
+ nlh = nlmsg_hdr(msg);
+ i = 1;
+ 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);
+ }
+
+ nlmsg_free(msg);
+}
+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;
+
+ link = rtnl_link_alloc();
+ ck_assert(link);
+
+ rtnl_link_set_ifindex(link, 5);
+
+ cls = rtnl_cls_alloc();
+ ck_assert(cls);
+
+ rtnl_tc_set_link(TC_CAST(cls), link);
+
+ 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_tc_set_parent(TC_CAST(cls), TC_HANDLE(1, 0));
+
+ rtnl_u32_set_hashtable(cls, 5);
+
+ rtnl_u32_add_key_uint32(cls, 0x0a000914, 0xffffffff, direction, 0);
+
+ rtnl_u32_set_hashmask(cls, 0xff000000, direction);
+
+ rtnl_u32_add_mark(cls, 55, 66);
+
+ rtnl_u32_set_link(cls, 44);
+
+ 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");
+ TCase *tc = tcase_create("Core");
+
+ 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/check-ematch-tree-clone.c b/tests/cksuite-all-ematch-tree-clone.c
index 9c35c52e..f9d17a98 100644
--- a/tests/check-ematch-tree-clone.c
+++ b/tests/cksuite-all-ematch-tree-clone.c
@@ -1,23 +1,20 @@
-#include <netlink-private/types.h>
-#include <netlink/route/cls/ematch.h>
-
#include <linux/netlink.h>
-
#include <stdio.h>
#include <time.h>
-
#include <check.h>
-#include "util.h"
-#define MAX_DEPTH 6
-#define MAX_CHILDREN 5
+#include "netlink-private/types.h"
+#include "netlink/route/cls/ematch.h"
+#include "cksuite-all.h"
+#include "netlink-private/nl-auto.h"
+
+#define MAX_DEPTH 6
+#define MAX_CHILDREN 5
static int current_depth = 0;
static int id = 1;
static long long array_size = 0;
-static int *src_result = NULL, *dst_result = NULL;
-
static long long my_pow(long long x, long long y)
{
int ret = x;
@@ -28,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;
}
@@ -79,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;
@@ -87,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;
@@ -107,8 +105,12 @@ static int compare(int *r1, int *r2, int len)
START_TEST(ematch_tree_clone)
{
- struct rtnl_ematch_tree *src = NULL, *dst = NULL;
- int i = 0, j = 0;
+ _nl_auto_rtnl_ematch_tree struct rtnl_ematch_tree *src = NULL;
+ _nl_auto_rtnl_ematch_tree struct rtnl_ematch_tree *dst = NULL;
+ _nl_auto_free int *src_result = NULL;
+ _nl_auto_free int *dst_result = NULL;
+ int i = 0;
+ int j = 0;
array_size = (MAX_DEPTH * my_pow(MAX_CHILDREN, MAX_DEPTH)) / 2;
src_result = calloc(4, array_size);
@@ -122,22 +124,19 @@ START_TEST(ematch_tree_clone)
dst = rtnl_ematch_tree_clone(src);
dump_ematch_tree(dst, dst_result, &j);
- fail_if(!dst);
- fail_if(i != j);
- fail_if(compare(src_result, dst_result, i));
-
- free(src_result);
- free(dst_result);
+ ck_assert(dst);
+ ck_assert(i == j);
+ ck_assert(!compare(src_result, dst_result, i));
}
END_TEST
Suite *make_nl_ematch_tree_clone_suite(void)
{
Suite *suite = suite_create("Clone ematch tree");
+ TCase *tc = tcase_create("Core");
- TCase *ematch_tree = tcase_create("Core");
- tcase_add_test(ematch_tree, ematch_tree_clone);
- suite_add_tcase(suite, ematch_tree);
+ tcase_add_test(tc, ematch_tree_clone);
+ suite_add_tcase(suite, tc);
return suite;
}
diff --git a/tests/cksuite-all-netns.c b/tests/cksuite-all-netns.c
new file mode 100644
index 00000000..97f3a702
--- /dev/null
+++ b/tests/cksuite-all-netns.c
@@ -0,0 +1,319 @@
+/* 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>
+#include <linux/netlink.h>
+
+#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;
+ _nl_auto_nl_cache struct nl_cache *link_cache = NULL;
+ _nl_auto_free struct nl_object **links_all = NULL;
+ static const struct {
+ const char *ifname;
+ const char *kind;
+ bool add;
+ } links[] = {
+ {
+ .ifname = "xbr0",
+ .kind = "bridge",
+ .add = true,
+ },
+ {
+ .ifname = "xdummy0",
+ .kind = "dummy",
+ .add = true,
+ },
+ {
+ .ifname = "xbond0",
+ .kind = "bond",
+ .add = true,
+ },
+ {
+ .ifname = "lo",
+ .kind = NULL,
+ .add = false,
+ },
+ };
+ int i;
+ int r;
+
+ for (i = 0; i < _NL_N_ELEMENTS(links); i++) {
+ if (links[i].add)
+ _nltst_add_link(NULL, links[i].ifname, links[i].kind,
+ NULL);
+ }
+
+ sk = _nltst_socket(NETLINK_ROUTE);
+
+ r = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache);
+ ck_assert_int_eq(r, 0);
+
+ r = nl_cache_refill(sk, link_cache);
+ ck_assert_int_eq(r, 0);
+
+ for (i = 0; i < _NL_N_ELEMENTS(links); i++) {
+ _nl_auto_rtnl_link struct rtnl_link *link_clone = NULL;
+ struct rtnl_link *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);
+
+ if (_nl_streq(links[i].ifname, "lo"))
+ ck_assert_int_eq(rtnl_link_get_ifindex(link), 1);
+ else
+ ck_assert_int_gt(rtnl_link_get_ifindex(link), 1);
+
+ link_clone = (struct rtnl_link *)nl_object_clone(
+ (struct nl_object *)link);
+ ck_assert(link_clone);
+
+ _nltst_object_identical(link, link_clone);
+ }
+
+ links_all = _nltst_cache_get_all(link_cache, NULL);
+ for (i = 0; links_all[i]; i++) {
+ struct rtnl_link *link = (struct rtnl_link *)links_all[i];
+ _nl_auto_rtnl_link struct rtnl_link *link_clone = NULL;
+
+ link_clone = (struct rtnl_link *)nl_object_clone(
+ (struct nl_object *)link);
+ ck_assert(link_clone);
+
+ _nltst_object_identical(link, link_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");
+ TCase *tc = tcase_create("Core");
+
+ 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/cksuite-all.h b/tests/cksuite-all.h
new file mode 100644
index 00000000..c4b1d8e5
--- /dev/null
+++ b/tests/cksuite-all.h
@@ -0,0 +1,12 @@
+#ifndef __LIBNL3_TESTS_CHECK_ALL_H__
+#define __LIBNL3_TESTS_CHECK_ALL_H__
+
+#include <check.h>
+#include "nl-test-util.h"
+
+Suite *make_nl_attr_suite(void);
+Suite *make_nl_addr_suite(void);
+Suite *make_nl_ematch_tree_clone_suite(void);
+Suite *make_nl_netns_suite(void);
+
+#endif /* __LIBNL3_TESTS_CHECK_ALL_H__ */
diff --git a/tests/nl-test-util.c b/tests/nl-test-util.c
new file mode 100644
index 00000000..1f67ac88
--- /dev/null
+++ b/tests/nl-test-util.c
@@ -0,0 +1,657 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+
+#include "nl-test-util.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <sched.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+#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 {
+ int canary;
+};
+
+/*****************************************************************************/
+
+#define _assert_nltst_netns(nsdata) \
+ do { \
+ const struct nltst_netns *_nsdata = (nsdata); \
+ \
+ ck_assert_ptr_nonnull(_nsdata); \
+ ck_assert_int_eq(_nsdata->canary, _CANARY); \
+ } while (0)
+
+static struct {
+ struct nltst_netns *nsdata;
+} _netns_fixture_global;
+
+void nltst_netns_fixture_setup(void)
+{
+ ck_assert(!_netns_fixture_global.nsdata);
+
+ _netns_fixture_global.nsdata = nltst_netns_enter();
+ _assert_nltst_netns(_netns_fixture_global.nsdata);
+}
+
+void nltst_netns_fixture_teardown(void)
+{
+ _assert_nltst_netns(_netns_fixture_global.nsdata);
+ _nl_clear_pointer(&_netns_fixture_global.nsdata, nltst_netns_leave);
+}
+
+/*****************************************************************************/
+
+static void unshare_user(void)
+{
+ const uid_t uid = geteuid();
+ const gid_t gid = getegid();
+ FILE *f;
+ int r;
+
+ /* Become a root in new user NS. */
+ r = unshare(CLONE_NEWUSER);
+ _nltst_assert_errno(r == 0);
+
+ /* Since Linux 3.19 we have to disable setgroups() in order to map users.
+ * Just proceed if the file is not there. */
+ f = fopen("/proc/self/setgroups", "we");
+ if (f) {
+ r = fprintf(f, "deny");
+ _nltst_assert_errno(r > 0);
+ _nltst_fclose(f);
+ }
+
+ /* Map current UID to root in NS to be created. */
+ f = fopen("/proc/self/uid_map", "we");
+ if (!f) {
+ if (errno == EACCES) {
+ /* Accessing uid_map might fail due to sandboxing.
+ * We ignore that error and proceed with the test. It will probably
+ * still work. */
+ return;
+ }
+ _nltst_assert_errno(f);
+ }
+ r = fprintf(f, "0 %d 1", uid);
+ _nltst_assert_errno(r > 0);
+ _nltst_fclose(f);
+
+ /* Map current GID to root in NS to be created. */
+ f = fopen("/proc/self/gid_map", "we");
+ _nltst_assert_errno(f);
+ r = fprintf(f, "0 %d 1", gid);
+ _nltst_assert_errno(r > 0);
+ _nltst_fclose(f);
+}
+
+struct nltst_netns *nltst_netns_enter(void)
+{
+ struct nltst_netns *nsdata;
+ int r;
+
+ nsdata = calloc(1, sizeof(struct nltst_netns));
+ ck_assert(nsdata);
+
+ nsdata->canary = _CANARY;
+
+ unshare_user();
+
+ r = unshare(CLONE_NEWNET | CLONE_NEWNS);
+ _nltst_assert_errno(r == 0);
+
+ /* We need a read-only /sys so that the platform knows there's no udev. */
+ mount(NULL, "/sys", "sysfs", MS_SLAVE, NULL);
+ r = mount("sys", "/sys", "sysfs", MS_RDONLY, NULL);
+ _nltst_assert_errno(r == 0);
+
+ return nsdata;
+}
+
+void nltst_netns_leave(struct nltst_netns *nsdata)
+{
+ ck_assert(nsdata);
+ ck_assert_int_eq(nsdata->canary, _CANARY);
+
+ /* nltst_netns_leave() was supposed to enter the original namespaces again
+ * and undo enter.
+ *
+ * However, I could get it to work (setns() always fails with EPERM)
+ * and valgrind on current Ubuntu seems not to support setns() call.
+ *
+ * So, do nothing. It's not really a problem, because the next test
+ * either should unshare yet another namespace, or not care about
+ * such things. */
+
+ free(nsdata);
+}
+
+/*****************************************************************************/
+
+void _nltst_object_identical(const void *a, const void *b)
+{
+ struct nl_object *o_a = (void *)a;
+ struct nl_object *o_b = (void *)b;
+
+ ck_assert(a);
+ ck_assert(b);
+
+ ck_assert_int_eq(nl_object_identical(o_a, o_b), 1);
+ ck_assert_int_eq(nl_object_identical(o_b, o_a), 1);
+ ck_assert_int_eq(nl_object_diff64(o_b, o_a), 0);
+ ck_assert_int_eq(nl_object_diff64(o_a, o_b), 0);
+ ck_assert_int_eq(nl_object_diff(o_a, o_b), 0);
+ ck_assert_int_eq(nl_object_diff(o_b, o_a), 0);
+}
+
+/*****************************************************************************/
+
+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;
+ size_t idx;
+};
+
+static void _cache_get_all_fcn(struct nl_object *obj, void *user_data)
+{
+ struct cache_get_all_data *data = user_data;
+ size_t i;
+
+ ck_assert(obj);
+ ck_assert_int_lt(data->idx, data->len);
+
+ for (i = 0; i < data->idx; i++)
+ ck_assert_ptr_ne(data->arr[i], obj);
+
+ data->arr[data->idx++] = obj;
+}
+
+struct nl_object **_nltst_cache_get_all(struct nl_cache *cache, size_t *out_len)
+{
+ int nitems;
+ struct cache_get_all_data data = {
+ .idx = 0,
+ .len = 0,
+ };
+ size_t len2 = 0;
+ size_t i;
+ size_t j;
+
+ ck_assert(cache);
+
+ nitems = nl_cache_nitems(cache);
+ ck_assert_int_ge(nitems, 0);
+
+ data.len = nitems;
+ data.arr = malloc(sizeof(struct nl_object *) * (data.len + 1));
+ ck_assert_ptr_nonnull(data.arr);
+
+ nl_cache_foreach(cache, _cache_get_all_fcn, &data);
+
+ 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)
+{
+ struct nl_sock *sk;
+ int r;
+
+ sk = nl_socket_alloc();
+ ck_assert(sk);
+
+ 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,
+ 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;
+ }
+
+ link = rtnl_link_alloc();
+ ck_assert(link);
+
+ r = rtnl_link_set_type(link, kind);
+ ck_assert_int_eq(r, 0);
+
+ rtnl_link_set_name(link, ifname);
+
+ 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
new file mode 100644
index 00000000..0ff97528
--- /dev/null
+++ b/tests/nl-test-util.h
@@ -0,0 +1,400 @@
+/* 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__ */
diff --git a/tests/test-complex-HTB-with-hash-filters.c b/tests/test-complex-HTB-with-hash-filters.c
index b1bf36e5..016d467a 100644
--- a/tests/test-complex-HTB-with-hash-filters.c
+++ b/tests/test-complex-HTB-with-hash-filters.c
@@ -1,11 +1,5 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
/*
- * test/test-complex-HTB-with-hash-filters.c Add HTB qdisc, HTB classes and creates some hash filters
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation version 2.1
- * of the License.
- *
* Copyright (c) 2011 Adrian Ban <adrian.ban@mantech.ro>
*/
diff --git a/tests/test-u32-filter-with-actions.c b/tests/test-u32-filter-with-actions.c
index e9910e33..17e7bdc1 100644
--- a/tests/test-u32-filter-with-actions.c
+++ b/tests/test-u32-filter-with-actions.c
@@ -1,14 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
/*
- * test/tests-u32-with-actions.c Add ingress qdisc, create some hash filters, and add redirect action
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation version 2.1
- * of the License.
+ * Copyright (c) 2013 Cong Wang <xiyou.wangcong@gmail.com>
*
* Stolen from tests/test-complex-HTB-with-hash-filters.c
- *
- * Copyright (c) 2013 Cong Wang <xiyou.wangcong@gmail.com>
*/
#include <netlink/route/link.h>
diff --git a/tests/util.h b/tests/util.h
deleted file mode 100644
index 8c9acf12..00000000
--- a/tests/util.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#include <check.h>
-
-#define nl_fail_if(condition, error, message) \
- fail_if((condition), "nlerr=%d (%s): %s", \
- (error), nl_geterror(error), (message))
-
-Suite *make_nl_attr_suite(void);
-Suite *make_nl_addr_suite(void);
-Suite *make_nl_ematch_tree_clone_suite(void);
-