From 96dd66eddfefacc0863c67ba6694e5f9c7fbabec Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 23 Apr 2024 12:34:47 +0900 Subject: [PATCH 1/5] sd-radv: send RA on update in caller side As the caller may want to update multiple prefixes and friends. Let's not automatically announce on update in library side. Currently, only prefix is updated during running. Hence, this does not change any effective behavior. --- src/libsystemd-network/sd-radv.c | 41 -------------------------------- src/network/networkd-radv.c | 11 ++++++++- 2 files changed, 10 insertions(+), 42 deletions(-) diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index a30a805271..fa15f96336 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -672,19 +672,6 @@ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { log_radv(ra, "Added prefix %s", addr_p); } - if (!sd_radv_is_running(ra)) - return 0; - - if (ra->ra_sent == 0) - return 0; - - /* If RAs have already been sent, send an RA immediately to announce the newly-added prefix */ - r = radv_send_router(ra, NULL); - if (r < 0) - log_radv_errno(ra, r, "Unable to send Router Advertisement for added prefix %s, ignoring: %m", addr_p); - else - log_radv(ra, "Sent Router Advertisement for added/updated prefix %s.", addr_p); - return 0; } @@ -768,20 +755,6 @@ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) { log_radv(ra, "Added route prefix %s", strna(addr_p)); } - if (!sd_radv_is_running(ra)) - return 0; - - if (ra->ra_sent == 0) - return 0; - - /* If RAs have already been sent, send an RA immediately to announce the newly-added route prefix */ - r = radv_send_router(ra, NULL); - if (r < 0) - log_radv_errno(ra, r, "Unable to send Router Advertisement for added route prefix %s, ignoring: %m", - strna(addr_p)); - else - log_radv(ra, "Sent Router Advertisement for added route prefix %s.", strna(addr_p)); - return 0; } @@ -840,20 +813,6 @@ int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p) { log_radv(ra, "Added PREF64 prefix %s", strna(addr_p)); } - if (!sd_radv_is_running(ra)) - return 0; - - if (ra->ra_sent == 0) - return 0; - - /* If RAs have already been sent, send an RA immediately to announce the newly-added route prefix */ - r = radv_send_router(ra, NULL); - if (r < 0) - log_radv_errno(ra, r, "Unable to send Router Advertisement for added PREF64 prefix %s, ignoring: %m", - strna(addr_p)); - else - log_radv(ra, "Sent Router Advertisement for added PREF64 prefix %s.", strna(addr_p)); - return 0; } diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index 485178678c..a7f89adbda 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -761,9 +761,18 @@ int radv_add_prefix( return r; r = sd_radv_add_prefix(link->radv, p); - if (r < 0 && r != -EEXIST) + if (r == -EEXIST) + return 0; + if (r < 0) return r; + if (sd_radv_is_running(link->radv)) { + /* Announce updated prefixe now. */ + r = sd_radv_send(link->radv); + if (r < 0) + return r; + } + return 0; } From 9dc25035394d2da5d1612062c1ce153e33afb0d0 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 23 Apr 2024 13:09:52 +0900 Subject: [PATCH 2/5] sd-radv: allow to send multiple routes or prefix64 that have intersection with others I cannot find any RFC that states we should not send multiple route prefix or pref64 options that have intersection with others. Moreover, each route prefix option has preference field, thus, user may want to send e.g. a prefix with the normal preference, and another sub prefix with the high preference. Previously, such configuration was prohibited. Let's allow that now. --- src/libsystemd-network/sd-radv.c | 58 +++++++------------------------- 1 file changed, 12 insertions(+), 46 deletions(-) diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index fa15f96336..6dc4f9be29 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -702,35 +702,17 @@ void sd_radv_remove_prefix( int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) { sd_radv_route_prefix *found = NULL; - int r; assert_return(ra, -EINVAL); assert_return(p, -EINVAL); - const char *addr_p = IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen); - - LIST_FOREACH(prefix, cur, ra->route_prefixes) { - r = in_addr_prefix_intersect(AF_INET6, - (const union in_addr_union*) &cur->opt.in6_addr, - cur->opt.prefixlen, - (const union in_addr_union*) &p->opt.in6_addr, - p->opt.prefixlen); - if (r < 0) - return r; - if (r == 0) - continue; - - if (cur->opt.prefixlen == p->opt.prefixlen) { + LIST_FOREACH(prefix, cur, ra->route_prefixes) + if (cur->opt.prefixlen == p->opt.prefixlen && + in6_addr_equal(&cur->opt.in6_addr, &p->opt.in6_addr)) { found = cur; break; } - return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST), - "IPv6 route prefix %s conflicts with %s, ignoring.", - addr_p, - IN6_ADDR_PREFIX_TO_STRING(&cur->opt.in6_addr, cur->opt.prefixlen)); - } - if (found) { /* p and cur may be equivalent. First increment the reference counter. */ sd_radv_route_prefix_ref(p); @@ -743,7 +725,7 @@ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) { LIST_APPEND(prefix, ra->route_prefixes, p); log_radv(ra, "Updated/replaced IPv6 route prefix %s (lifetime: %s)", - strna(addr_p), + IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen), FORMAT_TIMESPAN(p->lifetime_usec, USEC_PER_SEC)); } else { /* The route prefix is new. Let's simply add it. */ @@ -752,7 +734,8 @@ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) { LIST_APPEND(prefix, ra->route_prefixes, p); ra->n_route_prefixes++; - log_radv(ra, "Added route prefix %s", strna(addr_p)); + log_radv(ra, "Added route prefix %s", + IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen)); } return 0; @@ -760,35 +743,17 @@ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) { int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p) { sd_radv_pref64_prefix *found = NULL; - int r; assert_return(ra, -EINVAL); assert_return(p, -EINVAL); - const char *addr_p = IN6_ADDR_PREFIX_TO_STRING(&p->in6_addr, p->prefixlen); - - LIST_FOREACH(prefix, cur, ra->pref64_prefixes) { - r = in_addr_prefix_intersect(AF_INET6, - (const union in_addr_union*) &cur->in6_addr, - cur->prefixlen, - (const union in_addr_union*) &p->in6_addr, - p->prefixlen); - if (r < 0) - return r; - if (r == 0) - continue; - - if (cur->prefixlen == p->prefixlen) { + LIST_FOREACH(prefix, cur, ra->pref64_prefixes) + if (cur->prefixlen == p->prefixlen && + in6_addr_equal(&cur->in6_addr, &p->in6_addr)) { found = cur; break; } - return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST), - "IPv6 PREF64 prefix %s conflicts with %s, ignoring.", - addr_p, - IN6_ADDR_PREFIX_TO_STRING(&cur->in6_addr, cur->prefixlen)); - } - if (found) { /* p and cur may be equivalent. First increment the reference counter. */ sd_radv_pref64_prefix_ref(p); @@ -801,7 +766,7 @@ int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p) { LIST_APPEND(prefix, ra->pref64_prefixes, p); log_radv(ra, "Updated/replaced IPv6 PREF64 prefix %s (lifetime: %s)", - strna(addr_p), + IN6_ADDR_PREFIX_TO_STRING(&p->in6_addr, p->prefixlen), FORMAT_TIMESPAN(p->lifetime_usec, USEC_PER_SEC)); } else { /* The route prefix is new. Let's simply add it. */ @@ -810,7 +775,8 @@ int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p) { LIST_APPEND(prefix, ra->pref64_prefixes, p); ra->n_pref64_prefixes++; - log_radv(ra, "Added PREF64 prefix %s", strna(addr_p)); + log_radv(ra, "Added PREF64 prefix %s", + IN6_ADDR_PREFIX_TO_STRING(&p->in6_addr, p->prefixlen)); } return 0; From 21ae0b4b3cc82d4f63d4426ce9ff78167b69122c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 23 Apr 2024 12:29:38 +0900 Subject: [PATCH 3/5] in-addr-util: introduce in{4,6}_addr_prefix_intersect() --- src/basic/in-addr-util.c | 91 ++++++++++++++++++++++------------------ src/basic/in-addr-util.h | 17 +++++++- 2 files changed, 67 insertions(+), 41 deletions(-) diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index 82c8ff9a2d..c077f0aa7d 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -194,6 +194,52 @@ int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_ return -EAFNOSUPPORT; } +bool in4_addr_prefix_intersect( + const struct in_addr *a, + unsigned aprefixlen, + const struct in_addr *b, + unsigned bprefixlen) { + + assert(a); + assert(b); + + unsigned m = MIN3(aprefixlen, bprefixlen, (unsigned) (sizeof(struct in_addr) * 8)); + if (m == 0) + return true; /* Let's return earlier, to avoid shift by 32. */ + + uint32_t x = be32toh(a->s_addr ^ b->s_addr); + uint32_t n = 0xFFFFFFFFUL << (32 - m); + return (x & n) == 0; +} + +bool in6_addr_prefix_intersect( + const struct in6_addr *a, + unsigned aprefixlen, + const struct in6_addr *b, + unsigned bprefixlen) { + + assert(a); + assert(b); + + unsigned m = MIN3(aprefixlen, bprefixlen, (unsigned) (sizeof(struct in6_addr) * 8)); + if (m == 0) + return true; + + for (size_t i = 0; i < sizeof(struct in6_addr); i++) { + uint8_t x = a->s6_addr[i] ^ b->s6_addr[i]; + uint8_t n = m < 8 ? (0xFF << (8 - m)) : 0xFF; + if ((x & n) != 0) + return false; + + if (m <= 8) + break; + + m -= 8; + } + + return true; +} + int in_addr_prefix_intersect( int family, const union in_addr_union *a, @@ -201,51 +247,16 @@ int in_addr_prefix_intersect( const union in_addr_union *b, unsigned bprefixlen) { - unsigned m; - assert(a); assert(b); - /* Checks whether there are any addresses that are in both networks */ + /* Checks whether there are any addresses that are in both networks. */ - m = MIN(aprefixlen, bprefixlen); + if (family == AF_INET) + return in4_addr_prefix_intersect(&a->in, aprefixlen, &b->in, bprefixlen); - if (family == AF_INET) { - uint32_t x, nm; - - x = be32toh(a->in.s_addr ^ b->in.s_addr); - nm = m == 0 ? 0 : 0xFFFFFFFFUL << (32 - m); - - return (x & nm) == 0; - } - - if (family == AF_INET6) { - unsigned i; - - if (m > 128) - m = 128; - - for (i = 0; i < 16; i++) { - uint8_t x, nm; - - x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i]; - - if (m < 8) - nm = 0xFF << (8 - m); - else - nm = 0xFF; - - if ((x & nm) != 0) - return 0; - - if (m > 8) - m -= 8; - else - m = 0; - } - - return 1; - } + if (family == AF_INET6) + return in6_addr_prefix_intersect(&a->in6, aprefixlen, &b->in6, bprefixlen); return -EAFNOSUPPORT; } diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index 5c820c6ec6..9cd0aca9a4 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -61,7 +61,22 @@ bool in6_addr_is_ipv4_mapped_address(const struct in6_addr *a); bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b); bool in6_addr_equal(const struct in6_addr *a, const struct in6_addr *b); int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b); -int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen); +bool in4_addr_prefix_intersect( + const struct in_addr *a, + unsigned aprefixlen, + const struct in_addr *b, + unsigned bprefixlen); +bool in6_addr_prefix_intersect( + const struct in6_addr *a, + unsigned aprefixlen, + const struct in6_addr *b, + unsigned bprefixlen); +int in_addr_prefix_intersect( + int family, + const union in_addr_union *a, + unsigned aprefixlen, + const union in_addr_union *b, + unsigned bprefixlen); int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth); int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, unsigned prefixlen); From c7c56724a5d7cc2b6b0d9d5f95b1055811014843 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 23 Apr 2024 13:14:21 +0900 Subject: [PATCH 4/5] sd-radv: use in6_addr_prefix_intersect() --- src/libsystemd-network/sd-radv.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index 6dc4f9be29..b8c5d1fbfd 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -626,19 +626,16 @@ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { const char *addr_p = IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen); LIST_FOREACH(prefix, cur, ra->prefixes) { - r = in_addr_prefix_intersect(AF_INET6, - (const union in_addr_union*) &cur->opt.in6_addr, - cur->opt.prefixlen, - (const union in_addr_union*) &p->opt.in6_addr, - p->opt.prefixlen); + r = in6_addr_prefix_intersect(&cur->opt.in6_addr, cur->opt.prefixlen, + &p->opt.in6_addr, p->opt.prefixlen); if (r < 0) return r; if (r == 0) - continue; + continue; /* no intersection */ if (cur->opt.prefixlen == p->opt.prefixlen) { found = cur; - break; + break; /* same prefix */ } return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST), From 6fe51dc50cd22e3a07276adceaffe3d8d4acfcfa Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 23 Apr 2024 12:45:18 +0900 Subject: [PATCH 5/5] network/radv: merge two boolean flags for prefix into one --- src/network/networkd-network-gperf.gperf | 6 ++++-- src/network/networkd-radv.c | 19 ++++++++----------- src/network/networkd-radv.h | 6 ++---- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 4124b3270f..23e25ffc1f 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -3,7 +3,9 @@ #if __GNUC__ >= 7 _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") #endif +#include #include + #include "conf-parser.h" #include "in-addr-prefix-util.h" #include "netem.h" @@ -409,8 +411,8 @@ IPv6SendRA.HomeAgent, config_parse_bool, IPv6SendRA.HomeAgentLifetimeSec, config_parse_router_home_agent_lifetime, 0, offsetof(Network, home_agent_lifetime_usec) IPv6SendRA.HomeAgentPreference, config_parse_uint16, 0, offsetof(Network, router_home_agent_preference) IPv6Prefix.Prefix, config_parse_prefix, 0, 0 -IPv6Prefix.OnLink, config_parse_prefix_boolean, 0, 0 -IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_boolean, 0, 0 +IPv6Prefix.OnLink, config_parse_prefix_boolean, ND_OPT_PI_FLAG_ONLINK, 0 +IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_boolean, ND_OPT_PI_FLAG_AUTO, 0 IPv6Prefix.ValidLifetimeSec, config_parse_prefix_lifetime, 0, 0 IPv6Prefix.PreferredLifetimeSec, config_parse_prefix_lifetime, 0, 0 IPv6Prefix.Assign, config_parse_prefix_boolean, 0, 0 diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index a7f89adbda..3d6e9a128f 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -80,10 +80,9 @@ static int prefix_new_static(Network *network, const char *filename, unsigned se .network = network, .section = TAKE_PTR(n), + .flags = ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO, .preferred_lifetime = RADV_DEFAULT_PREFERRED_LIFETIME_USEC, .valid_lifetime = RADV_DEFAULT_VALID_LIFETIME_USEC, - .onlink = true, - .address_auto_configuration = true, }; r = hashmap_ensure_put(&network->prefixes_by_section, &config_section_hash_ops, prefix->section, prefix); @@ -298,11 +297,11 @@ static int radv_set_prefix(Link *link, Prefix *prefix) { if (r < 0) return r; - r = sd_radv_prefix_set_onlink(p, prefix->onlink); + r = sd_radv_prefix_set_onlink(p, FLAGS_SET(prefix->flags, ND_OPT_PI_FLAG_ONLINK)); if (r < 0) return r; - r = sd_radv_prefix_set_address_autoconfiguration(p, prefix->address_auto_configuration); + r = sd_radv_prefix_set_address_autoconfiguration(p, FLAGS_SET(prefix->flags, ND_OPT_PI_FLAG_AUTO)); if (r < 0) return r; @@ -962,14 +961,12 @@ int config_parse_prefix_boolean( return 0; } - if (streq(lvalue, "OnLink")) - p->onlink = r; - else if (streq(lvalue, "AddressAutoconfiguration")) - p->address_auto_configuration = r; - else if (streq(lvalue, "Assign")) + if (ltype != 0) + SET_FLAG(p->flags, ltype, r); + else { + assert(streq(lvalue, "Assign")); p->assign = r; - else - assert_not_reached(); + } TAKE_PTR(p); return 0; diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h index 569ca9e62b..c1ec6453ce 100644 --- a/src/network/networkd-radv.h +++ b/src/network/networkd-radv.h @@ -30,14 +30,12 @@ typedef struct Prefix { Network *network; ConfigSection *section; - struct in6_addr prefix; + uint8_t flags; uint8_t prefixlen; + struct in6_addr prefix; usec_t preferred_lifetime; usec_t valid_lifetime; - bool onlink; - bool address_auto_configuration; - bool assign; uint32_t route_metric; Set *tokens;