summaryrefslogtreecommitdiff
path: root/src/network
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2016-06-07 11:13:39 +0200
committerLennart Poettering <lennart@poettering.net>2016-06-07 11:13:39 +0200
commita849538e3bf0097595fc1fd86bcda608d1151b4c (patch)
tree20e5dbc1399719e7e24ac6997adf2428a8cdce51 /src/network
parent82e4eda664d40ef60829e27d84b1610c2f4070cd (diff)
parent1e7a0e21c97ac1bbc743009e5ec8c12bc6200e19 (diff)
Merge pull request #3394 from poettering/triple-tstamp
timestamping improvements and IPv6 RA revamp
Diffstat (limited to 'src/network')
-rw-r--r--src/network/networkd-dhcp6.c15
-rw-r--r--src/network/networkd-link.c67
-rw-r--r--src/network/networkd-link.h7
-rw-r--r--src/network/networkd-ndisc.c534
-rw-r--r--src/network/networkd-ndisc.h39
-rw-r--r--src/network/networkd-network-gperf.gperf2
-rw-r--r--src/network/networkd-network.c5
-rw-r--r--src/network/networkd-network.h3
8 files changed, 592 insertions, 80 deletions
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
index 50721b1c74..15acf56a5f 100644
--- a/src/network/networkd-dhcp6.c
+++ b/src/network/networkd-dhcp6.c
@@ -60,10 +60,15 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
return 1;
}
-static int dhcp6_address_change(Link *link, struct in6_addr *ip6_addr,
- uint32_t lifetime_preferred, uint32_t lifetime_valid) {
- int r;
+static int dhcp6_address_change(
+ Link *link,
+ struct in6_addr *ip6_addr,
+ uint32_t lifetime_preferred,
+ uint32_t lifetime_valid) {
+
_cleanup_address_free_ Address *addr = NULL;
+ char buffer[INET6_ADDRSTRLEN];
+ int r;
r = address_new(&addr);
if (r < 0)
@@ -79,8 +84,8 @@ static int dhcp6_address_change(Link *link, struct in6_addr *ip6_addr,
addr->cinfo.ifa_valid = lifetime_valid;
log_link_info(link,
- "DHCPv6 address "SD_NDISC_ADDRESS_FORMAT_STR"/%d timeout preferred %d valid %d",
- SD_NDISC_ADDRESS_FORMAT_VAL(addr->in_addr.in6),
+ "DHCPv6 address %s/%d timeout preferred %d valid %d",
+ inet_ntop(AF_INET6, &addr->in_addr.in6, buffer, sizeof(buffer)),
addr->prefixlen, lifetime_preferred, lifetime_valid);
r = address_configure(addr, link, dhcp6_address_handler, true);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index ee52b1ce1e..11628339b9 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -28,8 +28,9 @@
#include "fileio.h"
#include "netlink-util.h"
#include "network-internal.h"
-#include "networkd.h"
#include "networkd-lldp-tx.h"
+#include "networkd-ndisc.h"
+#include "networkd.h"
#include "set.h"
#include "socket-util.h"
#include "stdio-util.h"
@@ -504,7 +505,10 @@ static void link_free(Link *link) {
sd_ipv4ll_unref(link->ipv4ll);
sd_dhcp6_client_unref(link->dhcp6_client);
- sd_ndisc_unref(link->ndisc_router_discovery);
+ sd_ndisc_unref(link->ndisc);
+
+ set_free_free(link->ndisc_rdnss);
+ set_free_free(link->ndisc_dnssl);
if (link->manager)
hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex));
@@ -616,8 +620,8 @@ static int link_stop_clients(Link *link) {
r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m");
}
- if (link->ndisc_router_discovery) {
- k = sd_ndisc_stop(link->ndisc_router_discovery);
+ if (link->ndisc) {
+ k = sd_ndisc_stop(link->ndisc);
if (k < 0)
r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m");
}
@@ -1453,11 +1457,11 @@ static int link_acquire_ipv6_conf(Link *link) {
}
if (link_ipv6_accept_ra_enabled(link)) {
- assert(link->ndisc_router_discovery);
+ assert(link->ndisc);
log_link_debug(link, "Discovering IPv6 routers");
- r = sd_ndisc_router_discovery_start(link->ndisc_router_discovery);
+ r = sd_ndisc_start(link->ndisc);
if (r < 0 && r != -EBUSY)
return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m");
}
@@ -2364,7 +2368,11 @@ static int link_configure(Link *link) {
}
if (link_lldp_rx_enabled(link)) {
- r = sd_lldp_new(&link->lldp, link->ifindex);
+ r = sd_lldp_new(&link->lldp);
+ if (r < 0)
+ return r;
+
+ r = sd_lldp_set_ifindex(link->lldp, link->ifindex);
if (r < 0)
return r;
@@ -3083,6 +3091,22 @@ int link_save(Link *link) {
if (space)
fputc(' ', f);
serialize_in6_addrs(f, in6_addrs, r);
+ space = true;
+ }
+ }
+
+ /* Make sure to flush out old entries before we use the NDISC data */
+ ndisc_vacuum(link);
+
+ if (link->network->dhcp_use_dns && link->ndisc_rdnss) {
+ NDiscRDNSS *dd;
+
+ SET_FOREACH(dd, link->ndisc_rdnss, i) {
+ if (space)
+ fputc(' ', f);
+
+ serialize_in6_addrs(f, &dd->address, 1);
+ space = true;
}
}
@@ -3128,7 +3152,6 @@ int link_save(Link *link) {
if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
if (link->dhcp_lease)
(void) sd_dhcp_lease_get_domainname(link->dhcp_lease, &dhcp_domainname);
-
if (dhcp6_lease)
(void) sd_dhcp6_lease_get_domains(dhcp6_lease, &dhcp6_domains);
}
@@ -3136,22 +3159,34 @@ int link_save(Link *link) {
fputs("DOMAINS=", f);
fputstrv(f, link->network->search_domains, NULL, &space);
- if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES && dhcp_domainname)
- fputs_with_space(f, dhcp_domainname, NULL, &space);
+ if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) {
+ NDiscDNSSL *dd;
+
+ if (dhcp_domainname)
+ fputs_with_space(f, dhcp_domainname, NULL, &space);
+ if (dhcp6_domains)
+ fputstrv(f, dhcp6_domains, NULL, &space);
- if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES && dhcp6_domains)
- fputstrv(f, dhcp6_domains, NULL, &space);
+ SET_FOREACH(dd, link->ndisc_dnssl, i)
+ fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space);
+ }
fputc('\n', f);
fputs("ROUTE_DOMAINS=", f);
fputstrv(f, link->network->route_domains, NULL, NULL);
- if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE && dhcp_domainname)
- fputs_with_space(f, dhcp_domainname, NULL, &space);
+ if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE) {
+ NDiscDNSSL *dd;
- if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE && dhcp6_domains)
- fputstrv(f, dhcp6_domains, NULL, &space);
+ if (dhcp_domainname)
+ fputs_with_space(f, dhcp_domainname, NULL, &space);
+ if (dhcp6_domains)
+ fputstrv(f, dhcp6_domains, NULL, &space);
+
+ SET_FOREACH(dd, link->ndisc_dnssl, i)
+ fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space);
+ }
fputc('\n', f);
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 5efefd27d6..7db94e79e8 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -98,6 +98,7 @@ typedef struct Link {
unsigned dhcp4_messages;
bool dhcp4_configured;
bool dhcp6_configured;
+
unsigned ndisc_messages;
bool ndisc_configured;
@@ -111,7 +112,10 @@ typedef struct Link {
sd_dhcp_server *dhcp_server;
- sd_ndisc *ndisc_router_discovery;
+ sd_ndisc *ndisc;
+ Set *ndisc_rdnss;
+ Set *ndisc_dnssl;
+
sd_dhcp6_client *dhcp6_client;
bool rtnl_extended_attrs;
@@ -161,7 +165,6 @@ int ipv4ll_configure(Link *link);
int dhcp4_configure(Link *link);
int dhcp6_configure(Link *link);
int dhcp6_request_address(Link *link, int ir);
-int ndisc_configure(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-ndisc.c b/src/network/networkd-ndisc.c
index a0d4fa77d8..2a1ba2bac7 100644
--- a/src/network/networkd-ndisc.c
+++ b/src/network/networkd-ndisc.c
@@ -17,14 +17,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/ether.h>
#include <netinet/icmp6.h>
-#include <netinet/in.h>
-#include <linux/if.h>
#include "sd-ndisc.h"
#include "networkd.h"
+#include "networkd-ndisc.h"
+
+#define NDISC_DNSSL_MAX 64U
+#define NDISC_RDNSS_MAX 64U
static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
_cleanup_link_unref_ Link *link = userdata;
@@ -49,19 +50,92 @@ static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *
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;
+static void ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
+ _cleanup_route_free_ Route *route = NULL;
+ struct in6_addr gateway;
+ uint16_t lifetime;
+ unsigned preference;
usec_t time_now;
int r;
- assert(nd);
assert(link);
- assert(link->network);
+ assert(rt);
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ r = sd_ndisc_router_get_lifetime(rt, &lifetime);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
+ return;
+ }
+ if (lifetime == 0) /* not a default router */
+ return;
+
+ r = sd_ndisc_router_get_address(rt, &gateway);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
+ return;
+ }
+
+ r = sd_ndisc_router_get_preference(rt, &preference);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m");
+ return;
+ }
+
+ r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
+ return;
+ }
+
+ r = route_new(&route);
+ if (r < 0) {
+ log_link_error_errno(link, r, "Could not allocate route: %m");
+ return;
+ }
+
+ route->family = AF_INET6;
+ route->table = RT_TABLE_MAIN;
+ route->protocol = RTPROT_RA;
+ route->pref = preference;
+ 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_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
+ _cleanup_address_free_ Address *address = NULL;
+ uint32_t lifetime_valid, lifetime_preferred;
+ unsigned prefixlen;
+ int r;
+
+ assert(link);
+ assert(rt);
+
+ r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
+ if (r < 0) {
+ log_link_error_errno(link, r, "Failed to get prefix length: %m");
+ return;
+ }
+
+ r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid);
+ if (r < 0) {
+ log_link_error_errno(link, r, "Failed to get prefix valid lifetime: %m");
return;
+ }
+
+ r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred);
+ if (r < 0) {
+ log_link_error_errno(link, r, "Failed to get prefix preferred lifetime: %m");
+ return;
+ }
r = address_new(&address);
if (r < 0) {
@@ -69,10 +143,13 @@ static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr
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;
+ r = sd_ndisc_router_prefix_get_address(rt, &address->in_addr.in6);
+ if (r < 0) {
+ log_link_error_errno(link, r, "Failed to get prefix address: %m");
+ return;
+ }
+
if (in_addr_is_null(AF_INET6, (const union in_addr_union *) &link->network->ipv6_token) == 0)
memcpy(((char *)&address->in_addr.in6) + 8, ((char *)&link->network->ipv6_token) + 8, 8);
else {
@@ -102,17 +179,33 @@ static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr
link->ndisc_messages++;
}
-static void ndisc_prefix_onlink_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, unsigned lifetime, void *userdata) {
+static void ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
_cleanup_route_free_ Route *route = NULL;
- Link *link = userdata;
usec_t time_now;
+ uint32_t lifetime;
+ unsigned prefixlen;
int r;
- assert(nd);
assert(link);
+ assert(rt);
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
return;
+ }
+
+ r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
+ if (r < 0) {
+ log_link_error_errno(link, r, "Failed to get prefix length: %m");
+ return;
+ }
+
+ r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime);
+ if (r < 0) {
+ log_link_error_errno(link, r, "Failed to get prefix lifetime: %m");
+ return;
+ }
r = route_new(&route);
if (r < 0) {
@@ -120,16 +213,19 @@ static void ndisc_prefix_onlink_handler(sd_ndisc *nd, const struct in6_addr *pre
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 = sd_ndisc_router_prefix_get_address(rt, &route->dst.in6);
+ if (r < 0) {
+ log_link_error_errno(link, r, "Failed to get prefix address: %m");
+ return;
+ }
+
r = route_configure(route, link, ndisc_netlink_handler);
if (r < 0) {
log_link_warning_errno(link, r, "Could not set prefix route: %m");
@@ -140,32 +236,47 @@ static void ndisc_prefix_onlink_handler(sd_ndisc *nd, const struct in6_addr *pre
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) {
+static void ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
_cleanup_route_free_ Route *route = NULL;
- Link *link = userdata;
+ struct in6_addr gateway;
+ uint32_t lifetime;
+ unsigned preference, prefixlen;
usec_t time_now;
int r;
assert(link);
- assert(link->network);
- assert(link->manager);
- assert(link->dhcp6_client);
- assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ r = sd_ndisc_router_route_get_lifetime(rt, &lifetime);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
+ return;
+ }
+ if (lifetime == 0)
return;
- if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) {
- /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
- r = dhcp6_request_address(link, flags & ND_RA_FLAG_MANAGED ? false : true);
- if (r < 0 && r != -EBUSY)
- log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
- else
- log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request");
+ r = sd_ndisc_router_get_address(rt, &gateway);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
+ return;
}
- if (!gateway)
+ r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get route prefix length: %m");
return;
+ }
+
+ r = sd_ndisc_router_route_get_preference(rt, &preference);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m");
+ return;
+ }
+
+ r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
+ return;
+ }
r = route_new(&route);
if (r < 0) {
@@ -173,18 +284,23 @@ static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_a
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->pref = preference;
+ route->gw.in6 = gateway;
+ route->dst_prefixlen = prefixlen;
route->lifetime = time_now + lifetime * USEC_PER_SEC;
+ r = sd_ndisc_router_route_get_address(rt, &route->dst.in6);
+ if (r < 0) {
+ log_link_error_errno(link, r, "Failed to get route address: %m");
+ return;
+ }
+
r = route_configure(route, link, ndisc_netlink_handler);
if (r < 0) {
- log_link_warning_errno(link, r, "Could not set default route: %m");
+ log_link_warning_errno(link, r, "Could not set additional route: %m");
link_enter_failed(link);
return;
}
@@ -192,7 +308,290 @@ static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_a
link->ndisc_messages++;
}
-static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) {
+static void ndisc_rdnss_hash_func(const void *p, struct siphash *state) {
+ const NDiscRDNSS *x = p;
+
+ siphash24_compress(&x->address, sizeof(x->address), state);
+}
+
+static int ndisc_rdnss_compare_func(const void *_a, const void *_b) {
+ const NDiscRDNSS *a = _a, *b = _b;
+
+ return memcmp(&a->address, &b->address, sizeof(a->address));
+}
+
+static const struct hash_ops ndisc_rdnss_hash_ops = {
+ .hash = ndisc_rdnss_hash_func,
+ .compare = ndisc_rdnss_compare_func
+};
+
+static void ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
+ uint32_t lifetime;
+ const struct in6_addr *a;
+ usec_t time_now;
+ int i, n, r;
+
+ assert(link);
+ assert(rt);
+
+ r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
+ return;
+ }
+
+ r = sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m");
+ return;
+ }
+
+ n = sd_ndisc_router_rdnss_get_addresses(rt, &a);
+ if (n < 0) {
+ log_link_warning_errno(link, n, "Failed to get RDNSS addresses: %m");
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ NDiscRDNSS d = {
+ .address = a[i]
+ }, *x;
+
+ if (lifetime == 0) {
+ (void) set_remove(link->ndisc_rdnss, &d);
+ link_dirty(link);
+ continue;
+ }
+
+ x = set_get(link->ndisc_rdnss, &d);
+ if (x) {
+ x->valid_until = time_now + lifetime * USEC_PER_SEC;
+ continue;
+ }
+
+ ndisc_vacuum(link);
+
+ if (set_size(link->ndisc_rdnss) >= NDISC_RDNSS_MAX) {
+ log_link_warning(link, "Too many RDNSS records per link, ignoring.");
+ continue;
+ }
+
+ r = set_ensure_allocated(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops);
+ if (r < 0) {
+ log_oom();
+ return;
+ }
+
+ x = new0(NDiscRDNSS, 1);
+ if (!x) {
+ log_oom();
+ return;
+ }
+
+ x->address = a[i];
+ x->valid_until = time_now + lifetime * USEC_PER_SEC;
+
+ r = set_put(link->ndisc_rdnss, x);
+ if (r < 0) {
+ free(x);
+ log_oom();
+ return;
+ }
+
+ assert(r > 0);
+ link_dirty(link);
+ }
+}
+
+static void ndisc_dnssl_hash_func(const void *p, struct siphash *state) {
+ const NDiscDNSSL *x = p;
+
+ siphash24_compress(NDISC_DNSSL_DOMAIN(x), strlen(NDISC_DNSSL_DOMAIN(x)), state);
+}
+
+static int ndisc_dnssl_compare_func(const void *_a, const void *_b) {
+ const NDiscDNSSL *a = _a, *b = _b;
+
+ return strcmp(NDISC_DNSSL_DOMAIN(a), NDISC_DNSSL_DOMAIN(b));
+}
+
+static const struct hash_ops ndisc_dnssl_hash_ops = {
+ .hash = ndisc_dnssl_hash_func,
+ .compare = ndisc_dnssl_compare_func
+};
+
+static void ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
+ _cleanup_strv_free_ char **l = NULL;
+ uint32_t lifetime;
+ usec_t time_now;
+ char **i;
+ int r;
+
+ assert(link);
+ assert(rt);
+
+ r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
+ return;
+ }
+
+ r = sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m");
+ return;
+ }
+
+ r = sd_ndisc_router_dnssl_get_domains(rt, &l);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get RDNSS addresses: %m");
+ return;
+ }
+
+ STRV_FOREACH(i, l) {
+ struct {
+ NDiscDNSSL header;
+ char domain[strlen(*i)];
+ } s;
+ NDiscDNSSL *x;
+
+ zero(s.header);
+ strcpy(s.domain, *i);
+
+ if (lifetime == 0) {
+ (void) set_remove(link->ndisc_dnssl, &s);
+ link_dirty(link);
+ continue;
+ }
+
+ x = set_get(link->ndisc_dnssl, &s);
+ if (x) {
+ x->valid_until = time_now + lifetime * USEC_PER_SEC;
+ continue;
+ }
+
+ ndisc_vacuum(link);
+
+ if (set_size(link->ndisc_dnssl) >= NDISC_DNSSL_MAX) {
+ log_link_warning(link, "Too many DNSSL records per link, ignoring.");
+ continue;
+ }
+
+ r = set_ensure_allocated(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops);
+ if (r < 0) {
+ log_oom();
+ return;
+ }
+
+ x = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*i) + 1);
+ if (!x) {
+ log_oom();
+ return;
+ }
+
+ strcpy(NDISC_DNSSL_DOMAIN(x), *i);
+ x->valid_until = time_now + lifetime * USEC_PER_SEC;
+
+ r = set_put(link->ndisc_dnssl, x);
+ if (r < 0) {
+ free(x);
+ log_oom();
+ return;
+ }
+
+ assert(r > 0);
+ link_dirty(link);
+ }
+}
+
+static void ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
+ int r;
+
+ assert(link);
+ assert(rt);
+
+ r = sd_ndisc_router_option_rewind(rt);
+ for (;;) {
+ uint8_t type;
+
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to iterate through options: %m");
+ return;
+ }
+ if (r == 0) /* EOF */
+ break;
+
+ r = sd_ndisc_router_option_get_type(rt, &type);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get RA option type: %m");
+ return;
+ }
+
+ switch (type) {
+
+ case SD_NDISC_OPTION_PREFIX_INFORMATION: {
+ uint8_t flags;
+
+ r = sd_ndisc_router_prefix_get_flags(rt, &flags);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m");
+ return;
+ }
+
+ if (flags & ND_OPT_PI_FLAG_ONLINK)
+ ndisc_router_process_onlink_prefix(link, rt);
+ if (flags & ND_OPT_PI_FLAG_AUTO)
+ ndisc_router_process_autonomous_prefix(link, rt);
+
+ break;
+ }
+
+ case SD_NDISC_OPTION_ROUTE_INFORMATION:
+ ndisc_router_process_route(link, rt);
+ break;
+
+ case SD_NDISC_OPTION_RDNSS:
+ ndisc_router_process_rdnss(link, rt);
+ break;
+
+ case SD_NDISC_OPTION_DNSSL:
+ ndisc_router_process_dnssl(link, rt);
+ break;
+ }
+
+ r = sd_ndisc_router_option_next(rt);
+ }
+}
+
+static void ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
+ uint64_t flags;
+ int r;
+
+ assert(link);
+ assert(link->network);
+ assert(link->manager);
+ assert(rt);
+
+ r = sd_ndisc_router_get_flags(rt, &flags);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get RA flags: %m");
+ return;
+ }
+
+ if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) {
+ /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
+ r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED));
+ if (r < 0 && r != -EBUSY)
+ log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
+ else
+ log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request");
+ }
+
+ ndisc_router_process_default(link, rt);
+ ndisc_router_process_options(link, rt);
+}
+
+static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) {
Link *link = userdata;
assert(link);
@@ -201,13 +600,16 @@ static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) {
return;
switch (event) {
+
+ case SD_NDISC_EVENT_ROUTER:
+ ndisc_router_handler(link, rt);
+ break;
+
case SD_NDISC_EVENT_TIMEOUT:
link->ndisc_configured = true;
link_check_ready(link);
break;
- case SD_NDISC_EVENT_STOP:
- break;
default:
log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event);
}
@@ -216,30 +618,52 @@ static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) {
int ndisc_configure(Link *link) {
int r;
- assert_return(link, -EINVAL);
+ assert(link);
+
+ r = sd_ndisc_new(&link->ndisc);
+ if (r < 0)
+ return r;
- r = sd_ndisc_new(&link->ndisc_router_discovery);
+ r = sd_ndisc_attach_event(link->ndisc, NULL, 0);
if (r < 0)
return r;
- r = sd_ndisc_attach_event(link->ndisc_router_discovery, NULL, 0);
+ r = sd_ndisc_set_mac(link->ndisc, &link->mac);
if (r < 0)
return r;
- r = sd_ndisc_set_mac(link->ndisc_router_discovery, &link->mac);
+ r = sd_ndisc_set_ifindex(link->ndisc, link->ifindex);
if (r < 0)
return r;
- r = sd_ndisc_set_ifindex(link->ndisc_router_discovery, link->ifindex);
+ r = sd_ndisc_set_callback(link->ndisc, ndisc_handler, link);
if (r < 0)
return r;
- r = sd_ndisc_set_callback(link->ndisc_router_discovery,
- ndisc_router_handler,
- ndisc_prefix_onlink_handler,
- ndisc_prefix_autonomous_handler,
- ndisc_handler,
- link);
+ return 0;
+}
+
+void ndisc_vacuum(Link *link) {
+ NDiscRDNSS *r;
+ NDiscDNSSL *d;
+ Iterator i;
+ usec_t time_now;
+
+ assert(link);
+
+ /* Removes all RDNSS and DNSSL entries whose validity time has passed */
+
+ time_now = now(clock_boottime_or_monotonic());
+
+ SET_FOREACH(r, link->ndisc_rdnss, i)
+ if (r->valid_until < time_now) {
+ (void) set_remove(link->ndisc_rdnss, r);
+ link_dirty(link);
+ }
- return r;
+ SET_FOREACH(d, link->ndisc_dnssl, i)
+ if (d->valid_until < time_now) {
+ (void) set_remove(link->ndisc_dnssl, d);
+ link_dirty(link);
+ }
}
diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h
new file mode 100644
index 0000000000..2002f55107
--- /dev/null
+++ b/src/network/networkd-ndisc.h
@@ -0,0 +1,39 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "networkd-link.h"
+
+typedef struct NDiscRDNSS {
+ usec_t valid_until;
+ struct in6_addr address;
+} NDiscRDNSS;
+
+typedef struct NDiscDNSSL {
+ usec_t valid_until;
+ /* The domain name follows immediately. */
+} NDiscDNSSL;
+
+static inline char* NDISC_DNSSL_DOMAIN(const NDiscDNSSL *n) {
+ return ((char*) n) + ALIGN(sizeof(NDiscDNSSL));
+}
+
+int ndisc_configure(Link *link);
+void ndisc_vacuum(Link *link);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 03e4e3b39f..c722db55c7 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -89,6 +89,8 @@ DHCP.DUIDRawData, config_parse_duid_rawdata,
DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
DHCP.IAID, config_parse_iaid, 0, offsetof(Network, iaid)
+IPv6AcceptRouterAdvertisements.UseDNS config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns)
+IPv6AcceptRouterAdvertisements.UseDomains config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains)
DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec)
DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec)
DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index dd89b3770c..e961db60a7 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -51,8 +51,8 @@ static int network_load_one(Manager *manager, const char *filename) {
if (!file) {
if (errno == ENOENT)
return 0;
- else
- return -errno;
+
+ return -errno;
}
if (null_or_empty_fd(fileno(file))) {
@@ -134,6 +134,7 @@ static int network_load_one(Manager *manager, const char *filename) {
network->ipv6_hop_limit = -1;
network->duid.type = _DUID_TYPE_INVALID;
network->proxy_arp = -1;
+ network->ipv6_accept_ra_use_dns = true;
r = config_parse(NULL, filename, file,
"Match\0"
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 91099161ce..b54616b838 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -154,6 +154,9 @@ struct Network {
int ipv6_hop_limit;
int proxy_arp;
+ bool ipv6_accept_ra_use_dns;
+ DHCPUseDomains ipv6_accept_ra_use_domains;
+
union in_addr_union ipv6_token;
IPv6PrivacyExtensions ipv6_privacy_extensions;