summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2024-05-17 14:54:50 (GMT)
committerThomas Haller <thaller@redhat.com>2024-05-17 17:03:15 (GMT)
commit2ebbc0342ea49df16f801eb11703ec9f868d2f97 (patch)
tree609ceff06862fd034c3ee1cd5fa83562453da56a
parentd784f2cb42bde01f27feacebdfaf51033ae82f02 (diff)
downloadlibnl-2ebbc0342ea49df16f801eb11703ec9f868d2f97.zip
libnl-2ebbc0342ea49df16f801eb11703ec9f868d2f97.tar.gz
libnl-2ebbc0342ea49df16f801eb11703ec9f868d2f97.tar.bz2
tests: add NLTstSelectRoute test helper
Add functions for matching a route. Currently only a few trivial options are supported, like the destination address and the ifindex.
-rw-r--r--tests/cksuite-all-attr.c55
-rw-r--r--tests/nl-test-util.c282
-rw-r--r--tests/nl-test-util.h64
3 files changed, 401 insertions, 0 deletions
diff --git a/tests/cksuite-all-attr.c b/tests/cksuite-all-attr.c
index b4ac61f..943eda4 100644
--- a/tests/cksuite-all-attr.c
+++ b/tests/cksuite-all-attr.c
@@ -144,6 +144,60 @@ END_TEST
/*****************************************************************************/
+START_TEST(test_nltst_select_route)
+{
+ /* This is a unit test for testing the unit-test helper function
+ * _nltst_select_route_parse(). */
+
+#define _check(str, exp_addr_family, exp_addr_pattern, exp_plen) \
+ do { \
+ const char *_str = (str); \
+ const int _exp_addr_family = (exp_addr_family); \
+ const char *const _exp_addr_pattern = (exp_addr_pattern); \
+ const int _exp_plen = (exp_plen); \
+ _nltst_auto_clear_select_route NLTstSelectRoute \
+ _select_route = { 0 }; \
+ _nltst_auto_clear_select_route NLTstSelectRoute \
+ _select_route2 = { 0 }; \
+ _nl_auto_free char *_str2 = NULL; \
+ \
+ _nltst_select_route_parse(_str, &_select_route); \
+ ck_assert_int_eq(_exp_addr_family, _select_route.addr_family); \
+ if (_nltst_inet_valid(AF_UNSPEC, _exp_addr_pattern)) { \
+ ck_assert_str_eq(_exp_addr_pattern, \
+ _select_route.addr); \
+ ck_assert_ptr_null(_select_route.addr_pattern); \
+ } else { \
+ ck_assert_str_eq(_exp_addr_pattern, \
+ _select_route.addr_pattern); \
+ ck_assert_ptr_null(_select_route.addr); \
+ } \
+ ck_assert_int_eq(_exp_plen, _select_route.plen); \
+ \
+ _nltst_assert_select_route(&_select_route); \
+ \
+ _str2 = _nltst_select_route_to_string(&_select_route); \
+ ck_assert_ptr_nonnull(_str2); \
+ \
+ _nltst_select_route_parse(_str2, &_select_route2); \
+ \
+ ck_assert(_nltst_select_route_equal(&_select_route, \
+ &_select_route2)); \
+ } while (0)
+
+ _check("0.0.0.0", AF_INET, "0.0.0.0", -1);
+ _check("4 0.0.0.0/0", AF_INET, "0.0.0.0", 0);
+ _check(" 6\n 0:0::/0", AF_INET6, "::", 0);
+ _check(" \n 0:0::/100", AF_INET6, "::", 100);
+ _check("6 0:0::*/0 ", AF_INET6, "0:0::*", 0);
+ _check("6 0:0::*/128 ", AF_INET6, "0:0::*", 128);
+ _check("6 0:0::* ", AF_INET6, "0:0::*", -1);
+
+#undef _check
+}
+
+/*****************************************************************************/
+
Suite *make_nl_attr_suite(void)
{
Suite *suite = suite_create("Netlink attributes");
@@ -153,6 +207,7 @@ Suite *make_nl_attr_suite(void)
tcase_add_test(tc, msg_construct);
tcase_add_test(tc, clone_cls_u32);
tcase_add_test(tc, test_nltst_strtok);
+ tcase_add_test(tc, test_nltst_select_route);
suite_add_tcase(suite, tc);
return suite;
diff --git a/tests/nl-test-util.c b/tests/nl-test-util.c
index fda9875..66fb4cb 100644
--- a/tests/nl-test-util.c
+++ b/tests/nl-test-util.c
@@ -5,6 +5,7 @@
#include "nl-test-util.h"
#include <fcntl.h>
+#include <fnmatch.h>
#include <sched.h>
#include <stdio.h>
#include <sys/mount.h>
@@ -14,6 +15,8 @@
#include <netlink/route/route.h>
#include <netlink/socket.h>
+#include "lib/route/nl-route.h"
+
#include "nl-aux-route/nl-route.h"
/*****************************************************************************/
@@ -708,3 +711,282 @@ bool _nltst_skip_no_iproute2(const char *msg)
msg ?: "", msg ? ")" : "");
return true;
}
+
+/*****************************************************************************/
+
+void _nltst_select_route_clear(NLTstSelectRoute *select_route)
+{
+ _nltst_assert_select_route(select_route);
+
+ _nl_clear_free(&select_route->addr);
+ _nl_clear_free(&select_route->addr_pattern);
+}
+
+int _nltst_select_route_cmp(const NLTstSelectRoute *select_route1,
+ const NLTstSelectRoute *select_route2)
+{
+ _NL_CMP_SELF(select_route1, select_route2);
+ _NL_CMP_FIELD_STR0(select_route1, select_route2, addr);
+ _NL_CMP_FIELD_STR0(select_route1, select_route2, addr_pattern);
+ _NL_CMP_FIELD(select_route1, select_route2, addr_family);
+ _NL_CMP_FIELD(select_route1, select_route2, ifindex);
+ _NL_CMP_FIELD(select_route1, select_route2, plen);
+ return 0;
+}
+
+char *_nltst_select_route_to_string(const NLTstSelectRoute *select_route)
+{
+ char buf[1024];
+ const char *family;
+ char b_plen[100];
+
+ _nltst_assert_select_route(select_route);
+
+ if (select_route->addr_family == AF_INET)
+ family = "4 ";
+ else if (select_route->addr_family == AF_INET6)
+ family = "6 ";
+ else
+ family = "";
+
+ b_plen[0] = '\0';
+ if (select_route->plen != -1)
+ _nltst_sprintf_arr(b_plen, "/%d", select_route->plen);
+
+ _nltst_sprintf_arr(buf,
+ "%s"
+ "%s"
+ "%s"
+ "",
+ family,
+ select_route->addr_pattern ?: select_route->addr,
+ b_plen);
+ return _nltst_strdup(buf);
+}
+
+void _nltst_select_route_parse(const char *str,
+ NLTstSelectRoute *out_select_route)
+{
+ _nltst_auto_strfreev char **tokens0 = NULL;
+ const char *const *tokens;
+ int addr_family = AF_UNSPEC;
+ int addr_family2 = AF_UNSPEC;
+ NLTstIPAddr addr;
+ int plen = -1;
+ _nl_auto_free char *addr_free = NULL;
+ const char *s_addr_pattern;
+ const char *s_addr = NULL;
+ const char *s;
+ const char *s1;
+
+ ck_assert_ptr_nonnull(str);
+ _nltst_assert_select_route(out_select_route);
+
+ tokens0 = _nltst_strtokv(str);
+ tokens = (const char *const *)tokens0;
+
+ s = tokens[0];
+ if (!s)
+ ck_abort_msg("invalid empty route pattern \"%s\"", str);
+ if (_nl_streq(s, "4") || _nl_streq(s, "inet") ||
+ _nl_streq(s, "inet4")) {
+ addr_family = AF_INET;
+ tokens++;
+ } else if (_nl_streq(s, "6") || _nl_streq(s, "inet6")) {
+ addr_family = AF_INET6;
+ tokens++;
+ }
+
+ s_addr_pattern = tokens[0];
+ if (!s_addr_pattern) {
+ ck_abort_msg(
+ "the route pattern \"%s\" is invalid and contains no destination address",
+ str);
+ }
+ tokens++;
+
+ s = strchr(s_addr_pattern, '/');
+ if (s) {
+ long int plen2;
+
+ if (s == s_addr_pattern) {
+ ck_abort_msg(
+ "the route pattern \"%s\" contains no valid destination address",
+ str);
+ }
+ addr_free = strndup(s_addr_pattern, s - s_addr_pattern);
+ s_addr_pattern = addr_free;
+ s++;
+
+ errno = 0;
+ plen2 = strtol(s, (char **)&s1, 10);
+ if (errno != 0 || s1[0] != '\0' || plen2 < 0 || plen2 > 128 ||
+ ((_nltst_str_find_first_not_from_charset(
+ s, "0123456789"))[0] != '\0')) {
+ ck_abort_msg(
+ "the route pattern \"%s\" contains no valid destination address",
+ str);
+ }
+ plen = plen2;
+ }
+ if ((_nltst_str_find_first_not_from_charset(
+ s_addr_pattern, "abcdefABCDEF0123456789:.?*"))[0] != '\0') {
+ ck_abort_msg(
+ "the route pattern \"%s\" contains no valid destination address",
+ str);
+ }
+ if (_nltst_inet_pton(addr_family, s_addr_pattern, &addr_family2,
+ &addr)) {
+ free(addr_free);
+ addr_free = _nltst_inet_ntop_dup(addr_family2, &addr);
+ s_addr_pattern = addr_free;
+ addr_family = addr_family2;
+ } else {
+ if (addr_family == AF_UNSPEC) {
+ ck_abort_msg(
+ "the route pattern \"%s\" contains a wild card address, it requires the address family",
+ str);
+ }
+ }
+
+ ck_assert(addr_family == AF_INET || addr_family == AF_INET6);
+
+ if (plen > (addr_family == AF_INET ? 32 : 128)) {
+ ck_abort_msg(
+ "the route pattern \"%s\" contains no valid destination address (prefix length too large)",
+ str);
+ }
+ ck_assert_int_ge(plen, -1);
+
+ s = tokens[0];
+ if (s) {
+ ck_abort_msg("the route pattern \"%s\" contains extra tokens",
+ str);
+ }
+
+ if (_nltst_inet_valid(addr_family, s_addr_pattern))
+ _NL_SWAP(&s_addr, &s_addr_pattern);
+
+ _nltst_select_route_clear(out_select_route);
+ memset(out_select_route, 0, sizeof(*out_select_route));
+ *out_select_route = (NLTstSelectRoute){
+ .addr_family = addr_family,
+ .plen = plen,
+ .ifindex = 0,
+ .addr = s_addr ? strdup(s_addr) : NULL,
+ .addr_pattern = s_addr_pattern ? strdup(s_addr_pattern) : NULL,
+ };
+ ck_assert(!s_addr || out_select_route->addr);
+ ck_assert(!s_addr_pattern || out_select_route->addr_pattern);
+ _nltst_assert_select_route(out_select_route);
+}
+
+bool _nltst_select_route_match(struct nl_object *route,
+ const NLTstSelectRoute *select_route,
+ bool do_assert)
+{
+ struct nl_addr *addr;
+ struct rtnl_route *route_;
+ int i;
+ char sbuf1[200];
+
+ ck_assert_ptr_nonnull(route);
+ ck_assert_str_eq(nl_object_get_type(route), "route/route");
+
+ if (!select_route)
+ return true;
+
+ route_ = (struct rtnl_route *)route;
+
+ _nltst_assert_select_route(select_route);
+
+#define _check(cond, msg, ...) \
+ do { \
+ if (do_assert) { \
+ _nl_auto_free char *s1 = NULL; \
+ _nl_auto_free char *s2 = NULL; \
+ \
+ ck_assert_msg( \
+ (cond), \
+ "Checking condition \"%s\" for route \"%s\" (expected \"%s\") failed (msg: " msg \
+ ")", \
+ #cond, (s1 = _nltst_object_to_string(route)), \
+ (s2 = _nltst_select_route_to_string( \
+ select_route)), \
+ ##__VA_ARGS__); \
+ } else if (cond) { \
+ } else { \
+ return false; \
+ } \
+ } while (0)
+
+ if (select_route->addr_family != AF_UNSPEC) {
+ _check(rtnl_route_get_family(route_) ==
+ select_route->addr_family,
+ "mismatching address family");
+ }
+
+ if (select_route->ifindex != 0) {
+ struct nl_list_head *list;
+ struct rtnl_nexthop *nh;
+ size_t n;
+ struct rtnl_nexthop *nh2;
+
+ list = rtnl_route_get_nexthops(route_);
+ _check(list, "no nexthops for ifindex");
+
+ n = 0;
+ nl_list_for_each_entry(nh, list, rtnh_list) {
+ nh2 = nh;
+ n++;
+ }
+ _check(n == 1, "expects one nexthop for ifindex but got %zu",
+ n);
+ ck_assert_ptr_nonnull(nh2);
+
+ i = rtnl_route_nh_get_ifindex(nh2);
+ _check(i == select_route->ifindex,
+ "route has unexpected ifindex %d for next hop", i);
+ }
+
+ addr = rtnl_route_get_dst(route_);
+
+ if (addr) {
+ if (select_route->addr_family != AF_UNSPEC) {
+ _check(nl_addr_get_family(addr) ==
+ select_route->addr_family,
+ "unexecpted address family of dst");
+ }
+ }
+
+ if (select_route->plen != -1) {
+ _check(addr, "missing address");
+ _check(nl_addr_get_prefixlen(addr) == select_route->plen,
+ "unexpected prefix length");
+ }
+ if (select_route->addr || select_route->addr_pattern) {
+ _check(addr, "missing address");
+
+ _nl_inet_ntop(nl_addr_get_family(addr),
+ nl_addr_get_binary_addr(addr), sbuf1);
+
+ ck_assert(strlen(sbuf1) > 0);
+ ck_assert(strlen(sbuf1) < sizeof(sbuf1));
+
+ if (select_route->addr) {
+ _check(_nl_streq(sbuf1, select_route->addr),
+ "unexpected address, \"%s\" does not match \"%s\"",
+ sbuf1, select_route->addr);
+ }
+ if (select_route->addr_pattern) {
+ _check(fnmatch(select_route->addr_pattern, sbuf1, 0) ==
+ 0,
+ "unexpected address, \"%s\" does not match pattern \"%s\"",
+ sbuf1, select_route->addr_pattern);
+ }
+ }
+
+#undef _check
+
+ return false;
+}
diff --git a/tests/nl-test-util.h b/tests/nl-test-util.h
index 0d37d35..f843072 100644
--- a/tests/nl-test-util.h
+++ b/tests/nl-test-util.h
@@ -417,6 +417,70 @@ bool _nltst_skip_no_iproute2(const char *msg);
/*****************************************************************************/
+typedef struct {
+ int addr_family;
+ int ifindex;
+ int plen;
+ char *addr;
+ char *addr_pattern;
+} NLTstSelectRoute;
+
+#define _nltst_assert_select_route(select_route) \
+ do { \
+ const NLTstSelectRoute *_select_route_5 = (select_route); \
+ \
+ ck_assert_ptr_nonnull(_select_route_5); \
+ _nl_assert_addr_family_or_unspec( \
+ _select_route_5->addr_family); \
+ ck_assert_int_ge(_select_route_5->ifindex, 0); \
+ ck_assert_int_ge(_select_route_5->plen, -1); \
+ ck_assert_int_le( \
+ _select_route_5->plen, \
+ _select_route_5->addr_family == AF_INET ? 32 : 128); \
+ ck_assert(!_select_route_5->addr || ({ \
+ char _buf[INET6_ADDRSTRLEN]; \
+ const char *_s2; \
+ \
+ _s2 = _nltst_inet_normalize( \
+ _select_route_5->addr_family, \
+ _select_route_5->addr, _buf); \
+ (_select_route_5->addr_family != AF_UNSPEC && _s2 && \
+ _nl_streq(_s2, _select_route_5->addr)); \
+ })); \
+ ck_assert(!_select_route_5->addr_pattern || \
+ !_select_route_5->addr); \
+ ck_assert(!_select_route_5->addr_pattern || \
+ _select_route_5->addr_family != AF_UNSPEC); \
+ } while (0)
+
+void _nltst_select_route_clear(NLTstSelectRoute *select_route);
+
+#define _nltst_auto_clear_select_route \
+ _nl_auto(_nltst_auto_clear_select_route_fcn)
+_NL_AUTO_DEFINE_FCN_STRUCT(NLTstSelectRoute, _nltst_auto_clear_select_route_fcn,
+ _nltst_select_route_clear);
+
+int _nltst_select_route_cmp(const NLTstSelectRoute *select_route1,
+ const NLTstSelectRoute *select_route2);
+
+static inline bool
+_nltst_select_route_equal(const NLTstSelectRoute *select_route1,
+ const NLTstSelectRoute *select_route2)
+{
+ return _nltst_select_route_cmp(select_route1, select_route2) == 0;
+}
+
+char *_nltst_select_route_to_string(const NLTstSelectRoute *select_route);
+
+void _nltst_select_route_parse(const char *str,
+ NLTstSelectRoute *out_select_route);
+
+bool _nltst_select_route_match(struct nl_object *route,
+ const NLTstSelectRoute *select_route,
+ bool do_assert);
+
+/*****************************************************************************/
+
void _nltst_object_identical(const void *a, const void *b);
char *_nltst_object_to_string(const struct nl_object *obj);