]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: radv: reorder functions
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 17 Sep 2021 11:54:47 +0000 (20:54 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 6 Oct 2021 16:02:59 +0000 (01:02 +0900)
In other files, we usually (but not always) place functions in the following order:
- network_adjust_xxx(), which applies default or updates settings
  specified in .network files,
- link_xxx_enabled(), which checks if the functionality is enabled,
- xxx_new() and xxx_free(), allocator and deallocator for sections,
- functions which apply/update/remove configs
- validators of section,
- conf parsers.

This does not change each function, but just changes the order.

src/network/networkd-radv.c

index c734c4c3b48a29273986993743184269c20a7384..6a873d7669118dde88a792b5c3795566d7b22faa 100644 (file)
 #include "string-table.h"
 #include "strv.h"
 
+void network_adjust_radv(Network *network) {
+        assert(network);
+
+        /* After this function is called, network->router_prefix_delegation can be treated as a boolean. */
+
+        if (network->dhcp6_pd < 0)
+                /* For backward compatibility. */
+                network->dhcp6_pd = FLAGS_SET(network->router_prefix_delegation, RADV_PREFIX_DELEGATION_DHCP6);
+
+        if (!FLAGS_SET(network->link_local, ADDRESS_FAMILY_IPV6)) {
+                if (network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE)
+                        log_warning("%s: IPv6PrefixDelegation= is enabled but IPv6 link local addressing is disabled. "
+                                    "Disabling IPv6PrefixDelegation=.", network->filename);
+
+                network->router_prefix_delegation = RADV_PREFIX_DELEGATION_NONE;
+        }
+
+        if (network->router_prefix_delegation == RADV_PREFIX_DELEGATION_NONE) {
+                network->n_router_dns = 0;
+                network->router_dns = mfree(network->router_dns);
+                network->router_search_domains = ordered_set_free(network->router_search_domains);
+        }
+
+        if (!FLAGS_SET(network->router_prefix_delegation, RADV_PREFIX_DELEGATION_STATIC)) {
+                network->prefixes_by_section = hashmap_free_with_destructor(network->prefixes_by_section, prefix_free);
+                network->route_prefixes_by_section = hashmap_free_with_destructor(network->route_prefixes_by_section, route_prefix_free);
+        }
+}
+
+static bool link_radv_enabled(Link *link) {
+        assert(link);
+
+        if (!link_ipv6ll_enabled(link))
+                return false;
+
+        return link->network->router_prefix_delegation;
+}
+
 Prefix *prefix_free(Prefix *prefix) {
         if (!prefix)
                 return NULL;
@@ -155,64 +193,6 @@ static int route_prefix_new_static(Network *network, const char *filename, unsig
         return 0;
 }
 
-void network_drop_invalid_prefixes(Network *network) {
-        Prefix *prefix;
-
-        assert(network);
-
-        HASHMAP_FOREACH(prefix, network->prefixes_by_section)
-                if (section_is_invalid(prefix->section))
-                        prefix_free(prefix);
-}
-
-void network_drop_invalid_route_prefixes(Network *network) {
-        RoutePrefix *prefix;
-
-        assert(network);
-
-        HASHMAP_FOREACH(prefix, network->route_prefixes_by_section)
-                if (section_is_invalid(prefix->section))
-                        route_prefix_free(prefix);
-}
-
-void network_adjust_radv(Network *network) {
-        assert(network);
-
-        /* After this function is called, network->router_prefix_delegation can be treated as a boolean. */
-
-        if (network->dhcp6_pd < 0)
-                /* For backward compatibility. */
-                network->dhcp6_pd = FLAGS_SET(network->router_prefix_delegation, RADV_PREFIX_DELEGATION_DHCP6);
-
-        if (!FLAGS_SET(network->link_local, ADDRESS_FAMILY_IPV6)) {
-                if (network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE)
-                        log_warning("%s: IPv6PrefixDelegation= is enabled but IPv6 link local addressing is disabled. "
-                                    "Disabling IPv6PrefixDelegation=.", network->filename);
-
-                network->router_prefix_delegation = RADV_PREFIX_DELEGATION_NONE;
-        }
-
-        if (network->router_prefix_delegation == RADV_PREFIX_DELEGATION_NONE) {
-                network->n_router_dns = 0;
-                network->router_dns = mfree(network->router_dns);
-                network->router_search_domains = ordered_set_free(network->router_search_domains);
-        }
-
-        if (!FLAGS_SET(network->router_prefix_delegation, RADV_PREFIX_DELEGATION_STATIC)) {
-                network->prefixes_by_section = hashmap_free_with_destructor(network->prefixes_by_section, prefix_free);
-                network->route_prefixes_by_section = hashmap_free_with_destructor(network->route_prefixes_by_section, route_prefix_free);
-        }
-}
-
-static bool link_radv_enabled(Link *link) {
-        assert(link);
-
-        if (!link_ipv6ll_enabled(link))
-                return false;
-
-        return link->network->router_prefix_delegation;
-}
-
 int link_request_radv_addresses(Link *link) {
         Prefix *p;
         int r;
@@ -252,704 +232,724 @@ int link_request_radv_addresses(Link *link) {
         return 0;
 }
 
-int config_parse_prefix(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
+static int network_get_ipv6_dns(Network *network, struct in6_addr **ret_addresses, size_t *ret_size) {
+        _cleanup_free_ struct in6_addr *addresses = NULL;
+        size_t n_addresses = 0;
 
-        Network *network = userdata;
-        _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
-        uint8_t prefixlen = 64;
-        union in_addr_union in6addr;
-        int r;
+        assert(network);
+        assert(ret_addresses);
+        assert(ret_size);
 
-        assert(filename);
-        assert(section);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
+        for (size_t i = 0; i < network->n_dns; i++) {
+                union in_addr_union *addr;
 
-        r = prefix_new_static(network, filename, section_line, &p);
-        if (r < 0)
-                return log_oom();
+                if (network->dns[i]->family != AF_INET6)
+                        continue;
 
-        r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue);
-                return 0;
-        }
+                addr = &network->dns[i]->address;
 
-        r = sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set radv prefix, ignoring assignment: %s", rvalue);
-                return 0;
+                if (in_addr_is_null(AF_INET6, addr) ||
+                    in_addr_is_link_local(AF_INET6, addr) ||
+                    in_addr_is_localhost(AF_INET6, addr))
+                        continue;
+
+                if (!GREEDY_REALLOC(addresses, n_addresses + 1))
+                        return -ENOMEM;
+
+                addresses[n_addresses++] = addr->in6;
         }
 
-        p = NULL;
+        *ret_addresses = TAKE_PTR(addresses);
+        *ret_size = n_addresses;
 
-        return 0;
+        return n_addresses;
 }
 
-int config_parse_prefix_flags(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        Network *network = userdata;
-        _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
+static int radv_set_dns(Link *link, Link *uplink) {
+        _cleanup_free_ struct in6_addr *dns = NULL;
+        usec_t lifetime_usec;
+        size_t n_dns;
         int r;
 
-        assert(filename);
-        assert(section);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        r = prefix_new_static(network, filename, section_line, &p);
-        if (r < 0)
-                return log_oom();
-
-        r = parse_boolean(rvalue);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
+        if (!link->network->router_emit_dns)
                 return 0;
-        }
 
-        if (streq(lvalue, "OnLink"))
-                r = sd_radv_prefix_set_onlink(p->radv_prefix, r);
-        else if (streq(lvalue, "AddressAutoconfiguration"))
-                r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, r);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set %s=, ignoring assignment: %m", lvalue);
-                return 0;
-        }
+        if (link->network->router_dns) {
+                struct in6_addr *p;
 
-        p = NULL;
+                dns = new(struct in6_addr, link->network->n_router_dns);
+                if (!dns)
+                        return -ENOMEM;
 
-        return 0;
-}
+                p = dns;
+                for (size_t i = 0; i < link->network->n_router_dns; i++)
+                        if (in6_addr_is_null(&link->network->router_dns[i])) {
+                                if (in6_addr_is_set(&link->ipv6ll_address))
+                                        *(p++) = link->ipv6ll_address;
+                        } else
+                                *(p++) = link->network->router_dns[i];
 
-int config_parse_prefix_lifetime(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
+                n_dns = p - dns;
+                lifetime_usec = link->network->router_dns_lifetime_usec;
 
-        Network *network = userdata;
-        _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
-        usec_t usec;
-        int r;
+                goto set_dns;
+        }
 
-        assert(filename);
-        assert(section);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
+        lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC;
 
-        r = prefix_new_static(network, filename, section_line, &p);
-        if (r < 0)
-                return log_oom();
+        r = network_get_ipv6_dns(link->network, &dns, &n_dns);
+        if (r > 0)
+                goto set_dns;
 
-        r = parse_sec(rvalue, &usec);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue);
-                return 0;
+        if (uplink) {
+                assert(uplink->network);
+
+                r = network_get_ipv6_dns(uplink->network, &dns, &n_dns);
+                if (r > 0)
+                        goto set_dns;
         }
 
