summaryrefslogtreecommitdiff
path: root/src/network
diff options
context:
space:
mode:
authorDaniel Mack <github@zonque.org>2015-11-11 16:03:05 +0100
committerDaniel Mack <github@zonque.org>2015-11-11 16:03:05 +0100
commit3116c225d2e3c0d8e6b3f4d4a9b48443cc7baf2d (patch)
treedefe1a2bab15d776e52b8ba3d5a1c8744c0ef7bf /src/network
parenta2e6fbf5c047314db036000203b42c6aac1e3511 (diff)
parent4058d339f5b290ec970e1de0d3b804e4934acc20 (diff)
Merge pull request #1843 from teg/ndisc
ndisc and dhcpv6
Diffstat (limited to 'src/network')
-rw-r--r--src/network/networkd-address.c17
-rw-r--r--src/network/networkd-address.h1
-rw-r--r--src/network/networkd-dhcp4.c4
-rw-r--r--src/network/networkd-dhcp6.c95
-rw-r--r--src/network/networkd-link.c140
-rw-r--r--src/network/networkd-link.h13
-rw-r--r--src/network/networkd-manager.c19
-rw-r--r--src/network/networkd-ndisc.c191
-rw-r--r--src/network/networkd-route.c8
-rw-r--r--src/network/networkd-route.h2
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;