From d8a3e44a5cf8d61e0e60f4d2b84ab678c628cb89 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 19 Jan 2024 19:35:57 +0900 Subject: [PATCH 01/13] test-local-addresses: several modernization - use size_t for number of addresses, - use FOREACH_ARRAY() macro, - use IN_ADDR_TO_STRING() macro, etc. --- src/test/test-local-addresses.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/test/test-local-addresses.c b/src/test/test-local-addresses.c index 5a02465f23..bad47bc088 100644 --- a/src/test/test-local-addresses.c +++ b/src/test/test-local-addresses.c @@ -8,13 +8,11 @@ #include "local-addresses.h" #include "tests.h" -static void print_local_addresses(struct local_address *a, unsigned n) { - for (unsigned i = 0; i < n; i++) { - _cleanup_free_ char *b = NULL; - - assert_se(in_addr_to_string(a[i].family, &a[i].address, &b) >= 0); - log_debug("%s if%i scope=%i metric=%u address=%s", af_to_name(a[i].family), a[i].ifindex, a[i].scope, a[i].metric, b); - } +static void print_local_addresses(const struct local_address *a, size_t n) { + FOREACH_ARRAY(i, a, n) + log_debug("%s ifindex=%i scope=%u metric=%"PRIu32" address=%s", + af_to_name(i->family), i->ifindex, i->scope, i->metric, + IN_ADDR_TO_STRING(i->family, &i->address)); } TEST(local_addresses) { @@ -24,49 +22,49 @@ TEST(local_addresses) { n = local_addresses(NULL, 0, AF_INET, &a); assert_se(n >= 0); log_debug("/* Local Addresses(ifindex:0, AF_INET) */"); - print_local_addresses(a, (unsigned) n); + print_local_addresses(a, n); a = mfree(a); n = local_addresses(NULL, 0, AF_INET6, &a); assert_se(n >= 0); log_debug("/* Local Addresses(ifindex:0, AF_INET6) */"); - print_local_addresses(a, (unsigned) n); + print_local_addresses(a, n); a = mfree(a); n = local_addresses(NULL, 0, AF_UNSPEC, &a); assert_se(n >= 0); log_debug("/* Local Addresses(ifindex:0, AF_UNSPEC) */"); - print_local_addresses(a, (unsigned) n); + print_local_addresses(a, n); a = mfree(a); n = local_addresses(NULL, 1, AF_INET, &a); assert_se(n >= 0); log_debug("/* Local Addresses(ifindex:1, AF_INET) */"); - print_local_addresses(a, (unsigned) n); + print_local_addresses(a, n); a = mfree(a); n = local_addresses(NULL, 1, AF_INET6, &a); assert_se(n >= 0); log_debug("/* Local Addresses(ifindex:1, AF_INET6) */"); - print_local_addresses(a, (unsigned) n); + print_local_addresses(a, n); a = mfree(a); n = local_addresses(NULL, 1, AF_UNSPEC, &a); assert_se(n >= 0); log_debug("/* Local Addresses(ifindex:1, AF_UNSPEC) */"); - print_local_addresses(a, (unsigned) n); + print_local_addresses(a, n); a = mfree(a); n = local_gateways(NULL, 0, AF_UNSPEC, &a); assert_se(n >= 0); log_debug("/* Local Gateways */"); - print_local_addresses(a, (unsigned) n); + print_local_addresses(a, n); a = mfree(a); n = local_outbounds(NULL, 0, AF_UNSPEC, &a); assert_se(n >= 0); log_debug("/* Local Outbounds */"); - print_local_addresses(a, (unsigned) n); + print_local_addresses(a, n); free(a); } From 37359b1c8181efa98a2bd8a07f3d55c0db42a3c3 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 19 Jan 2024 19:24:45 +0900 Subject: [PATCH 02/13] local-addresses: rename metric -> priority To make it consistent with the netlink attribute RTA_PRIORITY. --- src/shared/local-addresses.c | 16 ++++++++-------- src/shared/local-addresses.h | 5 +++-- src/test/test-local-addresses.c | 4 ++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/shared/local-addresses.c b/src/shared/local-addresses.c index a1577de0df..216f5388c7 100644 --- a/src/shared/local-addresses.c +++ b/src/shared/local-addresses.c @@ -25,7 +25,7 @@ static int address_compare(const struct local_address *a, const struct local_add if (r != 0) return r; - r = CMP(a->metric, b->metric); + r = CMP(a->priority, b->priority); if (r != 0) return r; @@ -180,7 +180,7 @@ static int add_local_gateway( size_t *n_list, int af, int ifindex, - uint32_t metric, + uint32_t priority, const RouteVia *via) { assert(list); @@ -195,7 +195,7 @@ static int add_local_gateway( (*list)[(*n_list)++] = (struct local_address) { .ifindex = ifindex, - .metric = metric, + .priority = priority, .family = via->family, .address = via->address, }; @@ -249,7 +249,7 @@ int local_gateways( union in_addr_union gateway; uint16_t type; unsigned char dst_len, src_len, table; - uint32_t ifi = 0, metric = 0; + uint32_t ifi = 0, priority = 0; size_t rta_len; int family; RouteVia via; @@ -283,7 +283,7 @@ int local_gateways( if (table != RT_TABLE_MAIN) continue; - r = sd_netlink_message_read_u32(m, RTA_PRIORITY, &metric); + r = sd_netlink_message_read_u32(m, RTA_PRIORITY, &priority); if (r < 0 && r != -ENODATA) return r; @@ -308,7 +308,7 @@ int local_gateways( if (r >= 0) { via.family = family; via.address = gateway; - r = add_local_gateway(&list, &n_list, af, ifi, metric, &via); + r = add_local_gateway(&list, &n_list, af, ifi, priority, &via); if (r < 0) return r; @@ -322,7 +322,7 @@ int local_gateways( if (r < 0 && r != -ENODATA) return r; if (r >= 0) { - r = add_local_gateway(&list, &n_list, af, ifi, metric, &via); + r = add_local_gateway(&list, &n_list, af, ifi, priority, &via); if (r < 0) return r; @@ -344,7 +344,7 @@ int local_gateways( if (ifindex > 0 && mr->ifindex != ifindex) continue; - r = add_local_gateway(&list, &n_list, af, ifi, metric, &mr->gateway); + r = add_local_gateway(&list, &n_list, af, ifi, priority, &mr->gateway); if (r < 0) return r; } diff --git a/src/shared/local-addresses.h b/src/shared/local-addresses.h index 38a17d233e..42bed2ba0d 100644 --- a/src/shared/local-addresses.h +++ b/src/shared/local-addresses.h @@ -6,9 +6,10 @@ #include "in-addr-util.h" struct local_address { - int family, ifindex; + int ifindex; unsigned char scope; - uint32_t metric; + uint32_t priority; + int family; union in_addr_union address; }; diff --git a/src/test/test-local-addresses.c b/src/test/test-local-addresses.c index bad47bc088..c9ed7bf77d 100644 --- a/src/test/test-local-addresses.c +++ b/src/test/test-local-addresses.c @@ -10,8 +10,8 @@ static void print_local_addresses(const struct local_address *a, size_t n) { FOREACH_ARRAY(i, a, n) - log_debug("%s ifindex=%i scope=%u metric=%"PRIu32" address=%s", - af_to_name(i->family), i->ifindex, i->scope, i->metric, + log_debug("%s ifindex=%i scope=%u priority=%"PRIu32" address=%s", + af_to_name(i->family), i->ifindex, i->scope, i->priority, IN_ADDR_TO_STRING(i->family, &i->address)); } From 5cb56068d0aae4c85e97a4ae16e33a7614cdc0bb Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 19 Jan 2024 19:43:26 +0900 Subject: [PATCH 03/13] local-addresses: check family more Just for safety. No functional change, unless the kernel sends broken messages. --- src/shared/local-addresses.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/shared/local-addresses.c b/src/shared/local-addresses.c index 216f5388c7..48777e4c47 100644 --- a/src/shared/local-addresses.c +++ b/src/shared/local-addresses.c @@ -115,6 +115,8 @@ int local_addresses( r = sd_rtnl_message_addr_get_family(m, &family); if (r < 0) return r; + if (!IN_SET(family, AF_INET, AF_INET6)) + continue; if (af != AF_UNSPEC && af != family) continue; @@ -215,6 +217,12 @@ int local_gateways( size_t n_list = 0; int r; + /* The RTA_VIA attribute is used only for IPv4 routes with an IPv6 gateway. If IPv4 gateways are + * requested (af == AF_INET), then we do not return IPv6 gateway addresses. Similary, if IPv6 + * gateways are requested (af == AF_INET6), then we do not return gateway addresses for IPv4 routes. + * So, the RTA_VIA attribute is only parsed when af == AF_UNSPEC. */ + bool allow_via = af == AF_UNSPEC; + if (context) rtnl = sd_netlink_ref(context); else { @@ -292,6 +300,8 @@ int local_gateways( return r; if (!IN_SET(family, AF_INET, AF_INET6)) continue; + if (af != AF_UNSPEC && af != family) + continue; r = sd_netlink_message_read_u32(m, RTA_OIF, &ifi); if (r < 0 && r != -ENODATA) @@ -315,6 +325,9 @@ int local_gateways( continue; } + if (!allow_via) + continue; + if (family != AF_INET) continue; @@ -322,6 +335,9 @@ int local_gateways( if (r < 0 && r != -ENODATA) return r; if (r >= 0) { + if (via.family != AF_INET6) + return -EBADMSG; + r = add_local_gateway(&list, &n_list, af, ifi, priority, &via); if (r < 0) return r; @@ -344,6 +360,9 @@ int local_gateways( if (ifindex > 0 && mr->ifindex != ifindex) continue; + if (!allow_via && family != mr->gateway.family) + continue; + r = add_local_gateway(&list, &n_list, af, ifi, priority, &mr->gateway); if (r < 0) return r; From e90863f231b23834a8af7ae0b2e814252badd4c8 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 19 Jan 2024 19:44:49 +0900 Subject: [PATCH 04/13] local-addresses: ignore tentative addresses As tentative addresses may be dropped soon if DAD failed. --- src/shared/local-addresses.c | 2 +- test/units/testsuite-13.nss-mymachines.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/shared/local-addresses.c b/src/shared/local-addresses.c index 48777e4c47..43f0a2d89a 100644 --- a/src/shared/local-addresses.c +++ b/src/shared/local-addresses.c @@ -123,7 +123,7 @@ int local_addresses( r = sd_rtnl_message_addr_get_flags(m, &flags); if (r < 0) return r; - if (flags & IFA_F_DEPRECATED) + if ((flags & (IFA_F_DEPRECATED|IFA_F_TENTATIVE)) != 0) continue; if (!GREEDY_REALLOC0(list, n_list+1)) diff --git a/test/units/testsuite-13.nss-mymachines.sh b/test/units/testsuite-13.nss-mymachines.sh index b566c7343d..931b93f953 100755 --- a/test/units/testsuite-13.nss-mymachines.sh +++ b/test/units/testsuite-13.nss-mymachines.sh @@ -56,7 +56,7 @@ ip addr add 10.2.0.2/24 dev ve-manyips for i in {100..120}; do ip addr add 10.2.0.$i/24 dev ve-manyips done -ip addr add fd00:dead:beef:cafe::2/64 dev ve-manyips +ip addr add fd00:dead:beef:cafe::2/64 dev ve-manyips nodad ip addr show dev ve-manyips touch /initialized sleep infinity @@ -90,7 +90,7 @@ done # getaddrinfo() return EAI_NONAME without ever asking nss-mymachines. ip addr add 10.1.0.1/24 dev ve-singleip ip addr add 10.2.0.1/24 dev ve-manyips -ip addr add fd00:dead:beef:cafe::1/64 dev ve-manyips +ip addr add fd00:dead:beef:cafe::1/64 dev ve-manyips nodad getent hosts -s mymachines getent ahosts -s mymachines From a64f60416cda01e09308955e5be1c013dbd29314 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 19 Jan 2024 19:47:29 +0900 Subject: [PATCH 05/13] local-addresses: always sort and dedup even if addresses are not requested Otherwise, the return value may different when ret is NULL or not. --- src/shared/local-addresses.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/shared/local-addresses.c b/src/shared/local-addresses.c index 43f0a2d89a..c8feb58407 100644 --- a/src/shared/local-addresses.c +++ b/src/shared/local-addresses.c @@ -168,11 +168,11 @@ int local_addresses( n_list++; }; - if (ret) { - typesafe_qsort(list, n_list, address_compare); - suppress_duplicates(list, &n_list); + typesafe_qsort(list, n_list, address_compare); + suppress_duplicates(list, &n_list); + + if (ret) *ret = TAKE_PTR(list); - } return (int) n_list; } @@ -370,11 +370,11 @@ int local_gateways( } } - if (ret) { - typesafe_qsort(list, n_list, address_compare); - suppress_duplicates(list, &n_list); + typesafe_qsort(list, n_list, address_compare); + suppress_duplicates(list, &n_list); + + if (ret) *ret = TAKE_PTR(list); - } return (int) n_list; } @@ -515,11 +515,11 @@ int local_outbounds( } } - if (ret) { - typesafe_qsort(list, n_list, address_compare); - suppress_duplicates(list, &n_list); + typesafe_qsort(list, n_list, address_compare); + suppress_duplicates(list, &n_list); + + if (ret) *ret = TAKE_PTR(list); - } return (int) n_list; } From 4019bec852a3e16a4a20008995d0c5f9265a04a7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 19 Jan 2024 20:04:01 +0900 Subject: [PATCH 06/13] local-addresses: fix memleak of 'multipath_routes' Also reduces scopes of some variables. --- src/shared/local-addresses.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/shared/local-addresses.c b/src/shared/local-addresses.c index c8feb58407..6fc54b7bab 100644 --- a/src/shared/local-addresses.c +++ b/src/shared/local-addresses.c @@ -252,15 +252,10 @@ int local_gateways( return r; for (sd_netlink_message *m = reply; m; m = sd_netlink_message_next(m)) { - _cleanup_ordered_set_free_free_ OrderedSet *multipath_routes = NULL; - _cleanup_free_ void *rta_multipath = NULL; - union in_addr_union gateway; uint16_t type; unsigned char dst_len, src_len, table; uint32_t ifi = 0, priority = 0; - size_t rta_len; int family; - RouteVia via; r = sd_netlink_message_get_errno(m); if (r < 0) @@ -312,6 +307,7 @@ int local_gateways( if (ifindex > 0 && (int) ifi != ifindex) continue; + union in_addr_union gateway; r = netlink_message_read_in_addr_union(m, RTA_GATEWAY, family, &gateway); if (r < 0 && r != -ENODATA) return r; @@ -331,6 +327,7 @@ int local_gateways( if (family != AF_INET) continue; + RouteVia via; r = sd_netlink_message_read(m, RTA_VIA, sizeof(via), &via); if (r < 0 && r != -ENODATA) return r; @@ -346,10 +343,13 @@ int local_gateways( } } + size_t rta_len; + _cleanup_free_ void *rta_multipath = NULL; r = sd_netlink_message_read_data(m, RTA_MULTIPATH, &rta_len, &rta_multipath); if (r < 0 && r != -ENODATA) return r; if (r >= 0) { + _cleanup_ordered_set_free_free_ OrderedSet *multipath_routes = NULL; MultipathRoute *mr; r = rtattr_read_nexthop(rta_multipath, rta_len, family, &multipath_routes); From 1305fe4ecfa858ef4ecd374db863407e8e95491e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 19 Jan 2024 20:07:17 +0900 Subject: [PATCH 07/13] local-addresses: RTA_OIF and RTA_MULTIPATH are exclusive --- src/shared/local-addresses.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/shared/local-addresses.c b/src/shared/local-addresses.c index 6fc54b7bab..0c27814b61 100644 --- a/src/shared/local-addresses.c +++ b/src/shared/local-addresses.c @@ -338,9 +338,10 @@ int local_gateways( r = add_local_gateway(&list, &n_list, af, ifi, priority, &via); if (r < 0) return r; - - continue; } + + /* If the route has RTA_OIF, it does not have RTA_MULTIPATH. */ + continue; } size_t rta_len; From 0b2c0c315934376c0d1639ee814ae1534690460d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 19 Jan 2024 20:08:06 +0900 Subject: [PATCH 08/13] local-addresses: introduce generic setter add_local_addresses_full() --- src/shared/local-addresses.c | 136 ++++++++++++++++++++--------------- 1 file changed, 77 insertions(+), 59 deletions(-) diff --git a/src/shared/local-addresses.c b/src/shared/local-addresses.c index 0c27814b61..55cff7479e 100644 --- a/src/shared/local-addresses.c +++ b/src/shared/local-addresses.c @@ -58,6 +58,46 @@ static void suppress_duplicates(struct local_address *list, size_t *n_list) { *n_list = new_size; } +static int add_local_address_full( + struct local_address **list, + size_t *n_list, + int ifindex, + unsigned char scope, + uint32_t priority, + int family, + const union in_addr_union *address) { + + assert(list); + assert(n_list); + assert(ifindex > 0); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(address); + + if (!GREEDY_REALLOC(*list, *n_list + 1)) + return -ENOMEM; + + (*list)[(*n_list)++] = (struct local_address) { + .ifindex = ifindex, + .scope = scope, + .priority = priority, + .family = family, + .address = *address, + }; + + return 1; +} + +static int add_local_address( + struct local_address **list, + size_t *n_list, + int ifindex, + unsigned char scope, + int family, + const union in_addr_union *address) { + + return add_local_address_full(list, n_list, ifindex, scope, 0, family, address); +} + int local_addresses( sd_netlink *context, int ifindex, @@ -91,8 +131,8 @@ int local_addresses( return r; for (sd_netlink_message *m = reply; m; m = sd_netlink_message_next(m)) { - struct local_address *a; - unsigned char flags; + union in_addr_union a; + unsigned char flags, scope; uint16_t type; int ifi, family; @@ -126,33 +166,28 @@ int local_addresses( if ((flags & (IFA_F_DEPRECATED|IFA_F_TENTATIVE)) != 0) continue; - if (!GREEDY_REALLOC0(list, n_list+1)) - return -ENOMEM; - - a = list + n_list; - - r = sd_rtnl_message_addr_get_scope(m, &a->scope); + r = sd_rtnl_message_addr_get_scope(m, &scope); if (r < 0) return r; - if (ifindex == 0 && IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE)) + if (ifindex == 0 && IN_SET(scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE)) continue; switch (family) { case AF_INET: - r = sd_netlink_message_read_in_addr(m, IFA_LOCAL, &a->address.in); + r = sd_netlink_message_read_in_addr(m, IFA_LOCAL, &a.in); if (r < 0) { - r = sd_netlink_message_read_in_addr(m, IFA_ADDRESS, &a->address.in); + r = sd_netlink_message_read_in_addr(m, IFA_ADDRESS, &a.in); if (r < 0) continue; } break; case AF_INET6: - r = sd_netlink_message_read_in6_addr(m, IFA_LOCAL, &a->address.in6); + r = sd_netlink_message_read_in6_addr(m, IFA_LOCAL, &a.in6); if (r < 0) { - r = sd_netlink_message_read_in6_addr(m, IFA_ADDRESS, &a->address.in6); + r = sd_netlink_message_read_in6_addr(m, IFA_ADDRESS, &a.in6); if (r < 0) continue; } @@ -162,10 +197,9 @@ int local_addresses( continue; } - a->ifindex = ifi; - a->family = family; - - n_list++; + r = add_local_address(&list, &n_list, ifi, scope, family, &a); + if (r < 0) + return r; }; typesafe_qsort(list, n_list, address_compare); @@ -180,29 +214,12 @@ int local_addresses( static int add_local_gateway( struct local_address **list, size_t *n_list, - int af, int ifindex, uint32_t priority, - const RouteVia *via) { + int family, + const union in_addr_union *address) { - assert(list); - assert(n_list); - assert(via); - - if (af != AF_UNSPEC && af != via->family) - return 0; - - if (!GREEDY_REALLOC(*list, *n_list + 1)) - return -ENOMEM; - - (*list)[(*n_list)++] = (struct local_address) { - .ifindex = ifindex, - .priority = priority, - .family = via->family, - .address = via->address, - }; - - return 0; + return add_local_address_full(list, n_list, ifindex, 0, priority, family, address); } int local_gateways( @@ -312,9 +329,7 @@ int local_gateways( if (r < 0 && r != -ENODATA) return r; if (r >= 0) { - via.family = family; - via.address = gateway; - r = add_local_gateway(&list, &n_list, af, ifi, priority, &via); + r = add_local_gateway(&list, &n_list, ifi, priority, family, &gateway); if (r < 0) return r; @@ -335,7 +350,8 @@ int local_gateways( if (via.family != AF_INET6) return -EBADMSG; - r = add_local_gateway(&list, &n_list, af, ifi, priority, &via); + r = add_local_gateway(&list, &n_list, ifi, priority, via.family, + &(union in_addr_union) { .in6 = via.address.in6 }); if (r < 0) return r; } @@ -364,7 +380,8 @@ int local_gateways( if (!allow_via && family != mr->gateway.family) continue; - r = add_local_gateway(&list, &n_list, af, ifi, priority, &mr->gateway); + union in_addr_union a = mr->gateway.address; + r = add_local_gateway(&list, &n_list, ifi, priority, mr->gateway.family, &a); if (r < 0) return r; } @@ -380,6 +397,16 @@ int local_gateways( return (int) n_list; } +static int add_local_outbound( + struct local_address **list, + size_t *n_list, + int ifindex, + int family, + const union in_addr_union *address) { + + return add_local_address_full(list, n_list, ifindex, 0, 0, family, address); +} + int local_outbounds( sd_netlink *context, int ifindex, @@ -486,29 +513,20 @@ int local_outbounds( if (in4_addr_is_null(&sa.in.sin_addr)) /* Auto-binding didn't work. :-( */ continue; - if (!GREEDY_REALLOC(list, n_list+1)) - return -ENOMEM; - - list[n_list++] = (struct local_address) { - .family = gateways[i].family, - .ifindex = gateways[i].ifindex, - .address.in = sa.in.sin_addr, - }; - + r = add_local_outbound(&list, &n_list, gateways[i].ifindex, gateways[i].family, + &(union in_addr_union) { .in = sa.in.sin_addr }); + if (r < 0) + return r; break; case AF_INET6: if (in6_addr_is_null(&sa.in6.sin6_addr)) continue; - if (!GREEDY_REALLOC(list, n_list+1)) - return -ENOMEM; - - list[n_list++] = (struct local_address) { - .family = gateways[i].family, - .ifindex = gateways[i].ifindex, - .address.in6 = sa.in6.sin6_addr, - }; + r = add_local_outbound(&list, &n_list, gateways[i].ifindex, gateways[i].family, + &(union in_addr_union) { .in6 = sa.in6.sin6_addr }); + if (r < 0) + return r; break; default: From eb1f9ed6a0f89c885904e9060bc44ad3049fd239 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 19 Jan 2024 20:12:09 +0900 Subject: [PATCH 09/13] local-addresses: also save weight of multipath routes --- src/shared/local-addresses.c | 19 +++++++++++++------ src/shared/local-addresses.h | 1 + src/test/test-local-addresses.c | 4 ++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/shared/local-addresses.c b/src/shared/local-addresses.c index 55cff7479e..a67a2bfd80 100644 --- a/src/shared/local-addresses.c +++ b/src/shared/local-addresses.c @@ -29,6 +29,10 @@ static int address_compare(const struct local_address *a, const struct local_add if (r != 0) return r; + r = CMP(a->weight, b->weight); + if (r != 0) + return r; + r = CMP(a->ifindex, b->ifindex); if (r != 0) return r; @@ -64,6 +68,7 @@ static int add_local_address_full( int ifindex, unsigned char scope, uint32_t priority, + uint32_t weight, int family, const union in_addr_union *address) { @@ -80,6 +85,7 @@ static int add_local_address_full( .ifindex = ifindex, .scope = scope, .priority = priority, + .weight = weight, .family = family, .address = *address, }; @@ -95,7 +101,7 @@ static int add_local_address( int family, const union in_addr_union *address) { - return add_local_address_full(list, n_list, ifindex, scope, 0, family, address); + return add_local_address_full(list, n_list, ifindex, scope, 0, 0, family, address); } int local_addresses( @@ -216,10 +222,11 @@ static int add_local_gateway( size_t *n_list, int ifindex, uint32_t priority, + uint32_t weight, int family, const union in_addr_union *address) { - return add_local_address_full(list, n_list, ifindex, 0, priority, family, address); + return add_local_address_full(list, n_list, ifindex, 0, priority, weight, family, address); } int local_gateways( @@ -329,7 +336,7 @@ int local_gateways( if (r < 0 && r != -ENODATA) return r; if (r >= 0) { - r = add_local_gateway(&list, &n_list, ifi, priority, family, &gateway); + r = add_local_gateway(&list, &n_list, ifi, priority, 0, family, &gateway); if (r < 0) return r; @@ -350,7 +357,7 @@ int local_gateways( if (via.family != AF_INET6) return -EBADMSG; - r = add_local_gateway(&list, &n_list, ifi, priority, via.family, + r = add_local_gateway(&list, &n_list, ifi, priority, 0, via.family, &(union in_addr_union) { .in6 = via.address.in6 }); if (r < 0) return r; @@ -381,7 +388,7 @@ int local_gateways( continue; union in_addr_union a = mr->gateway.address; - r = add_local_gateway(&list, &n_list, ifi, priority, mr->gateway.family, &a); + r = add_local_gateway(&list, &n_list, ifi, priority, mr->weight, mr->gateway.family, &a); if (r < 0) return r; } @@ -404,7 +411,7 @@ static int add_local_outbound( int family, const union in_addr_union *address) { - return add_local_address_full(list, n_list, ifindex, 0, 0, family, address); + return add_local_address_full(list, n_list, ifindex, 0, 0, 0, family, address); } int local_outbounds( diff --git a/src/shared/local-addresses.h b/src/shared/local-addresses.h index 42bed2ba0d..7afbc89569 100644 --- a/src/shared/local-addresses.h +++ b/src/shared/local-addresses.h @@ -9,6 +9,7 @@ struct local_address { int ifindex; unsigned char scope; uint32_t priority; + uint32_t weight; int family; union in_addr_union address; }; diff --git a/src/test/test-local-addresses.c b/src/test/test-local-addresses.c index c9ed7bf77d..a7a684cc82 100644 --- a/src/test/test-local-addresses.c +++ b/src/test/test-local-addresses.c @@ -10,8 +10,8 @@ static void print_local_addresses(const struct local_address *a, size_t n) { FOREACH_ARRAY(i, a, n) - log_debug("%s ifindex=%i scope=%u priority=%"PRIu32" address=%s", - af_to_name(i->family), i->ifindex, i->scope, i->priority, + log_debug("%s ifindex=%i scope=%u priority=%"PRIu32" weight=%"PRIu32" address=%s", + af_to_name(i->family), i->ifindex, i->scope, i->priority, i->weight, IN_ADDR_TO_STRING(i->family, &i->address)); } From 16d95d6ff882739466de92bcb7d2b0d559f93a23 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 19 Jan 2024 20:14:08 +0900 Subject: [PATCH 10/13] local-addresses: introduce own parser for RTA_MULTIPATH --- src/shared/local-addresses.c | 124 ++++++++++++++++++++++++++++++----- 1 file changed, 107 insertions(+), 17 deletions(-) diff --git a/src/shared/local-addresses.c b/src/shared/local-addresses.c index a67a2bfd80..d5c478c5ca 100644 --- a/src/shared/local-addresses.c +++ b/src/shared/local-addresses.c @@ -229,6 +229,112 @@ static int add_local_gateway( return add_local_address_full(list, n_list, ifindex, 0, priority, weight, family, address); } +static int parse_nexthop_one( + struct local_address **list, + size_t *n_list, + bool allow_via, + int family, + uint32_t priority, + const struct rtnexthop *rtnh) { + + bool has_gw = false; + int r; + + assert(rtnh); + + size_t len = rtnh->rtnh_len - sizeof(struct rtnexthop); + for (struct rtattr *attr = RTNH_DATA(rtnh); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) + + switch (attr->rta_type) { + case RTA_GATEWAY: + if (has_gw) + return -EBADMSG; + + has_gw = true; + + if (attr->rta_len != RTA_LENGTH(FAMILY_ADDRESS_SIZE(family))) + return -EBADMSG; + + union in_addr_union a; + memcpy(&a, RTA_DATA(attr), FAMILY_ADDRESS_SIZE(family)); + r = add_local_gateway(list, n_list, rtnh->rtnh_ifindex, priority, rtnh->rtnh_hops, family, &a); + if (r < 0) + return r; + + break; + + case RTA_VIA: + if (has_gw) + return -EBADMSG; + + has_gw = true; + + if (!allow_via) + continue; + + if (family != AF_INET) + return -EBADMSG; /* RTA_VIA is only supported for IPv4 routes. */ + + if (attr->rta_len != RTA_LENGTH(sizeof(RouteVia))) + return -EBADMSG; + + RouteVia *via = RTA_DATA(attr); + if (via->family != AF_INET6) + return -EBADMSG; /* gateway address should be always IPv6. */ + + r = add_local_gateway(list, n_list, rtnh->rtnh_ifindex, priority, rtnh->rtnh_hops, via->family, + &(union in_addr_union) { .in6 = via->address.in6 }); + if (r < 0) + return r; + + break; + } + + return 0; +} + +static int parse_nexthops( + struct local_address **list, + size_t *n_list, + int ifindex, + bool allow_via, + int family, + uint32_t priority, + const struct rtnexthop *rtnh, + size_t size) { + + int r; + + assert(list); + assert(n_list); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(rtnh || size == 0); + + if (size < sizeof(struct rtnexthop)) + return -EBADMSG; + + for (; size >= sizeof(struct rtnexthop); ) { + if (NLMSG_ALIGN(rtnh->rtnh_len) > size) + return -EBADMSG; + + if (rtnh->rtnh_len < sizeof(struct rtnexthop)) + return -EBADMSG; + + if (ifindex > 0 && rtnh->rtnh_ifindex != ifindex) + goto next_nexthop; + + r = parse_nexthop_one(list, n_list, allow_via, family, priority, rtnh); + if (r < 0) + return r; + + next_nexthop: + size -= NLMSG_ALIGN(rtnh->rtnh_len); + rtnh = RTNH_NEXT(rtnh); + } + + return 0; +} + int local_gateways( sd_netlink *context, int ifindex, @@ -373,25 +479,9 @@ int local_gateways( if (r < 0 && r != -ENODATA) return r; if (r >= 0) { - _cleanup_ordered_set_free_free_ OrderedSet *multipath_routes = NULL; - MultipathRoute *mr; - - r = rtattr_read_nexthop(rta_multipath, rta_len, family, &multipath_routes); + r = parse_nexthops(&list, &n_list, ifindex, allow_via, family, priority, rta_multipath, rta_len); if (r < 0) return r; - - ORDERED_SET_FOREACH(mr, multipath_routes) { - if (ifindex > 0 && mr->ifindex != ifindex) - continue; - - if (!allow_via && family != mr->gateway.family) - continue; - - union in_addr_union a = mr->gateway.address; - r = add_local_gateway(&list, &n_list, ifi, priority, mr->weight, mr->gateway.family, &a); - if (r < 0) - return r; - } } } From d10311f407c510becaeb784effb7086dea2283e4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 19 Jan 2024 20:16:59 +0900 Subject: [PATCH 11/13] sd-netlink: drop unused parser for RTA_MULTIPATH --- src/libsystemd/sd-netlink/netlink-util.c | 115 ----------------------- src/libsystemd/sd-netlink/netlink-util.h | 14 --- 2 files changed, 129 deletions(-) diff --git a/src/libsystemd/sd-netlink/netlink-util.c b/src/libsystemd/sd-netlink/netlink-util.c index a591c7d6d6..3713fadfb4 100644 --- a/src/libsystemd/sd-netlink/netlink-util.c +++ b/src/libsystemd/sd-netlink/netlink-util.c @@ -613,121 +613,6 @@ int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void return 0; } -MultipathRoute *multipath_route_free(MultipathRoute *m) { - if (!m) - return NULL; - - free(m->ifname); - - return mfree(m); -} - -int multipath_route_dup(const MultipathRoute *m, MultipathRoute **ret) { - _cleanup_(multipath_route_freep) MultipathRoute *n = NULL; - _cleanup_free_ char *ifname = NULL; - - assert(m); - assert(ret); - - if (m->ifname) { - ifname = strdup(m->ifname); - if (!ifname) - return -ENOMEM; - } - - n = new(MultipathRoute, 1); - if (!n) - return -ENOMEM; - - *n = (MultipathRoute) { - .gateway = m->gateway, - .weight = m->weight, - .ifindex = m->ifindex, - .ifname = TAKE_PTR(ifname), - }; - - *ret = TAKE_PTR(n); - - return 0; -} - -int rtattr_read_nexthop(const struct rtnexthop *rtnh, size_t size, int family, OrderedSet **ret) { - _cleanup_ordered_set_free_free_ OrderedSet *set = NULL; - int r; - - assert(rtnh); - assert(IN_SET(family, AF_INET, AF_INET6)); - - if (size < sizeof(struct rtnexthop)) - return -EBADMSG; - - for (; size >= sizeof(struct rtnexthop); ) { - _cleanup_(multipath_route_freep) MultipathRoute *m = NULL; - - if (NLMSG_ALIGN(rtnh->rtnh_len) > size) - return -EBADMSG; - - if (rtnh->rtnh_len < sizeof(struct rtnexthop)) - return -EBADMSG; - - m = new(MultipathRoute, 1); - if (!m) - return -ENOMEM; - - *m = (MultipathRoute) { - .ifindex = rtnh->rtnh_ifindex, - .weight = rtnh->rtnh_hops, - }; - - if (rtnh->rtnh_len > sizeof(struct rtnexthop)) { - size_t len = rtnh->rtnh_len - sizeof(struct rtnexthop); - - for (struct rtattr *attr = RTNH_DATA(rtnh); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) { - if (attr->rta_type == RTA_GATEWAY) { - if (attr->rta_len != RTA_LENGTH(FAMILY_ADDRESS_SIZE(family))) - return -EBADMSG; - - m->gateway.family = family; - memcpy(&m->gateway.address, RTA_DATA(attr), FAMILY_ADDRESS_SIZE(family)); - break; - } else if (attr->rta_type == RTA_VIA) { - uint16_t gw_family; - - if (family != AF_INET) - return -EINVAL; - - if (attr->rta_len < RTA_LENGTH(sizeof(uint16_t))) - return -EBADMSG; - - gw_family = *(uint16_t *) RTA_DATA(attr); - - if (gw_family != AF_INET6) - return -EBADMSG; - - if (attr->rta_len != RTA_LENGTH(FAMILY_ADDRESS_SIZE(gw_family) + sizeof(gw_family))) - return -EBADMSG; - - memcpy(&m->gateway, RTA_DATA(attr), FAMILY_ADDRESS_SIZE(gw_family) + sizeof(gw_family)); - break; - } - } - } - - r = ordered_set_ensure_put(&set, NULL, m); - if (r < 0) - return r; - - TAKE_PTR(m); - - size -= NLMSG_ALIGN(rtnh->rtnh_len); - rtnh = RTNH_NEXT(rtnh); - } - - if (ret) - *ret = TAKE_PTR(set); - return 0; -} - bool netlink_pid_changed(sd_netlink *nl) { /* We don't support people creating an nl connection and * keeping it around over a fork(). Let's complain. */ diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h index b895ac41d1..277b4a30cf 100644 --- a/src/libsystemd/sd-netlink/netlink-util.h +++ b/src/libsystemd/sd-netlink/netlink-util.h @@ -19,18 +19,6 @@ typedef struct RouteVia { union in_addr_union address; } _packed_ RouteVia; -typedef struct MultipathRoute { - RouteVia gateway; - uint32_t weight; - int ifindex; - char *ifname; -} MultipathRoute; - -MultipathRoute *multipath_route_free(MultipathRoute *m); -DEFINE_TRIVIAL_CLEANUP_FUNC(MultipathRoute*, multipath_route_free); - -int multipath_route_dup(const MultipathRoute *m, MultipathRoute **ret); - int rtnl_rename_link(sd_netlink **rtnl, const char *orig_name, const char *new_name); int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name, char* const* alternative_names); static inline int rtnl_append_link_alternative_names(sd_netlink **rtnl, int ifindex, char* const *alternative_names) { @@ -107,8 +95,6 @@ int netlink_message_read_in_addr_union(sd_netlink_message *m, unsigned short typ void rtattr_append_attribute_internal(struct rtattr *rta, unsigned short type, const void *data, size_t data_length); int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void *data, size_t data_length); -int rtattr_read_nexthop(const struct rtnexthop *rtnh, size_t size, int family, OrderedSet **ret); - void netlink_seal_message(sd_netlink *nl, sd_netlink_message *m); size_t netlink_get_reply_callback_count(sd_netlink *nl); From e5ee645344c7407834e5cc5dad8db288c6760cc6 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 19 Jan 2024 22:33:36 +0900 Subject: [PATCH 12/13] local-addresses: introduce has_local_address() helper function It will be used later. --- src/shared/local-addresses.c | 11 +++++++++++ src/shared/local-addresses.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/src/shared/local-addresses.c b/src/shared/local-addresses.c index d5c478c5ca..b72ff1fb01 100644 --- a/src/shared/local-addresses.c +++ b/src/shared/local-addresses.c @@ -40,6 +40,17 @@ static int address_compare(const struct local_address *a, const struct local_add return memcmp(&a->address, &b->address, FAMILY_ADDRESS_SIZE(a->family)); } +bool has_local_address(const struct local_address *addresses, size_t n_addresses, const struct local_address *needle) { + assert(addresses || n_addresses == 0); + assert(needle); + + for (size_t i = 0; i < n_addresses; i++) + if (address_compare(addresses + i, needle) == 0) + return true; + + return false; +} + static void suppress_duplicates(struct local_address *list, size_t *n_list) { size_t old_size, new_size; diff --git a/src/shared/local-addresses.h b/src/shared/local-addresses.h index 7afbc89569..399d0c6fb8 100644 --- a/src/shared/local-addresses.h +++ b/src/shared/local-addresses.h @@ -14,6 +14,8 @@ struct local_address { union in_addr_union address; }; +bool has_local_address(const struct local_address *addresses, size_t n_addresses, const struct local_address *needle); + int local_addresses(sd_netlink *rtnl, int ifindex, int af, struct local_address **ret); int local_gateways(sd_netlink *rtnl, int ifindex, int af, struct local_address **ret); From c29138697de423d5b825e23fcf2bafe1765e389d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 19 Jan 2024 22:34:22 +0900 Subject: [PATCH 13/13] test-local-addresses: add more test cases --- src/test/test-local-addresses.c | 255 ++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) diff --git a/src/test/test-local-addresses.c b/src/test/test-local-addresses.c index a7a684cc82..e3d6c08d14 100644 --- a/src/test/test-local-addresses.c +++ b/src/test/test-local-addresses.c @@ -1,13 +1,18 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include #include #include "af-list.h" #include "alloc-util.h" +#include "capability-util.h" #include "in-addr-util.h" #include "local-addresses.h" +#include "netlink-util.h" #include "tests.h" +static bool support_rta_via = false; + static void print_local_addresses(const struct local_address *a, size_t n) { FOREACH_ARRAY(i, a, n) log_debug("%s ifindex=%i scope=%u priority=%"PRIu32" weight=%"PRIu32" address=%s", @@ -68,4 +73,254 @@ TEST(local_addresses) { free(a); } +static void check_local_addresses(sd_netlink *rtnl, int ifindex, int request_ifindex, int family) { + _cleanup_free_ struct local_address *a = NULL; + union in_addr_union u; + int n; + + log_debug("/* Local Addresses (ifindex:%i, %s) */", request_ifindex, family == AF_UNSPEC ? "AF_UNSPEC" : af_to_name(family)); + + n = local_addresses(rtnl, request_ifindex, family, &a); + assert_se(n >= 0); + print_local_addresses(a, n); + + assert_se(in_addr_from_string(AF_INET, "10.123.123.123", &u) >= 0); + assert_se(has_local_address(a, n, + &(struct local_address) { + .ifindex = ifindex, + .scope = RT_SCOPE_UNIVERSE, + .family = AF_INET, + .address = u, + }) == IN_SET(family, AF_UNSPEC, AF_INET)); + + assert_se(in_addr_from_string(AF_INET6, "2001:db8:0:123::123", &u) >= 0); + assert_se(has_local_address(a, n, + &(struct local_address) { + .ifindex = ifindex, + .scope = RT_SCOPE_UNIVERSE, + .family = AF_INET6, + .address = u, + }) == IN_SET(family, AF_UNSPEC, AF_INET6)); + + assert_se(in_addr_from_string(AF_INET6, "2001:db8:1:123::123", &u) >= 0); + assert_se(has_local_address(a, n, + &(struct local_address) { + .ifindex = ifindex, + .scope = RT_SCOPE_UNIVERSE, + .family = AF_INET6, + .address = u, + }) == IN_SET(family, AF_UNSPEC, AF_INET6)); +} + +static void check_local_gateways(sd_netlink *rtnl, int ifindex, int request_ifindex, int family) { + _cleanup_free_ struct local_address *a = NULL; + union in_addr_union u; + int n; + + log_debug("/* Local Gateways (ifindex:%i, %s) */", request_ifindex, family == AF_UNSPEC ? "AF_UNSPEC" : af_to_name(family)); + + n = local_gateways(rtnl, request_ifindex, family, &a); + assert_se(n >= 0); + print_local_addresses(a, n); + + assert_se(in_addr_from_string(AF_INET, "10.123.0.1", &u) >= 0); + assert_se(has_local_address(a, n, + &(struct local_address) { + .ifindex = ifindex, + .priority = 1234, + .family = AF_INET, + .address = u, + }) == IN_SET(family, AF_UNSPEC, AF_INET)); + + assert_se(in_addr_from_string(AF_INET6, "2001:db8:0:123::1", &u) >= 0); + assert_se(has_local_address(a, n, + &(struct local_address) { + .ifindex = ifindex, + .priority = 1234, + .family = AF_INET6, + .address = u, + }) == (family == AF_UNSPEC && support_rta_via)); + + assert_se(in_addr_from_string(AF_INET6, "2001:db8:1:123::1", &u) >= 0); + assert_se(has_local_address(a, n, + &(struct local_address) { + .ifindex = ifindex, + .priority = 1234, + .family = AF_INET6, + .address = u, + }) == IN_SET(family, AF_UNSPEC, AF_INET6)); +} + +static void check_local_outbounds(sd_netlink *rtnl, int ifindex, int request_ifindex, int family) { + _cleanup_free_ struct local_address *a = NULL; + union in_addr_union u; + int n; + + log_debug("/* Local Outbounds (ifindex:%i, %s) */", request_ifindex, family == AF_UNSPEC ? "AF_UNSPEC" : af_to_name(family)); + + n = local_outbounds(rtnl, request_ifindex, family, &a); + assert_se(n >= 0); + print_local_addresses(a, n); + + assert_se(in_addr_from_string(AF_INET, "10.123.123.123", &u) >= 0); + assert_se(has_local_address(a, n, + &(struct local_address) { + .ifindex = ifindex, + .family = AF_INET, + .address = u, + }) == IN_SET(family, AF_UNSPEC, AF_INET)); + + assert_se(in_addr_from_string(AF_INET6, "2001:db8:0:123::123", &u) >= 0); + assert_se(has_local_address(a, n, + &(struct local_address) { + .ifindex = ifindex, + .family = AF_INET6, + .address = u, + }) == (family == AF_UNSPEC && support_rta_via)); + + assert_se(in_addr_from_string(AF_INET6, "2001:db8:1:123::123", &u) >= 0); + assert_se(has_local_address(a, n, + &(struct local_address) { + .ifindex = ifindex, + .family = AF_INET6, + .address = u, + }) == IN_SET(family, AF_UNSPEC, AF_INET6)); +} + +TEST(local_addresses_with_dummy) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL; + union in_addr_union u; + int r, ifindex; + + assert_se(sd_netlink_open(&rtnl) >= 0); + + /* Create a dummy interface */ + assert_se(sd_rtnl_message_new_link(rtnl, &message, RTM_NEWLINK, 0) >= 0); + assert_se(sd_netlink_message_append_string(message, IFLA_IFNAME, "test-local-addr") >= 0); + assert_se(sd_netlink_message_open_container(message, IFLA_LINKINFO) >= 0); + assert_se(sd_netlink_message_append_string(message, IFLA_INFO_KIND, "dummy") >= 0); + r = sd_netlink_call(rtnl, message, 0, NULL); + if (r == -EPERM) + return (void) log_tests_skipped("missing required capabilities"); + if (r == -EOPNOTSUPP) + return (void) log_tests_skipped("dummy network interface is not supported"); + assert_se(r >= 0); + message = sd_netlink_message_unref(message); + + /* Get ifindex */ + assert_se(sd_rtnl_message_new_link(rtnl, &message, RTM_GETLINK, 0) >= 0); + assert_se(sd_netlink_message_append_string(message, IFLA_IFNAME, "test-local-addr") >= 0); + assert_se(sd_netlink_call(rtnl, message, 0, &reply) >= 0); + assert_se(sd_rtnl_message_link_get_ifindex(reply, &ifindex) >= 0); + assert_se(ifindex > 0); + message = sd_netlink_message_unref(message); + reply = sd_netlink_message_unref(reply); + + /* Bring the interface up */ + assert_se(sd_rtnl_message_new_link(rtnl, &message, RTM_SETLINK, ifindex) >= 0); + assert_se(sd_rtnl_message_link_set_flags(message, IFF_UP, IFF_UP) >= 0); + assert_se(sd_netlink_call(rtnl, message, 0, NULL) >= 0); + message = sd_netlink_message_unref(message); + + /* Add an IPv4 address */ + assert_se(sd_rtnl_message_new_addr_update(rtnl, &message, ifindex, AF_INET) >= 0); + assert_se(sd_rtnl_message_addr_set_scope(message, RT_SCOPE_UNIVERSE) >= 0); + assert_se(sd_rtnl_message_addr_set_prefixlen(message, 16) >= 0); + assert_se(in_addr_from_string(AF_INET, "10.123.123.123", &u) >= 0); + assert_se(sd_netlink_message_append_in_addr(message, IFA_LOCAL, &u.in) >= 0); + assert_se(in_addr_from_string(AF_INET, "10.123.255.255", &u) >= 0); + assert_se(sd_netlink_message_append_in_addr(message, IFA_BROADCAST, &u.in) >= 0); + assert_se(sd_netlink_call(rtnl, message, 0, NULL) >= 0); + message = sd_netlink_message_unref(message); + + /* Add IPv6 addresses */ + assert_se(sd_rtnl_message_new_addr_update(rtnl, &message, ifindex, AF_INET6) >= 0); + assert_se(sd_rtnl_message_addr_set_scope(message, RT_SCOPE_UNIVERSE) >= 0); + assert_se(sd_rtnl_message_addr_set_prefixlen(message, 64) >= 0); + assert_se(in_addr_from_string(AF_INET6, "2001:db8:0:123::123", &u) >= 0); + assert_se(sd_netlink_message_append_in6_addr(message, IFA_LOCAL, &u.in6) >= 0); + assert_se(sd_netlink_message_append_u32(message, IFA_FLAGS, IFA_F_NODAD) >= 0); + assert_se(sd_netlink_call(rtnl, message, 0, NULL) >= 0); + message = sd_netlink_message_unref(message); + + assert_se(sd_rtnl_message_new_addr_update(rtnl, &message, ifindex, AF_INET6) >= 0); + assert_se(sd_rtnl_message_addr_set_scope(message, RT_SCOPE_UNIVERSE) >= 0); + assert_se(sd_rtnl_message_addr_set_prefixlen(message, 64) >= 0); + assert_se(in_addr_from_string(AF_INET6, "2001:db8:1:123::123", &u) >= 0); + assert_se(sd_netlink_message_append_in6_addr(message, IFA_LOCAL, &u.in6) >= 0); + assert_se(sd_netlink_message_append_u32(message, IFA_FLAGS, IFA_F_NODAD) >= 0); + assert_se(sd_netlink_call(rtnl, message, 0, NULL) >= 0); + message = sd_netlink_message_unref(message); + + /* Add an IPv4 default gateway (RTA_GATEWAY) */ + assert_se(sd_rtnl_message_new_route(rtnl, &message, RTM_NEWROUTE, AF_INET, RTPROT_STATIC) >= 0); + assert_se(sd_rtnl_message_route_set_scope(message, RT_SCOPE_UNIVERSE) >= 0); + assert_se(sd_rtnl_message_route_set_type(message, RTN_UNICAST) >= 0); + assert_se(sd_netlink_message_append_u32(message, RTA_PRIORITY, 1234) >= 0); + assert_se(sd_netlink_message_append_u32(message, RTA_TABLE, RT_TABLE_MAIN) >= 0); + assert_se(in_addr_from_string(AF_INET, "10.123.0.1", &u) >= 0); + assert_se(sd_netlink_message_append_in_addr(message, RTA_GATEWAY, &u.in) >= 0); + assert_se(sd_netlink_message_append_u32(message, RTA_OIF, ifindex) >= 0); + assert_se(sd_netlink_call(rtnl, message, 0, NULL) >= 0); + message = sd_netlink_message_unref(message); + + /* Add an IPv4 default gateway (RTA_VIA) */ + assert_se(sd_rtnl_message_new_route(rtnl, &message, RTM_NEWROUTE, AF_INET, RTPROT_STATIC) >= 0); + assert_se(sd_rtnl_message_route_set_scope(message, RT_SCOPE_UNIVERSE) >= 0); + assert_se(sd_rtnl_message_route_set_type(message, RTN_UNICAST) >= 0); + assert_se(sd_netlink_message_append_u32(message, RTA_PRIORITY, 1234) >= 0); + assert_se(sd_netlink_message_append_u32(message, RTA_TABLE, RT_TABLE_MAIN) >= 0); + assert_se(in_addr_from_string(AF_INET6, "2001:db8:0:123::1", &u) >= 0); + assert_se(sd_netlink_message_append_data(message, RTA_VIA, + &(RouteVia) { + .family = AF_INET6, + .address = u, + }, sizeof(RouteVia)) >= 0); + assert_se(sd_netlink_message_append_u32(message, RTA_OIF, ifindex) >= 0); + r = sd_netlink_call(rtnl, message, 0, NULL); + if (r == -EINVAL) + log_debug_errno(r, "RTA_VIA is not supported, ignoring: %m"); + else + assert_se(r >= 0); + support_rta_via = r >= 0; + message = sd_netlink_message_unref(message); + + /* Add an IPv6 default gateway */ + assert_se(sd_rtnl_message_new_route(rtnl, &message, RTM_NEWROUTE, AF_INET6, RTPROT_STATIC) >= 0); + assert_se(sd_rtnl_message_route_set_type(message, RTN_UNICAST) >= 0); + assert_se(sd_netlink_message_append_u32(message, RTA_PRIORITY, 1234) >= 0); + assert_se(sd_netlink_message_append_u32(message, RTA_TABLE, RT_TABLE_MAIN) >= 0); + assert_se(in_addr_from_string(AF_INET6, "2001:db8:1:123::1", &u) >= 0); + assert_se(sd_netlink_message_append_in6_addr(message, RTA_GATEWAY, &u.in6) >= 0); + assert_se(sd_netlink_message_append_u32(message, RTA_OIF, ifindex) >= 0); + assert_se(sd_netlink_call(rtnl, message, 0, NULL) >= 0); + message = sd_netlink_message_unref(message); + + /* Check */ + check_local_addresses(rtnl, ifindex, 0, AF_UNSPEC); + check_local_addresses(rtnl, ifindex, 0, AF_INET); + check_local_addresses(rtnl, ifindex, 0, AF_INET6); + check_local_addresses(rtnl, ifindex, ifindex, AF_UNSPEC); + check_local_addresses(rtnl, ifindex, ifindex, AF_INET); + check_local_addresses(rtnl, ifindex, ifindex, AF_INET6); + check_local_gateways(rtnl, ifindex, 0, AF_UNSPEC); + check_local_gateways(rtnl, ifindex, 0, AF_INET); + check_local_gateways(rtnl, ifindex, 0, AF_INET6); + check_local_gateways(rtnl, ifindex, ifindex, AF_UNSPEC); + check_local_gateways(rtnl, ifindex, ifindex, AF_INET); + check_local_gateways(rtnl, ifindex, ifindex, AF_INET6); + check_local_outbounds(rtnl, ifindex, 0, AF_UNSPEC); + check_local_outbounds(rtnl, ifindex, 0, AF_INET); + check_local_outbounds(rtnl, ifindex, 0, AF_INET6); + check_local_outbounds(rtnl, ifindex, ifindex, AF_UNSPEC); + check_local_outbounds(rtnl, ifindex, ifindex, AF_INET); + check_local_outbounds(rtnl, ifindex, ifindex, AF_INET6); + + /* Cleanup */ + assert_se(sd_rtnl_message_new_link(rtnl, &message, RTM_DELLINK, ifindex) >= 0); + assert_se(sd_netlink_call(rtnl, message, 0, NULL) >= 0); + message = sd_netlink_message_unref(message); +} + DEFINE_TEST_MAIN(LOG_DEBUG);