-        /* a value of 0xffffffff represents infinity */
-        if (streq(lvalue, "PreferredLifetimeSec"))
-                r = sd_radv_prefix_set_preferred_lifetime(p->radv_prefix,
-                                                          DIV_ROUND_UP(usec, USEC_PER_SEC));
-        else if (streq(lvalue, "ValidLifetimeSec"))
-                r = sd_radv_prefix_set_valid_lifetime(p->radv_prefix,
-                                                      DIV_ROUND_UP(usec, USEC_PER_SEC));
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set %s=, ignoring assignment: %m", lvalue);
-                return 0;
-        }
-
-        p = NULL;
-
         return 0;
-}
-
-int config_parse_prefix_assign(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        Network *network = userdata;
-        _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
-        int r;
 
-        assert(filename);
-        assert(section);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
+set_dns:
+        return sd_radv_set_rdnss(link->radv,
+                                 DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC),
+                                 dns, n_dns);
+}
 
-        r = prefix_new_static(network, filename, section_line, &p);
-        if (r < 0)
-                return log_oom();
+static int radv_set_domains(Link *link, Link *uplink) {
+        OrderedSet *search_domains;
+        usec_t lifetime_usec;
+        _cleanup_free_ char **s = NULL; /* just free() because the strings are owned by the set */
 
-        r = parse_boolean(rvalue);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to parse %s=, ignoring assignment: %s",
-                           lvalue, rvalue);
+        if (!link->network->router_emit_domains)
                 return 0;
