diff options
author | Thomas Haller <thaller@redhat.com> | 2022-05-09 16:34:18 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2022-05-09 16:34:18 +0200 |
commit | df6058cfa60f45b44b73ed77ef09a621c41a9375 (patch) | |
tree | cf157731cbddcf2e0d28525dd9776966e7350ab1 | |
parent | 9b0493646c20f3eecdf664e7e00c6c9162e04fba (diff) | |
parent | 9772c1def0038d0814a4c558ed809fdaafb65784 (diff) | |
download | libnl-df6058cfa60f45b44b73ed77ef09a621c41a9375.tar.gz |
tests: merge branch 'th/test-link'
https://github.com/thom311/libnl/pull/314
-rw-r--r-- | .github/workflows/ci.yml | 11 | ||||
-rw-r--r-- | include/netlink-private/nl-auto.h | 8 | ||||
-rw-r--r-- | include/netlink-private/utils.h | 17 | ||||
-rw-r--r-- | tests/check-direct.c | 15 | ||||
-rw-r--r-- | tests/cksuite-all-addr.c | 99 | ||||
-rw-r--r-- | tests/cksuite-all-attr.c | 109 | ||||
-rw-r--r-- | tests/cksuite-all-ematch-tree-clone.c | 11 | ||||
-rw-r--r-- | tests/cksuite-all-netns.c | 222 | ||||
-rw-r--r-- | tests/nl-test-util.c | 412 | ||||
-rw-r--r-- | tests/nl-test-util.h | 336 |
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__ */ |