diff options
author | Daniel Mack <github@zonque.org> | 2015-11-11 16:03:05 +0100 |
---|---|---|
committer | Daniel Mack <github@zonque.org> | 2015-11-11 16:03:05 +0100 |
commit | 3116c225d2e3c0d8e6b3f4d4a9b48443cc7baf2d (patch) | |
tree | defe1a2bab15d776e52b8ba3d5a1c8744c0ef7bf /src/network | |
parent | a2e6fbf5c047314db036000203b42c6aac1e3511 (diff) | |
parent | 4058d339f5b290ec970e1de0d3b804e4934acc20 (diff) |
Merge pull request #1843 from teg/ndisc
ndisc and dhcpv6
Diffstat (limited to 'src/network')
-rw-r--r-- | src/network/networkd-address.c | 17 | ||||
-rw-r--r-- | src/network/networkd-address.h | 1 | ||||
-rw-r--r-- | src/network/networkd-dhcp4.c | 4 | ||||
-rw-r--r-- | src/network/networkd-dhcp6.c | 95 | ||||
-rw-r--r-- | src/network/networkd-link.c | 140 | ||||
-rw-r--r-- | src/network/networkd-link.h | 13 | ||||
-rw-r--r-- | src/network/networkd-manager.c | 19 | ||||
-rw-r--r-- | src/network/networkd-ndisc.c | 191 | ||||
-rw-r--r-- | src/network/networkd-route.c | 8 | ||||
-rw-r--r-- | src/network/networkd-route.h | 2 |
10 files changed, 372 insertions, 118 deletions
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 8b6acf2e1d..c0562e5788 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -248,6 +248,8 @@ static int address_add_internal(Link *link, Set **addresses, address->family = family; address->in_addr = *in_addr; address->prefixlen = prefixlen; + /* Consider address tentative until we get the real flags from the kernel */ + address->flags = IFA_F_TENTATIVE; r = set_ensure_allocated(addresses, &address_hash_ops); if (r < 0) @@ -327,13 +329,13 @@ static int address_release(Address *address) { int address_update(Address *address, unsigned char flags, unsigned char scope, struct ifa_cacheinfo *cinfo) { bool ready; + int r; assert(address); assert(cinfo); ready = address_is_ready(address); - address->added = true; address->flags = flags; address->scope = scope; address->cinfo = *cinfo; @@ -341,8 +343,17 @@ int address_update(Address *address, unsigned char flags, unsigned char scope, s if (address->link) { link_update_operstate(address->link); - if (!ready && address_is_ready(address)) + if (!ready && address_is_ready(address)) { link_check_ready(address->link); + + if (address->family == AF_INET6 && + in_addr_is_link_local(AF_INET6, &address->in_addr) && + !address->link->ipv6ll_address) { + r = link_ipv6ll_gained(address->link); + if (r < 0) + return r; + } + } } return 0; @@ -769,5 +780,5 @@ int config_parse_label(const char *unit, bool address_is_ready(const Address *a) { assert(a); - return a->added && !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)); + return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)); } diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 0b1f3b688b..4049a23bdc 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -52,7 +52,6 @@ struct Address { union in_addr_union in_addr; union in_addr_union in_addr_peer; - bool added:1; bool ip_masquerade_done:1; LIST_FIELDS(Address, addresses); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index b58fc5808c..b9c60a3c77 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -34,7 +34,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, int r; assert(link); - assert(link->dhcp4_messages); + assert(link->dhcp4_messages > 0); link->dhcp4_messages --; @@ -44,7 +44,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, link_enter_failed(link); } - if (!link->dhcp4_messages) { + if (link->dhcp4_messages == 0) { link->dhcp4_configured = true; link_check_ready(link); } diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index c3332bb1ac..3ec9b84a93 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -165,83 +165,82 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { link_check_ready(link); } -int dhcp6_configure(Link *link, bool inf_req) { - int r, information_request; +int dhcp6_request_address(Link *link) { + int r, inf_req; + bool running; - assert_return(link, -EINVAL); + assert(link); + assert(link->dhcp6_client); - link->dhcp6_configured = false; + r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req); + if (r < 0) + return r; - if (link->dhcp6_client) { - r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &information_request); - if (r < 0) { - log_link_warning_errno(link, r, "Could not get DHCPv6 Information request setting: %m"); - goto error; - } + if (!inf_req) + return 0; - if (information_request && !inf_req) { - r = sd_dhcp6_client_stop(link->dhcp6_client); - if (r < 0) { - log_link_warning_errno(link, r, "Could not stop DHCPv6 while setting Managed mode: %m"); - goto error; - } + r = sd_dhcp6_client_is_running(link->dhcp6_client); + if (r < 0) + return r; + else + running = !!r; - r = sd_dhcp6_client_set_information_request(link->dhcp6_client, false); - if (r < 0) { - log_link_warning_errno(link, r, "Could not unset DHCPv6 Information request: %m"); - goto error; - } + if (running) { + r = sd_dhcp6_client_stop(link->dhcp6_client); + if (r < 0) + return r; + } - } + r = sd_dhcp6_client_set_information_request(link->dhcp6_client, false); + if (r < 0) + return r; + if (running) { r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0 && r != -EALREADY) { - log_link_warning_errno(link, r, "Could not restart DHCPv6: %m"); - goto error; - } + if (r < 0) + return r; + } - if (r == -EALREADY) - link->dhcp6_configured = true; + return 0; +} +int dhcp6_configure(Link *link) { + sd_dhcp6_client *client = NULL; + int r; + + assert(link); + + r = sd_dhcp6_client_new(&client); + if (r < 0) return r; - } - r = sd_dhcp6_client_new(&link->dhcp6_client); + r = sd_dhcp6_client_attach_event(client, NULL, 0); if (r < 0) goto error; - r = sd_dhcp6_client_attach_event(link->dhcp6_client, NULL, 0); + r = sd_dhcp6_client_set_information_request(client, true); if (r < 0) - goto error; + return r; - r = sd_dhcp6_client_set_mac(link->dhcp6_client, + r = sd_dhcp6_client_set_mac(client, (const uint8_t *) &link->mac, sizeof (link->mac), ARPHRD_ETHER); if (r < 0) goto error; - r = sd_dhcp6_client_set_index(link->dhcp6_client, link->ifindex); + r = sd_dhcp6_client_set_index(client, link->ifindex); if (r < 0) goto error; - r = sd_dhcp6_client_set_callback(link->dhcp6_client, dhcp6_handler, - link); + r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link); if (r < 0) goto error; - if (inf_req) { - r = sd_dhcp6_client_set_information_request(link->dhcp6_client, true); - if (r < 0) - goto error; - } - - r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0) - goto error; + link->dhcp6_client = client; - return r; + return 0; - error: - link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); +error: + sd_dhcp6_client_unref(client); return r; } diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 13d2fc6d0d..a7d0908bdd 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -124,6 +124,28 @@ static bool link_ipv6_forward_enabled(Link *link) { return link->network->ip_forward & ADDRESS_FAMILY_IPV6; } +bool link_ipv6_accept_ra_enabled(Link *link) { + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + /* If unset use system default (enabled if local forwarding is disabled. + * disabled if local forwarding is enabled). + * If set, ignore or enforce RA independent of local forwarding state. + */ + if (link->network->ipv6_accept_ra < 0) + /* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */ + return !link_ipv6_forward_enabled(link); + else if (link->network->ipv6_accept_ra > 0) + /* accept RA even if ip_forward is enabled */ + return true; + else + /* ignore RA */ + return false; +} + static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) { if (link->flags & IFF_LOOPBACK) return _IPV6_PRIVACY_EXTENSIONS_INVALID; @@ -485,13 +507,13 @@ static int link_stop_clients(Link *link) { r = log_link_warning_errno(link, r, "Could not stop IPv4 link-local: %m"); } - if(link->ndisc_router_discovery) { - if (link->dhcp6_client) { - k = sd_dhcp6_client_stop(link->dhcp6_client); - if (k < 0) - r = log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m"); - } + if (link->dhcp6_client) { + k = sd_dhcp6_client_stop(link->dhcp6_client); + if (k < 0) + r = log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m"); + } + if (link->ndisc_router_discovery) { k = sd_ndisc_stop(link->ndisc_router_discovery); if (k < 0) r = log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery: %m"); @@ -581,6 +603,10 @@ void link_check_ready(Link *link) { !link->ipv4ll_route) return; + if (link_ipv6ll_enabled(link)) + if (!link->ipv6ll_address) + return; + if ((link_dhcp4_enabled(link) && !link_dhcp6_enabled(link) && !link->dhcp4_configured) || (link_dhcp6_enabled(link) && !link_dhcp4_enabled(link) && @@ -589,6 +615,9 @@ void link_check_ready(Link *link) { !link->dhcp4_configured && !link->dhcp6_configured)) return; + if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured) + return; + SET_FOREACH(a, link->addresses, i) if (!address_is_ready(a)) return; @@ -1213,6 +1242,34 @@ static void lldp_handler(sd_lldp *lldp, int event, void *userdata) { } } +static int link_acquire_ipv6_conf(Link *link) { + int r; + + assert(link); + + if (link_dhcp6_enabled(link)) { + assert(link->dhcp6_client); + + log_link_debug(link, "Acquiring DHCPv6 lease"); + + r = sd_dhcp6_client_start(link->dhcp6_client); + if (r < 0) + return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m"); + } + + if (link_ipv6_accept_ra_enabled(link)) { + assert(link->ndisc_router_discovery); + + log_link_debug(link, "Discovering IPv6 routers"); + + r = sd_ndisc_router_discovery_start(link->ndisc_router_discovery); + if (r < 0) + return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m"); + } + + return 0; +} + static int link_acquire_conf(Link *link) { int r; @@ -1241,16 +1298,6 @@ static int link_acquire_conf(Link *link) { return log_link_warning_errno(link, r, "Could not acquire DHCPv4 lease: %m"); } - if (link_dhcp6_enabled(link)) { - assert(link->ndisc_router_discovery); - - log_link_debug(link, "Discovering IPv6 routers"); - - r = sd_ndisc_router_discovery_start(link->ndisc_router_discovery); - if (r < 0) - return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m"); - } - if (link_lldp_enabled(link)) { assert(link->lldp); @@ -1883,7 +1930,7 @@ static int link_set_ipv6_privacy_extensions(Link *link) { } static int link_set_ipv6_accept_ra(Link *link) { - const char *p = NULL, *v = NULL; + const char *p = NULL; int r; /* Make this a NOP if IPv6 is not available */ @@ -1893,29 +1940,16 @@ static int link_set_ipv6_accept_ra(Link *link) { if (link->flags & IFF_LOOPBACK) return 0; - /* If unset use system default (enabled if local forwarding is disabled. - * disabled if local forwarding is enabled). - * If set, ignore or enforce RA independent of local forwarding state. - */ - if (link->network->ipv6_accept_ra < 0) - /* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */ - v = "1"; - else if (link->network->ipv6_accept_ra > 0) - /* "2" means accept RA even if ip_forward is enabled */ - v = "2"; - else - /* "0" means ignore RA */ - v = "0"; - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/accept_ra"); - r = write_string_file(p, v, 0); + /* we handle router advertisments ourselves, tell the kernel to GTFO */ + r = write_string_file(p, "0", 0); if (r < 0) { /* If the right value is set anyway, don't complain */ - if (verify_one_line_file(p, v) > 0) + if (verify_one_line_file(p, "0") > 0) return 0; - log_link_warning_errno(link, r, "Cannot configure IPv6 accept_ra for interface: %m"); + log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface: %m"); } return 0; @@ -2041,6 +2075,12 @@ static int link_configure(Link *link) { } if (link_dhcp6_enabled(link)) { + r = dhcp6_configure(link); + if (r < 0) + return r; + } + + if (link_ipv6_accept_ra_enabled(link)) { r = ndisc_configure(link); if (r < 0) return r; @@ -2065,6 +2105,12 @@ static int link_configure(Link *link) { r = link_acquire_conf(link); if (r < 0) return r; + + if (link->ipv6ll_address) { + r = link_acquire_ipv6_conf(link); + if (r < 0) + return r; + } } return link_enter_join_netdev(link); @@ -2411,6 +2457,27 @@ failed: return r; } +int link_ipv6ll_gained(Link *link) { + int r; + + assert(link); + + log_link_info(link, "Gained IPv6LL"); + + link->ipv6ll_address = true; + link_check_ready(link); + + if (link->network) { + r = link_acquire_ipv6_conf(link); + if (r < 0) { + link_enter_failed(link); + return r; + } + } + + return 0; +} + static int link_carrier_gained(Link *link) { int r; @@ -2639,9 +2706,8 @@ int link_save(Link *link) { sd_dhcp6_lease *dhcp6_lease = NULL; if (link->dhcp6_client) { - r = sd_dhcp6_client_get_lease(link->dhcp6_client, - &dhcp6_lease); - if (r < 0) + r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease); + if (r < 0 && r != -ENOMSG) log_link_debug(link, "No DHCPv6 lease"); } diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index a22041870e..b564bcbca0 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -95,10 +95,13 @@ struct Link { unsigned dhcp4_messages; bool dhcp4_configured; bool dhcp6_configured; + unsigned ndisc_messages; + bool ndisc_configured; sd_ipv4ll *ipv4ll; - bool ipv4ll_address; - bool ipv4ll_route; + bool ipv4ll_address:1; + bool ipv4ll_route:1; + bool ipv6ll_address:1; bool static_configured; @@ -141,13 +144,16 @@ int link_save(Link *link); int link_carrier_reset(Link *link); bool link_has_carrier(Link *link); +int link_ipv6ll_gained(Link *link); + int link_set_mtu(Link *link, uint32_t mtu); int link_set_hostname(Link *link, const char *hostname); int link_set_timezone(Link *link, const char *timezone); int ipv4ll_configure(Link *link); int dhcp4_configure(Link *link); -int dhcp6_configure(Link *link, bool information_request); +int dhcp6_configure(Link *link); +int dhcp6_request_address(Link *link); int ndisc_configure(Link *link); bool link_lldp_enabled(Link *link); @@ -156,6 +162,7 @@ bool link_ipv6ll_enabled(Link *link); bool link_dhcp4_server_enabled(Link *link); bool link_dhcp4_enabled(Link *link); bool link_dhcp6_enabled(Link *link); +bool link_ipv6_accept_ra_enabled(Link *link); const char* link_state_to_string(LinkState s) _const_; LinkState link_state_from_string(const char *s) _pure_; diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index a5701001c1..42f58fed19 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -577,9 +577,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo); if (r >= 0) { - if (cinfo.ifa_valid == CACHE_INFO_INFINITY_LIFE_TIME) - valid_str = "ever"; - else + if (cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME) valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX, cinfo.ifa_valid * USEC_PER_SEC, USEC_PER_SEC); @@ -590,7 +588,8 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, switch (type) { case RTM_NEWADDR: if (address) - log_link_debug(link, "Updating address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + log_link_debug(link, "Updating address: %s/%u (valid %s%s)", buf, prefixlen, + valid_str ? "for " : "forever", valid_str ?: ""); else { /* An address appeared that we did not request */ r = address_add_foreign(link, family, &in_addr, prefixlen, &address); @@ -598,7 +597,8 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, log_link_warning_errno(link, r, "Failed to add address %s/%u: %m", buf, prefixlen); return 0; } else - log_link_debug(link, "Adding address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + log_link_debug(link, "Adding address: %s/%u (valid %s%s)", buf, prefixlen, + valid_str ? "for " : "forever", valid_str ?: ""); } address_update(address, flags, scope, &cinfo); @@ -608,10 +608,12 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, case RTM_DELADDR: if (address) { - log_link_debug(link, "Removing address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + log_link_debug(link, "Removing address: %s/%u (valid %s%s)", buf, prefixlen, + valid_str ? "for " : "forever", valid_str ?: ""); address_drop(address); } else - log_link_warning(link, "Removing non-existent address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + log_link_warning(link, "Removing non-existent address: %s/%u (valid %s%s)", buf, prefixlen, + valid_str ? "for " : "forever", valid_str ?: ""); break; default: @@ -1091,7 +1093,8 @@ static bool manager_check_idle(void *userdata) { link_ipv4ll_enabled(link) || link_dhcp4_server_enabled(link) || link_dhcp4_enabled(link) || - link_dhcp6_enabled(link)) + link_dhcp6_enabled(link) || + link_ipv6_accept_ra_enabled(link)) return false; } diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 33e692f97f..126f9c0fe9 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -20,14 +20,132 @@ ***/ #include <netinet/ether.h> +#include <netinet/icmp6.h> #include <linux/if.h> #include "sd-ndisc.h" #include "networkd-link.h" -static void ndisc_router_handler(sd_ndisc *nd, int event, void *userdata) { +static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(link); + assert(link->ndisc_messages > 0); + + link->ndisc_messages --; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_error_errno(link, r, "Could not set NDisc route or address: %m"); + link_enter_failed(link); + } + + if (link->ndisc_messages == 0) { + link->ndisc_configured = true; + link_check_ready(link); + } + + return 1; +} + +static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, + unsigned lifetime_preferred, unsigned lifetime_valid, void *userdata) { + _cleanup_address_free_ Address *address = NULL; Link *link = userdata; + usec_t time_now; + int r; + + assert(nd); + assert(link); + assert(link->network); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + r = address_new(&address); + if (r < 0) { + log_link_error_errno(link, r, "Could not allocate address: %m"); + return; + } + + assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0); + + address->family = AF_INET6; + address->in_addr.in6 = *prefix; + if (in_addr_is_null(AF_INET6, (const union in_addr_union *) &link->network->ipv6_token) == 0) + memcpy(&address->in_addr.in6 + 8, &link->network->ipv6_token + 8, 8); + else { + /* see RFC4291 section 2.5.1 */ + address->in_addr.in6.__in6_u.__u6_addr8[8] = link->mac.ether_addr_octet[0]; + address->in_addr.in6.__in6_u.__u6_addr8[8] ^= 1 << 1; + address->in_addr.in6.__in6_u.__u6_addr8[9] = link->mac.ether_addr_octet[1]; + address->in_addr.in6.__in6_u.__u6_addr8[10] = link->mac.ether_addr_octet[2]; + address->in_addr.in6.__in6_u.__u6_addr8[11] = 0xff; + address->in_addr.in6.__in6_u.__u6_addr8[12] = 0xfe; + address->in_addr.in6.__in6_u.__u6_addr8[13] = link->mac.ether_addr_octet[3]; + address->in_addr.in6.__in6_u.__u6_addr8[14] = link->mac.ether_addr_octet[4]; + address->in_addr.in6.__in6_u.__u6_addr8[15] = link->mac.ether_addr_octet[5]; + } + address->prefixlen = prefixlen; + address->flags = IFA_F_NOPREFIXROUTE; + address->cinfo.ifa_prefered = lifetime_preferred; + address->cinfo.ifa_valid = lifetime_valid; + + r = address_configure(address, link, ndisc_netlink_handler, true); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set SLAAC address: %m"); + link_enter_failed(link); + return; + } + + link->ndisc_messages ++; +} + +static void ndisc_prefix_onlink_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, unsigned lifetime, void *userdata) { + _cleanup_route_free_ Route *route = NULL; + Link *link = userdata; + usec_t time_now; + int r; + + assert(nd); + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + r = route_new(&route); + if (r < 0) { + log_link_error_errno(link, r, "Could not allocate route: %m"); + return; + } + + assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0); + + route->family = AF_INET6; + route->table = RT_TABLE_MAIN; + route->protocol = RTPROT_RA; + route->flags = RTM_F_PREFIX; + route->dst.in6 = *prefix; + route->dst_prefixlen = prefixlen; + route->lifetime = time_now + lifetime * USEC_PER_SEC; + + r = route_configure(route, link, ndisc_netlink_handler); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set prefix route: %m"); + link_enter_failed(link); + return; + } + + link->ndisc_messages ++; +} + +static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata) { + _cleanup_route_free_ Route *route = NULL; + Link *link = userdata; + usec_t time_now; + int r; assert(link); assert(link->network); @@ -36,27 +154,64 @@ static void ndisc_router_handler(sd_ndisc *nd, int event, void *userdata) { if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return; - switch(event) { - case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE: + if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) { + if (flags & ND_RA_FLAG_MANAGED) + dhcp6_request_address(link); + + r = sd_dhcp6_client_start(link->dhcp6_client); + if (r < 0 && r != -EALREADY) + log_link_warning_errno(link, r, "Starting DHCPv6 client on NDisc request failed: %m"); + } + + if (!gateway) return; - case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER: - dhcp6_configure(link, true); + r = route_new(&route); + if (r < 0) { + log_link_error_errno(link, r, "Could not allocate route: %m"); + return; + } - break; - case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_TIMEOUT: - case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED: - dhcp6_configure(link, false); + assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0); - break; + route->family = AF_INET6; + route->table = RT_TABLE_MAIN; + route->protocol = RTPROT_RA; + route->pref = pref; + route->gw.in6 = *gateway; + route->lifetime = time_now + lifetime * USEC_PER_SEC; - default: - if (event < 0) - log_link_warning_errno(link, event, "IPv6 Neighbor Discover error: %m"); - else - log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event); + r = route_configure(route, link, ndisc_netlink_handler); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set default route: %m"); + link_enter_failed(link); + return; + } + + link->ndisc_messages ++; +} + +static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) { + Link *link = userdata; + int r; + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + switch (event) { + case SD_NDISC_EVENT_TIMEOUT: + dhcp6_request_address(link); + + r = sd_dhcp6_client_start(link->dhcp6_client); + if (r < 0 && r != -EALREADY) + log_link_warning_errno(link, r, "Starting DHCPv6 client after NDisc timeout failed: %m"); break; + case SD_NDISC_EVENT_STOP: + break; + default: + log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event); } } @@ -82,7 +237,11 @@ int ndisc_configure(Link *link) { return r; r = sd_ndisc_set_callback(link->ndisc_router_discovery, - ndisc_router_handler, link); + ndisc_router_handler, + ndisc_prefix_onlink_handler, + ndisc_prefix_autonomous_handler, + ndisc_handler, + link); return r; } diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index f4bbd06af1..ed06c21160 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -494,10 +494,18 @@ int route_configure(Route *route, Link *link, if (r < 0) return log_error_errno(r, "Could not set scope: %m"); + r = sd_rtnl_message_route_set_flags(req, route->flags); + if (r < 0) + return log_error_errno(r, "Colud not set flags: %m"); + r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority); if (r < 0) return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m"); + r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref); + if (r < 0) + return log_error_errno(r, "Could not append RTA_PREF attribute: %m"); + r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex); if (r < 0) return log_error_errno(r, "Could not append RTA_OIF attribute: %m"); diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index d0a51838ed..b276756674 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -40,6 +40,8 @@ struct Route { unsigned char tos; uint32_t priority; /* note that ip(8) calls this 'metric' */ unsigned char table; + unsigned char pref; + unsigned flags; union in_addr_union gw; union in_addr_union dst; |