-        }
-
-        p->assign = r;
-        p = NULL;
 
-        return 0;
-}
+        search_domains = link->network->router_search_domains;
+        lifetime_usec = link->network->router_dns_lifetime_usec;
 
-int config_parse_prefix_metric(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
+        if (search_domains)
+                goto set_domains;
 
-        Network *network = userdata;
-        _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
-        int r;
+        lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC;
 
-        assert(filename);
-        assert(section);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
+        search_domains = link->network->search_domains;
+        if (search_domains)
+                goto set_domains;
 
-        r = prefix_new_static(network, filename, section_line, &p);
-        if (r < 0)
-                return log_oom();
+        if (uplink) {
+                assert(uplink->network);
 
-        r = safe_atou32(rvalue, &p->route_metric);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to parse %s=, ignoring assignment: %s",
-                           lvalue, rvalue);
-                return 0;
+                search_domains = uplink->network->search_domains;
+                if (search_domains)
+                        goto set_domains;
         }
 
-        TAKE_PTR(p);
-
         return 0;
-}
 
-int config_parse_route_prefix(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
+set_domains:
+        s = ordered_set_get_strv(search_domains);
+        if (!s)
+                return log_oom();
 
-        Network *network = userdata;
-        _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
-        uint8_t prefixlen = 64;
-        union in_addr_union in6addr;
-        int r;
+        return sd_radv_set_dnssl(link->radv,
+                                 DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC),
+                                 s);
 
-        assert(filename);
-        assert(section);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
+}
 
-        r = route_prefix_new_static(network, filename, section_line, &p);
-        if (r < 0)
-                return log_oom();
+static int radv_find_uplink(Link *link, Link **ret) {
+        assert(link);
 
-        r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r, "Route prefix is invalid, ignoring assignment: %s", rvalue);
-                return 0;
-        }
+        if (link->network->router_uplink_name)
+                return link_get_by_name(link->manager, link->network->router_uplink_name, ret);
 
-        r = sd_radv_prefix_set_route_prefix(p->radv_route_prefix, &in6addr.in6, prefixlen);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set route prefix, ignoring assignment: %m");
+        if (link->network->router_uplink_index > 0)
+                return link_get_by_index(link->manager, link->network->router_uplink_index, ret);
+
+        if (link->network->router_uplink_index == UPLINK_INDEX_AUTO) {
+                /* It is not necessary to propagate error in automatic selection. */
+                if (manager_find_uplink(link->manager, AF_INET6, link, ret) < 0)
+                        *ret = NULL;
                 return 0;
         }
 
