From f1dcf95d9b74282db7978589962f8f46af14a881 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 6 May 2022 12:47:41 +0200 Subject: [PATCH] m3 --- tests/cksuite-all-attr.c | 53 +++++++ tests/cksuite-all-netns.c | 28 +++- tests/nl-test-util.c | 294 +++++++++++++++++++++++++++++++++++++- tests/nl-test-util.h | 112 ++++++++++++++- 4 files changed, 483 insertions(+), 4 deletions(-) diff --git a/tests/cksuite-all-attr.c b/tests/cksuite-all-attr.c index b4ac61ff..fa6f9ff9 100644 --- a/tests/cksuite-all-attr.c +++ b/tests/cksuite-all-attr.c @@ -144,6 +144,58 @@ END_TEST /*****************************************************************************/ +START_TEST(test_nltst_select_route_parse) +{ + /* This is a unit test for testing the unit-test helper function + * _nltst_select_route_parse(). */ + +#define _check_erp(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_erp("0.0.0.0", AF_INET, "0.0.0.0", -1); + _check_erp("4 0.0.0.0/0", AF_INET, "0.0.0.0", 0); + _check_erp(" 6\n 0:0::/0", AF_INET6, "::", 0); + _check_erp(" \n 0:0::/100", AF_INET6, "::", 100); + _check_erp("6 0:0::*/0 ", AF_INET6, "0:0::*", 0); + _check_erp("6 0:0::*/128 ", AF_INET6, "0:0::*", 128); + _check_erp("6 0:0::* ", AF_INET6, "0:0::*", -1); +} + +/*****************************************************************************/ + Suite *make_nl_attr_suite(void) { Suite *suite = suite_create("Netlink attributes"); @@ -153,6 +205,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_parse); suite_add_tcase(suite, tc); return suite; diff --git a/tests/cksuite-all-netns.c b/tests/cksuite-all-netns.c index c6a5ce23..0264e132 100644 --- a/tests/cksuite-all-netns.c +++ b/tests/cksuite-all-netns.c @@ -302,6 +302,32 @@ END_TEST /*****************************************************************************/ +static void _route_init(int addr_family, struct nl_sock **sk, + struct nl_cache **cache) +{ + ck_assert(sk && !*sk); + ck_assert(cache && !*cache); + + *sk = _nltst_socket(NETLINK_ROUTE); + *cache = _nltst_rtnl_route_alloc_cache(*sk, addr_family); +} + +START_TEST(route_1) +{ + _nl_auto_nl_socket struct nl_sock *sk = NULL; + _nl_auto_nl_cache struct nl_cache *cache = NULL; + + _nltst_add_link(NULL, "v1", "dummy", NULL); + _nltst_system("ip -d link set v1 up"); + + _route_init(AF_INET6, &sk, &cache); + + _nltst_assert_route_cache(cache, "fe80::/64", "::", "::"); +} +END_TEST + +/*****************************************************************************/ + Suite *make_nl_netns_suite(void) { Suite *suite = suite_create("netns"); @@ -311,7 +337,7 @@ Suite *make_nl_netns_suite(void) nltst_netns_fixture_teardown); tcase_add_test(tc, cache_and_clone); tcase_add_loop_test(tc, test_create_iface, 0, 17); - + tcase_add_test(tc, route_1); suite_add_tcase(suite, tc); return suite; diff --git a/tests/nl-test-util.c b/tests/nl-test-util.c index 52188a00..c20a4282 100644 --- a/tests/nl-test-util.c +++ b/tests/nl-test-util.c @@ -217,7 +217,7 @@ void _nltst_object_identical(const void *a, const void *b) /*****************************************************************************/ -char *_nltst_object_to_string(struct nl_object *obj) +char *_nltst_object_to_string(const struct nl_object *obj) { size_t L = 1024; size_t l; @@ -229,7 +229,7 @@ char *_nltst_object_to_string(struct nl_object *obj) s = malloc(L); ck_assert_ptr_nonnull(s); - nl_object_dump_buf(obj, s, L); + nl_object_dump_buf((struct nl_object *)obj, s, L); l = strlen(s); ck_assert_int_lt(l, L); s = realloc(s, l + 1); @@ -653,3 +653,293 @@ void _nltst_assert_link_exists_full(const char *ifname, bool exists) /* FIXME: we would expect that the cloned object is identical. It is not. */ /* _nltst_object_identical(link, link_clone); */ } + +/*****************************************************************************/ + +int _nltst_select_route_cmp(const NLTstSelectRoute *select_route1, + const NLTstSelectRoute *select_route2) +{ + NLTstSelectRoute r2; + + _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); + + r2 = *select_route2; + r2.addr = select_route1->addr; + r2.addr_pattern = select_route1->addr_pattern; + ck_assert_mem_eq(select_route1, &r2, sizeof(NLTstSelectRoute)); + + 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); +} + +void _nltst_assert_route_list(struct nl_object *const *objs, ssize_t len, + const char *const *expected_routes) +{ + size_t l; + size_t i; + + if (len < 0) { + l = 0; + if (objs) { + while (objs[l]) + l++; + } + } else + l = len; + + for (i = 0; i < l; i++) { + struct nl_object *route = objs[i]; + _nltst_auto_clear_select_route NLTstSelectRoute select_route = { + 0 + }; + _nl_auto_free char *s = _nltst_object_to_string(route); + + if (!expected_routes[i]) { + ck_abort_msg( + "No more expected route, but have route %zu (of %zu) as %s", + i + 1, l, s); + } + + _nltst_select_route_parse(expected_routes[i], &select_route); + + _nltst_select_route(route, &select_route, true); + } +} + +void _nltst_assert_route_cache_v(struct nl_cache *cache, + const char *const *expected_routes) +{ + _nl_auto_free struct nl_object **objs = NULL; + size_t len; + + ck_assert(cache); + ck_assert(expected_routes); + + objs = _nltst_cache_get_all(cache, &len); + + _nltst_assert_route_list(objs, len, expected_routes); +} + +/*****************************************************************************/ + +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); +} + +bool _nltst_select_route(struct nl_object *route, + const NLTstSelectRoute *select_route, bool do_assert) +{ + struct rtnl_route *route_; + + 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 _assert(cond, do_assert, route, select_route, msg, ...) \ + do { \ + const struct nl_object *const _route = (route); \ + const NLTstSelectRoute *const _select_route = (select_route); \ + const bool _do_assert = (do_assert); \ + \ + 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->ifindex != 0) { + struct nl_list_head *list; + struct rtnl_nexthop *nh; + size_t n; + struct rtnl_nexthop *nh2; + + list = rtnl_route_get_nexthops(route_); + _assert(list, do_assert, route, select_route, + "no nexthops for ifindex"); + + n = 0; + nl_list_for_each_entry(nh, list, rtnl_list) { + nh2 = nh; + n++; + } + _assert(n == 1, do_assert, route, select_route, + "expects one nexthop for ifindex but got %zu", n); + } + + return false; +} diff --git a/tests/nl-test-util.h b/tests/nl-test-util.h index 4d95a62c..e5ab30f3 100644 --- a/tests/nl-test-util.h +++ b/tests/nl-test-util.h @@ -51,6 +51,41 @@ _NL_AUTO_DEFINE_FCN_TYPED0(char **, _nltst_auto_strfreev_fcn, _nltst_strfreev); /*****************************************************************************/ +static inline char *_nltst_strdup(const char *str) +{ + char *s2; + + if (!str) + return NULL; + s2 = strdup(str); + ck_assert_ptr_nonnull(s2); + return s2; +} + +/*****************************************************************************/ + +#define __nltst_sprintf_arr(uniq, arr, fmt, ...) \ + ({ \ + char *const _NL_UNIQ_T(arr, uniq) = (arr); \ + int _NL_UNIQ_T(c, uniq); \ + \ + _NL_STATIC_ASSERT(sizeof(arr) > \ + sizeof(_NL_UNIQ_T(arr, uniq))); \ + \ + _NL_UNIQ_T(c, uniq) = snprintf(_NL_UNIQ_T(arr, uniq), \ + sizeof(arr), fmt, \ + ##__VA_ARGS__); \ + \ + ck_assert_int_lt(_NL_UNIQ_T(c, uniq), sizeof(arr)); \ + \ + _NL_UNIQ_T(arr, uniq); \ + }) + +#define _nltst_sprintf_arr(arr, fmt, ...) \ + __nltst_sprintf_arr(_NL_UNIQ, arr, fmt, ##__VA_ARGS__) + +/*****************************************************************************/ + void _nltst_get_urandom(void *ptr, size_t len); uint32_t _nltst_rand_u32(void); @@ -368,9 +403,74 @@ void nltst_netns_leave(struct nltst_netns *nsdata); /*****************************************************************************/ +#define _nltst_system(command) _nltst_assert_retcode(system(command)) + +/*****************************************************************************/ + +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; +} + +bool _nltst_select_route(struct nl_object *route, + const NLTstSelectRoute *select_route, bool do_assert); + +char *_nltst_select_route_to_string(const NLTstSelectRoute *select_route); + +void _nltst_select_route_parse(const char *str, + NLTstSelectRoute *out_select_route); + void _nltst_object_identical(const void *a, const void *b); -char *_nltst_object_to_string(struct nl_object *obj); +char *_nltst_object_to_string(const struct nl_object *obj); struct nl_object **_nltst_cache_get_all(struct nl_cache *cache, size_t *out_len); @@ -394,4 +494,14 @@ 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); +void _nltst_assert_route_list(struct nl_object *const *objs, ssize_t len, + const char *const *expected_routes); + +void _nltst_assert_route_cache_v(struct nl_cache *cache, + const char *const *expected_routes); + +#define _nltst_assert_route_cache(cache, ...) \ + _nltst_assert_route_cache_v(cache, \ + ((const char *const[200]){ __VA_ARGS__ })) + #endif /* __NL_TEST_UTIL_H__ */