diff options
-rw-r--r-- | src/libsystemd-network/sd-ndisc.c | 11 | ||||
-rw-r--r-- | src/network/networkd-link.c | 26 | ||||
-rw-r--r-- | src/network/networkd-link.h | 2 | ||||
-rw-r--r-- | src/network/networkd-ndisc.c | 148 | ||||
-rw-r--r-- | src/network/networkd-route.c | 8 | ||||
-rw-r--r-- | src/network/networkd-route.h | 2 |
6 files changed, 171 insertions, 26 deletions
diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index de3f26211e..f35b3a33b8 100644 --- a/src/libsystemd-network/sd-ndisc.c +++ b/src/libsystemd-network/sd-ndisc.c @@ -519,15 +519,14 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r nd->state = NDISC_STATE_ADVERTISMENT_LISTEN; stateful = ra->nd_ra_flags_reserved & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER); - switch (ra->nd_ra_flags_reserved & ND_RA_FLAG_PREF >> 3) { + pref = ra->nd_ra_flags_reserved & ND_RA_FLAG_PREF >> 3; + + switch (pref) { case ND_RA_FLAG_PREF_LOW: - pref = -1; - break; case ND_RA_FLAG_PREF_HIGH: - pref = 1; break; default: - pref = 0; + pref = ND_RA_FLAG_PREF_MEDIUM; break; } @@ -535,7 +534,7 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r log_ndisc(nd, "Received Router Advertisement: flags %s preference %s lifetime %u sec", stateful & ND_RA_FLAG_MANAGED ? "MANAGED" : stateful & ND_RA_FLAG_OTHER ? "OTHER" : "none", - pref == 1 ? "high" : pref == -1 ? "low" : "medium", + pref == ND_RA_FLAG_PREF_HIGH ? "high" : pref == ND_RA_FLAG_PREF_LOW ? "low" : "medium", lifetime); r = ndisc_ra_parse(nd, ra, len); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 3ca9ceb114..19dff893ac 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -611,6 +611,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; @@ -1915,7 +1918,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 */ @@ -1925,29 +1928,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; diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index f453665655..440e33227a 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -95,6 +95,8 @@ struct Link { unsigned dhcp4_messages; bool dhcp4_configured; bool dhcp6_configured; + unsigned ndisc_messages; + bool ndisc_configured; sd_ipv4ll *ipv4ll; bool ipv4ll_address; diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 68277d6ad4..126f9c0fe9 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -27,12 +27,129 @@ #include "networkd-link.h" +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); + assert(link->manager); if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return; @@ -45,6 +162,33 @@ static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_a if (r < 0 && r != -EALREADY) log_link_warning_errno(link, r, "Starting DHCPv6 client on NDisc request failed: %m"); } + + if (!gateway) + 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->pref = pref; + route->gw.in6 = *gateway; + 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 default route: %m"); + link_enter_failed(link); + return; + } + + link->ndisc_messages ++; } static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) { @@ -94,8 +238,8 @@ int ndisc_configure(Link *link) { r = sd_ndisc_set_callback(link->ndisc_router_discovery, ndisc_router_handler, - NULL, - NULL, + ndisc_prefix_onlink_handler, + ndisc_prefix_autonomous_handler, ndisc_handler, link); 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; |