-        p = NULL;
-
+        *ret = NULL;
         return 0;
 }
 
-int config_parse_route_prefix_lifetime(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        Network *network = userdata;
-        _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
-        usec_t usec;
+static int radv_configure(Link *link) {
+        uint16_t router_lifetime;
+        Link *uplink = NULL;
+        RoutePrefix *q;
+        Prefix *p;
         int r;
 
-        assert(filename);
-        assert(section);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
+        assert(link);
+        assert(link->network);
 
-        r = route_prefix_new_static(network, filename, section_line, &p);
+        if (link->radv)
+                return -EBUSY;
+
+        r = sd_radv_new(&link->radv);
         if (r < 0)
-                return log_oom();
+                return r;
 
-        r = parse_sec(rvalue, &usec);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Route lifetime is invalid, ignoring assignment: %s", rvalue);
-                return 0;
-        }
+        r = sd_radv_attach_event(link->radv, link->manager->event, 0);
+        if (r < 0)
+                return r;
 
-        /* a value of 0xffffffff represents infinity */
-        r = sd_radv_route_prefix_set_lifetime(p->radv_route_prefix, DIV_ROUND_UP(usec, USEC_PER_SEC));
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to set route lifetime, ignoring assignment: %m");
-                return 0;
-        }
+        r = sd_radv_set_mac(link->radv, &link->hw_addr.ether);
+        if (r < 0)
+                return r;
 
-        p = NULL;
+        r = sd_radv_set_ifindex(link->radv, link->ifindex);
+        if (r < 0)
+                return r;
 
-        return 0;
-}
+        r = sd_radv_set_managed_information(link->radv, link->network->router_managed);
+        if (r < 0)
+                return r;
 
-static int network_get_ipv6_dns(Network *network, struct in6_addr **ret_addresses, size_t *ret_size) {
-        _cleanup_free_ struct in6_addr *addresses = NULL;
-        size_t n_addresses = 0;
+        r = sd_radv_set_other_information(link->radv, link->network->router_other_information);
+        if (r < 0)
+                return r;
 
-        assert(network);
-        assert(ret_addresses);
-        assert(ret_size);
+        /* a value of UINT16_MAX represents infinity, 0x0 means this host is not a router */
+        if (link->network->router_lifetime_usec == USEC_INFINITY)
+                router_lifetime = UINT16_MAX;
+        else if (link->network->router_lifetime_usec > (UINT16_MAX - 1) * USEC_PER_SEC)
+                router_lifetime = UINT16_MAX - 1;
+        else
+                router_lifetime = DIV_ROUND_UP(link->network->router_lifetime_usec, USEC_PER_SEC);
 
-        for (size_t i = 0; i < network->n_dns; i++) {
-                union in_addr_union *addr;
+        r = sd_radv_set_router_lifetime(link->radv, router_lifetime);
+        if (r < 0)
+                return r;
 
-                if (network->dns[i]->family != AF_INET6)
-                        continue;
+        if (router_lifetime > 0) {
+                r = sd_radv_set_preference(link->radv, link->network->router_preference);
+                if (r < 0)
+                        return r;
+        }
 
-                addr = &network->dns[i]->address;
+        HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
+                r = sd_radv_add_prefix(link->radv, p->radv_prefix, false);
+                if (r == -EEXIST)
+                        continue;
+                if (r == -ENOEXEC) {
+                        log_link_warning_errno(link, r, "[IPv6Prefix] section configured without Prefix= setting, ignoring section.");
+                        continue;
+                }
+                if (r < 0)
+                        return r;
+        }
 
-                if (in_addr_is_null(AF_INET6, addr) ||
-                    in_addr_is_link_local(AF_INET6, addr) ||
-                    in_addr_is_localhost(AF_INET6, addr))
+        HASHMAP_FOREACH(q, link->network->route_prefixes_by_section) {
+                r = sd_radv_add_route_prefix(link->radv, q->radv_route_prefix, false);
+                if (r == -EEXIST)
                         continue;
+                if (r < 0)
+                        return r;
+        }
 
-                if (!GREEDY_REALLOC(addresses, n_addresses + 1))
-                        return -ENOMEM;
+        (void) radv_find_uplink(link, &uplink);
 
-                addresses[n_addresses++] = addr->in6;
-        }
+        r = radv_set_dns(link, uplink);
+        if (r < 0)
+                return log_link_debug_errno(link, r, "Could not set RA DNS: %m");
 
