diff options
author | Thomas Haller <thaller@redhat.com> | 2024-05-17 14:54:50 (GMT) |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2024-05-17 17:03:15 (GMT) |
commit | 2ebbc0342ea49df16f801eb11703ec9f868d2f97 (patch) | |
tree | 609ceff06862fd034c3ee1cd5fa83562453da56a | |
parent | d784f2cb42bde01f27feacebdfaf51033ae82f02 (diff) | |
download | libnl-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.c | 55 | ||||
-rw-r--r-- | tests/nl-test-util.c | 282 | ||||
-rw-r--r-- | tests/nl-test-util.h | 64 |
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); |