diff options
author | Lennart Poettering <lennart@poettering.net> | 2016-06-07 11:13:39 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2016-06-07 11:13:39 +0200 |
commit | a849538e3bf0097595fc1fd86bcda608d1151b4c (patch) | |
tree | 20e5dbc1399719e7e24ac6997adf2428a8cdce51 /src/network | |
parent | 82e4eda664d40ef60829e27d84b1610c2f4070cd (diff) | |
parent | 1e7a0e21c97ac1bbc743009e5ec8c12bc6200e19 (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.c | 15 | ||||
-rw-r--r-- | src/network/networkd-link.c | 67 | ||||
-rw-r--r-- | src/network/networkd-link.h | 7 | ||||
-rw-r--r-- | src/network/networkd-ndisc.c | 534 | ||||
-rw-r--r-- | src/network/networkd-ndisc.h | 39 | ||||
-rw-r--r-- | src/network/networkd-network-gperf.gperf | 2 | ||||
-rw-r--r-- | src/network/networkd-network.c | 5 | ||||
-rw-r--r-- | src/network/networkd-network.h | 3 |
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; |