-        *ret_addresses = TAKE_PTR(addresses);
-        *ret_size = n_addresses;
+        r = radv_set_domains(link, uplink);
+        if (r < 0)
+                return log_link_debug_errno(link, r, "Could not set RA Domains: %m");
 
-        return n_addresses;
+        return 0;
 }
 
-static int radv_set_dns(Link *link, Link *uplink) {
-        _cleanup_free_ struct in6_addr *dns = NULL;
-        usec_t lifetime_usec;
-        size_t n_dns;
+int radv_update_mac(Link *link) {
+        bool restart;
         int r;
 
-        if (!link->network->router_emit_dns)
-                return 0;
+        assert(link);
 
-        if (link->network->router_dns) {
-                struct in6_addr *p;
+        if (!link->radv)
+                return 0;
 
-                dns = new(struct in6_addr, link->network->n_router_dns);
-                if (!dns)
-                        return -ENOMEM;
+        restart = sd_radv_is_running(link->radv);
 
-                p = dns;
-                for (size_t i = 0; i < link->network->n_router_dns; i++)
-                        if (in6_addr_is_null(&link->network->router_dns[i])) {
-                                if (in6_addr_is_set(&link->ipv6ll_address))
-                                        *(p++) = link->ipv6ll_address;
-                        } else
-                                *(p++) = link->network->router_dns[i];
+        r = sd_radv_stop(link->radv);
+        if (r < 0)
+                return r;
 
-                n_dns = p - dns;
-                lifetime_usec = link->network->router_dns_lifetime_usec;
+        r = sd_radv_set_mac(link->radv, &link->hw_addr.ether);
+        if (r < 0)
+                return r;
 
-                goto set_dns;
+        if (restart) {
+                r = sd_radv_start(link->radv);
+                if (r < 0)
+                        return r;
         }
 
-        lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC;
+        return 0;
+}
 
-        r = network_get_ipv6_dns(link->network, &dns, &n_dns);
-        if (r > 0)
-                goto set_dns;
+static int radv_is_ready_to_configure(Link *link) {
+        bool needs_uplink = false;
+        int r;
 
-        if (uplink) {
-                assert(uplink->network);
+        assert(link);
+        assert(link->network);
 
-                r = network_get_ipv6_dns(uplink->network, &dns, &n_dns);
-                if (r > 0)
-                        goto set_dns;
-        }
+        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+                return false;
 
-        return 0;
+        if (in6_addr_is_null(&link->ipv6ll_address))
+                return false;
 
-set_dns:
-        return sd_radv_set_rdnss(link->radv,
-                                 DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC),
-                                 dns, n_dns);
-}
+        if (link->network->router_emit_dns && !link->network->router_dns) {
+                _cleanup_free_ struct in6_addr *dns = NULL;
+                size_t n_dns;
 
-static int radv_set_domains(Link *link, Link *uplink) {
-        OrderedSet *search_domains;
-        usec_t lifetime_usec;
-        _cleanup_free_ char **s = NULL; /* just free() because the strings are owned by the set */
+                r = network_get_ipv6_dns(link->network, &dns, &n_dns);
+                if (r < 0)
+                        return r;
 
-        if (!link->network->router_emit_domains)
-                return 0;
+                needs_uplink = r == 0;
+        }
 
-        search_domains = link->network->router_search_domains;
-        lifetime_usec = link->network->router_dns_lifetime_usec;
+        if (link->network->router_emit_domains &&
+            !link->network->router_search_domains &&
+            !link->network->search_domains)
+                needs_uplink = true;
+
+        if (needs_uplink) {
+                Link *uplink = NULL;
 
-        if (search_domains)
-                goto set_domains;
+                if (radv_find_uplink(link, &uplink) < 0)
+                        return false;
 
-        lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC;
+                if (uplink && !uplink->network)
+                        return false;
+        }
 
-        search_domains = link->network->search_domains;
-        if (search_domains)
-                goto set_domains;
+        return true;
+}
 
-        if (uplink) {
-                assert(uplink->network);
+int request_process_radv(Request *req) {
+        Link *link;
+        int r;
 
-                search_domains = uplink->network->search_domains;
-                if (search_domains)
-                        goto set_domains;
-        }
+        assert(req);
+        assert(req->link);
+        assert(req->type == REQUEST_TYPE_RADV);
 
-        return 0;
+        link = req->link;
 
-set_domains:
-        s = ordered_set_get_strv(search_domains);
-        if (!s)
-                return log_oom();
+        r = radv_is_ready_to_configure(link);
+        if (r <= 0)
+                return r;
 
-        return sd_radv_set_dnssl(link->radv,
-                                 DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC),
-                                 s);
+        r = radv_configure(link);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Advertisement engine: %m");
 
+        if (link_has_carrier(link)) {
+                r = sd_radv_start(link->radv);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Failed to start IPv6 Router Advertisement engine: %m");
+        }
+
+        log_link_debug(link, "IPv6 Router Advertisement engine is configured%s.",
+                       link_has_carrier(link) ? " and started." : "");
+        return 1;
 }
 
