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); diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index a30a805271..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), @@ -672,19 +669,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; } @@ -715,35 +699,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); @@ -756,7 +722,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. */ @@ -765,57 +731,26 @@ 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)); } - 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; } 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); @@ -828,7 +763,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. */ @@ -837,23 +772,10 @@ 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)); } - 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-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 485178678c..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; @@ -761,9 +760,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; } @@ -953,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;