-static int radv_find_uplink(Link *link, Link **ret) {
-        assert(link);
+int link_request_radv(Link *link) {
+        int r;
 
-        if (link->network->router_uplink_name)
-                return link_get_by_name(link->manager, link->network->router_uplink_name, ret);
+        assert(link);
 
-        if (link->network->router_uplink_index > 0)
-                return link_get_by_index(link->manager, link->network->router_uplink_index, ret);
+        if (!link_radv_enabled(link))
+                return 0;
 
-        if (link->network->router_uplink_index == UPLINK_INDEX_AUTO) {
-                /* It is not necessary to propagate error in automatic selection. */
-                if (manager_find_uplink(link->manager, AF_INET6, link, ret) < 0)
-                        *ret = NULL;
+        if (link->radv)
                 return 0;
-        }
 
-        *ret = NULL;
+        r = link_queue_request(link, REQUEST_TYPE_RADV, NULL, false, NULL, NULL, NULL);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to request configuring of the IPv6 Router Advertisement engine: %m");
+
+        log_link_debug(link, "Requested configuring of the IPv6 Router Advertisement engine.");
         return 0;
 }
 
-static int radv_configure(Link *link) {
-        uint16_t router_lifetime;
-        Link *uplink = NULL;
-        RoutePrefix *q;
-        Prefix *p;
+int radv_add_prefix(
+                Link *link,
+                const struct in6_addr *prefix,
+                uint8_t prefix_len,
+                uint32_t lifetime_preferred,
+                uint32_t lifetime_valid) {
+
+        _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
         int r;
 
         assert(link);
-        assert(link->network);
 
-        if (link->radv)
-                return -EBUSY;
+        if (!link->radv)
+                return 0;
 
-        r = sd_radv_new(&link->radv);
+        r = sd_radv_prefix_new(&p);
         if (r < 0)
                 return r;
 
-        r = sd_radv_attach_event(link->radv, link->manager->event, 0);
+        r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
         if (r < 0)
                 return r;
 
-        r = sd_radv_set_mac(link->radv, &link->hw_addr.ether);
+        r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred);
         if (r < 0)
                 return r;
 
-        r = sd_radv_set_ifindex(link->radv, link->ifindex);
+        r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid);
         if (r < 0)
                 return r;
 
-        r = sd_radv_set_managed_information(link->radv, link->network->router_managed);
-        if (r < 0)
+        r = sd_radv_add_prefix(link->radv, p, true);
+        if (r < 0 && r != -EEXIST)
                 return r;
 
-        r = sd_radv_set_other_information(link->radv, link->network->router_other_information);
-        if (r < 0)
-                return r;
+        return 0;
+}
 
-        /* a value of UINT16_MAX represents infinity, 0x0 means this host is not a router */
-        if (link->network->router_lifetime_usec == USEC_INFINITY)
-                router_lifetime = UINT16_MAX;
-        else if (link->network->router_lifetime_usec > (UINT16_MAX - 1) * USEC_PER_SEC)
-                router_lifetime = UINT16_MAX - 1;
-        else
-                router_lifetime = DIV_ROUND_UP(link->network->router_lifetime_usec, USEC_PER_SEC);
+void network_drop_invalid_prefixes(Network *network) {
+        Prefix *prefix;
 
-        r = sd_radv_set_router_lifetime(link->radv, router_lifetime);
+        assert(network);
+
+        HASHMAP_FOREACH(prefix, network->prefixes_by_section)
+                if (section_is_invalid(prefix->section))
+                        prefix_free(prefix);
+}
+
+void network_drop_invalid_route_prefixes(Network *network) {
+        RoutePrefix *prefix;
+
+        assert(network);
+
+        HASHMAP_FOREACH(prefix, network->route_prefixes_by_section)
+                if (section_is_invalid(prefix->section))
+                        route_prefix_free(prefix);
+}
+
+int config_parse_prefix(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Network *network = userdata;
+        _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
+        uint8_t prefixlen = 64;
+        union in_addr_union in6addr;
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = prefix_new_static(network, filename, section_line, &p);
         if (r < 0)
-                return r;
+                return log_oom();
 
-        if (router_lifetime > 0) {
-                r = sd_radv_set_preference(link->radv, link->network->router_preference);
-                if (r < 0)
-                        return r;
+        r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue);
+                return 0;
         }
 
-        HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
-                r = sd_radv_add_prefix(link->radv, p->radv_prefix, false);
-                if (r == -EEXIST)
-                        continue;
-                if (r == -ENOEXEC) {
-                        log_link_warning_errno(link, r, "[IPv6Prefix] section configured without Prefix= setting, ignoring section.");
-                        continue;
-                }
-                if (r < 0)
-                        return r;
+        r = sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set radv prefix, ignoring assignment: %s", rvalue);
+                return 0;
         }
 
-        HASHMAP_FOREACH(q, link->network->route_prefixes_by_section) {
-                r = sd_radv_add_route_prefix(link->radv, q->radv_route_prefix, false);
-                if (r == -EEXIST)
-                        continue;
-                if (r < 0)
-                        return r;
-        }
+        p = NULL;
 
-        (void) radv_find_uplink(link, &uplink);
+        return 0;
+}
 
-        r = radv_set_dns(link, uplink);
-        if (r < 0)
-                return log_link_debug_errno(link, r, "Could not set RA DNS: %m");
+int config_parse_prefix_flags(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
-        r = radv_set_domains(link, uplink);
+        Network *network = userdata;
+        _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = prefix_new_static(network, filename, section_line, &p);
         if (r < 0)
-                return log_link_debug_errno(link, r, "Could not set RA Domains: %m");
+                return log_oom();
+
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        if (streq(lvalue, "OnLink"))
+                r = sd_radv_prefix_set_onlink(p->radv_prefix, r);
+        else if (streq(lvalue, "AddressAutoconfiguration"))
+                r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, r);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set %s=, ignoring assignment: %m", lvalue);
+                return 0;
+        }
+
+        p = NULL;
 
         return 0;
 }
 
-int radv_update_mac(Link *link) {
-        bool restart;
+int config_parse_prefix_lifetime(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Network *network = userdata;
+        _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
+        usec_t usec;
         int r;
 
-        assert(link);
-
-        if (!link->radv)
-                return 0;
-
-        restart = sd_radv_is_running(link->radv);
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
 
-        r = sd_radv_stop(link->radv);
+        r = prefix_new_static(network, filename, section_line, &p);
         if (r < 0)
-                return r;
+                return log_oom();
 
-        r = sd_radv_set_mac(link->radv, &link->hw_addr.ether);
-        if (r < 0)
-                return r;
+        r = parse_sec(rvalue, &usec);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue);
+                return 0;
+        }
 
-        if (restart) {
-                r = sd_radv_start(link->radv);
-                if (r < 0)
-                        return r;
+        /* a value of 0xffffffff represents infinity */
+        if (streq(lvalue, "PreferredLifetimeSec"))
+                r = sd_radv_prefix_set_preferred_lifetime(p->radv_prefix,
+                                                          DIV_ROUND_UP(usec, USEC_PER_SEC));
+        else if (streq(lvalue, "ValidLifetimeSec"))
+                r = sd_radv_prefix_set_valid_lifetime(p->radv_prefix,
+                                                      DIV_ROUND_UP(usec, USEC_PER_SEC));
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set %s=, ignoring assignment: %m", lvalue);
+                return 0;
         }
 
+        p = NULL;
+
         return 0;
 }
 
-static int radv_is_ready_to_configure(Link *link) {
-        bool needs_uplink = false;
-        int r;
-
-        assert(link);
-        assert(link->network);
-
-        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
-                return false;
+int config_parse_prefix_assign(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
-        if (in6_addr_is_null(&link->ipv6ll_address))
-                return false;
+        Network *network = userdata;
+        _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
+        int r;
 
-        if (link->network->router_emit_dns && !link->network->router_dns) {
-                _cleanup_free_ struct in6_addr *dns = NULL;
-                size_t n_dns;
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
 
-                r = network_get_ipv6_dns(link->network, &dns, &n_dns);
-                if (r < 0)
-                        return r;
+        r = prefix_new_static(network, filename, section_line, &p);
+        if (r < 0)
+                return log_oom();
 
-                needs_uplink = r == 0;
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse %s=, ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
         }
 
-        if (link->network->router_emit_domains &&
-            !link->network->router_search_domains &&
-            !link->network->search_domains)
-                needs_uplink = true;
-
-        if (needs_uplink) {
-                Link *uplink = NULL;
-
-                if (radv_find_uplink(link, &uplink) < 0)
-                        return false;
-
-                if (uplink && !uplink->network)
-                        return false;
-        }
+        p->assign = r;
+        p = NULL;
 
-        return true;
+        return 0;
 }
 
-int request_process_radv(Request *req) {
-        Link *link;
-        int r;
-
-        assert(req);
-        assert(req->link);
-        assert(req->type == REQUEST_TYPE_RADV);
+int config_parse_prefix_metric(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
-        link = req->link;
+        Network *network = userdata;
+        _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
+        int r;
 
-        r = radv_is_ready_to_configure(link);
-        if (r <= 0)
-                return r;
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
 
-        r = radv_configure(link);
+        r = prefix_new_static(network, filename, section_line, &p);
         if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Advertisement engine: %m");
+                return log_oom();
 
-        if (link_has_carrier(link)) {
-                r = sd_radv_start(link->radv);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "Failed to start IPv6 Router Advertisement engine: %m");
+        r = safe_atou32(rvalue, &p->route_metric);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse %s=, ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
         }
 
-        log_link_debug(link, "IPv6 Router Advertisement engine is configured%s.",
-                       link_has_carrier(link) ? " and started." : "");
-        return 1;
+        TAKE_PTR(p);
+
+        return 0;
 }
 
-int link_request_radv(Link *link) {
+int config_parse_route_prefix(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Network *network = userdata;
+        _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
+        uint8_t prefixlen = 64;
+        union in_addr_union in6addr;
         int r;
 
-        assert(link);
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
 
-        if (!link_radv_enabled(link))
+        r = route_prefix_new_static(network, filename, section_line, &p);
+        if (r < 0)
+                return log_oom();
+
+        r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Route prefix is invalid, ignoring assignment: %s", rvalue);
                 return 0;
+        }
 
-        if (link->radv)
+        r = sd_radv_prefix_set_route_prefix(p->radv_route_prefix, &in6addr.in6, prefixlen);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set route prefix, ignoring assignment: %m");
                 return 0;
+        }
 
-        r = link_queue_request(link, REQUEST_TYPE_RADV, NULL, false, NULL, NULL, NULL);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to request configuring of the IPv6 Router Advertisement engine: %m");
+        p = NULL;
 
-        log_link_debug(link, "Requested configuring of the IPv6 Router Advertisement engine.");
         return 0;
 }
 
-int radv_add_prefix(
-                Link *link,
-                const struct in6_addr *prefix,
-                uint8_t prefix_len,
-                uint32_t lifetime_preferred,
-                uint32_t lifetime_valid) {
+int config_parse_route_prefix_lifetime(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
-        _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
+        Network *network = userdata;
+        _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
+        usec_t usec;
         int r;
 
-        assert(link);
-
-        if (!link->radv)
-                return 0;
-
-        r = sd_radv_prefix_new(&p);
-        if (r < 0)
-                return r;
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
 
-        r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
+        r = route_prefix_new_static(network, filename, section_line, &p);
         if (r < 0)
-                return r;
+                return log_oom();
 
-        r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred);
-        if (r < 0)
-                return r;
+        r = parse_sec(rvalue, &usec);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Route lifetime is invalid, ignoring assignment: %s", rvalue);
+                return 0;
+        }
 
-        r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid);
-        if (r < 0)
-                return r;
+        /* a value of 0xffffffff represents infinity */
+        r = sd_radv_route_prefix_set_lifetime(p->radv_route_prefix, DIV_ROUND_UP(usec, USEC_PER_SEC));
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to set route lifetime, ignoring assignment: %m");
+                return 0;
+        }
 
-        r = sd_radv_add_prefix(link->radv, p, true);
-        if (r < 0 && r != -EEXIST)
-                return r;
+        p = NULL;
 
         return 0;
 }