]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-ndisc.c
network: beef up ipv6 RA support considerably
[thirdparty/systemd.git] / src / network / networkd-ndisc.c
CommitLineData
a13c50e7
TG
1/***
2 This file is part of systemd.
3
4 Copyright (C) 2014 Intel Corporation. All rights reserved.
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
9d96e6c3 20#include <netinet/icmp6.h>
a13c50e7 21
a13c50e7
TG
22#include "sd-ndisc.h"
23
634f0f98 24#include "networkd.h"
1e7a0e21
LP
25#include "networkd-ndisc.h"
26
27#define NDISC_DNSSL_MAX 64U
28#define NDISC_RDNSS_MAX 64U
fe307276 29
3b015d40
TG
30static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
31 _cleanup_link_unref_ Link *link = userdata;
32 int r;
33
34 assert(link);
35 assert(link->ndisc_messages > 0);
36
313cefa1 37 link->ndisc_messages--;
3b015d40
TG
38
39 r = sd_netlink_message_get_errno(m);
40 if (r < 0 && r != -EEXIST) {
41 log_link_error_errno(link, r, "Could not set NDisc route or address: %m");
42 link_enter_failed(link);
43 }
44
45 if (link->ndisc_messages == 0) {
46 link->ndisc_configured = true;
47 link_check_ready(link);
48 }
49
50 return 1;
51}
52
1e7a0e21
LP
53static void ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
54 _cleanup_route_free_ Route *route = NULL;
55 struct in6_addr gateway;
56 uint16_t lifetime;
57 unsigned preference;
3b015d40
TG
58 usec_t time_now;
59 int r;
60
3b015d40 61 assert(link);
1e7a0e21 62 assert(rt);
3b015d40 63
1e7a0e21
LP
64 r = sd_ndisc_router_get_lifetime(rt, &lifetime);
65 if (r < 0) {
66 log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
67 return;
68 }
69 if (lifetime == 0) /* not a default router */
70 return;
71
72 r = sd_ndisc_router_get_address(rt, &gateway);
73 if (r < 0) {
74 log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
75 return;
76 }
77
78 r = sd_ndisc_router_get_preference(rt, &preference);
79 if (r < 0) {
80 log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m");
81 return;
82 }
83
84 r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
85 if (r < 0) {
86 log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
87 return;
88 }
89
90 r = route_new(&route);
91 if (r < 0) {
92 log_link_error_errno(link, r, "Could not allocate route: %m");
93 return;
94 }
95
96 route->family = AF_INET6;
97 route->table = RT_TABLE_MAIN;
98 route->protocol = RTPROT_RA;
99 route->pref = preference;
100 route->gw.in6 = gateway;
101 route->lifetime = time_now + lifetime * USEC_PER_SEC;
102
103 r = route_configure(route, link, ndisc_netlink_handler);
104 if (r < 0) {
105 log_link_warning_errno(link, r, "Could not set default route: %m");
106 link_enter_failed(link);
107 return;
108 }
109
110 link->ndisc_messages++;
111}
112
113static void ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
114 _cleanup_address_free_ Address *address = NULL;
115 uint32_t lifetime_valid, lifetime_preferred;
116 unsigned prefixlen;
117 int r;
118
119 assert(link);
120 assert(rt);
121
122 r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
123 if (r < 0) {
124 log_link_error_errno(link, r, "Failed to get prefix length: %m");
125 return;
126 }
127
128 r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid);
129 if (r < 0) {
130 log_link_error_errno(link, r, "Failed to get prefix valid lifetime: %m");
3b015d40 131 return;
1e7a0e21
LP
132 }
133
134 r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred);
135 if (r < 0) {
136 log_link_error_errno(link, r, "Failed to get prefix preferred lifetime: %m");
137 return;
138 }
3b015d40
TG
139
140 r = address_new(&address);
141 if (r < 0) {
142 log_link_error_errno(link, r, "Could not allocate address: %m");
143 return;
144 }
145
3b015d40 146 address->family = AF_INET6;
1e7a0e21
LP
147 r = sd_ndisc_router_prefix_get_address(rt, &address->in_addr.in6);
148 if (r < 0) {
149 log_link_error_errno(link, r, "Failed to get prefix address: %m");
150 return;
151 }
152
3b015d40 153 if (in_addr_is_null(AF_INET6, (const union in_addr_union *) &link->network->ipv6_token) == 0)
fb84d896 154 memcpy(((char *)&address->in_addr.in6) + 8, ((char *)&link->network->ipv6_token) + 8, 8);
3b015d40 155 else {
fe307276 156 /* see RFC4291 section 2.5.1 */
3a437557
NM
157 address->in_addr.in6.s6_addr[8] = link->mac.ether_addr_octet[0];
158 address->in_addr.in6.s6_addr[8] ^= 1 << 1;
159 address->in_addr.in6.s6_addr[9] = link->mac.ether_addr_octet[1];
160 address->in_addr.in6.s6_addr[10] = link->mac.ether_addr_octet[2];
161 address->in_addr.in6.s6_addr[11] = 0xff;
162 address->in_addr.in6.s6_addr[12] = 0xfe;
163 address->in_addr.in6.s6_addr[13] = link->mac.ether_addr_octet[3];
164 address->in_addr.in6.s6_addr[14] = link->mac.ether_addr_octet[4];
165 address->in_addr.in6.s6_addr[15] = link->mac.ether_addr_octet[5];
3b015d40
TG
166 }
167 address->prefixlen = prefixlen;
f217be19 168 address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
3b015d40
TG
169 address->cinfo.ifa_prefered = lifetime_preferred;
170 address->cinfo.ifa_valid = lifetime_valid;
171
172 r = address_configure(address, link, ndisc_netlink_handler, true);
173 if (r < 0) {
174 log_link_warning_errno(link, r, "Could not set SLAAC address: %m");
175 link_enter_failed(link);
176 return;
177 }
178
313cefa1 179 link->ndisc_messages++;
3b015d40
TG
180}
181
1e7a0e21 182static void ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
3b015d40 183 _cleanup_route_free_ Route *route = NULL;
3b015d40 184 usec_t time_now;
1e7a0e21
LP
185 uint32_t lifetime;
186 unsigned prefixlen;
3b015d40
TG
187 int r;
188
3b015d40 189 assert(link);
1e7a0e21 190 assert(rt);
3b015d40 191
1e7a0e21
LP
192 r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
193 if (r < 0) {
194 log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
3b015d40 195 return;
1e7a0e21
LP
196 }
197
198 r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
199 if (r < 0) {
200 log_link_error_errno(link, r, "Failed to get prefix length: %m");
201 return;
202 }
203
204 r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime);
205 if (r < 0) {
206 log_link_error_errno(link, r, "Failed to get prefix lifetime: %m");
207 return;
208 }
3b015d40
TG
209
210 r = route_new(&route);
211 if (r < 0) {
212 log_link_error_errno(link, r, "Could not allocate route: %m");
213 return;
214 }
215
3b015d40
TG
216 route->family = AF_INET6;
217 route->table = RT_TABLE_MAIN;
218 route->protocol = RTPROT_RA;
219 route->flags = RTM_F_PREFIX;
3b015d40
TG
220 route->dst_prefixlen = prefixlen;
221 route->lifetime = time_now + lifetime * USEC_PER_SEC;
222
1e7a0e21
LP
223 r = sd_ndisc_router_prefix_get_address(rt, &route->dst.in6);
224 if (r < 0) {
225 log_link_error_errno(link, r, "Failed to get prefix address: %m");
226 return;
227 }
228
3b015d40
TG
229 r = route_configure(route, link, ndisc_netlink_handler);
230 if (r < 0) {
231 log_link_warning_errno(link, r, "Could not set prefix route: %m");
232 link_enter_failed(link);
233 return;
234 }
235
313cefa1 236 link->ndisc_messages++;
3b015d40
TG
237}
238
1e7a0e21 239static void ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
3b015d40 240 _cleanup_route_free_ Route *route = NULL;
1e7a0e21
LP
241 struct in6_addr gateway;
242 uint32_t lifetime;
243 unsigned preference, prefixlen;
fe307276 244 usec_t time_now;
7a695d8e 245 int r;
a13c50e7
TG
246
247 assert(link);
a13c50e7 248
1e7a0e21
LP
249 r = sd_ndisc_router_route_get_lifetime(rt, &lifetime);
250 if (r < 0) {
251 log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
252 return;
253 }
254 if (lifetime == 0)
a13c50e7
TG
255 return;
256
1e7a0e21
LP
257 r = sd_ndisc_router_get_address(rt, &gateway);
258 if (r < 0) {
259 log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
260 return;
7a695d8e 261 }
3b015d40 262
1e7a0e21
LP
263 r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen);
264 if (r < 0) {
265 log_link_warning_errno(link, r, "Failed to get route prefix length: %m");
3b015d40 266 return;
1e7a0e21
LP
267 }
268
269 r = sd_ndisc_router_route_get_preference(rt, &preference);
270 if (r < 0) {
271 log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m");
272 return;
273 }
274
275 r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
276 if (r < 0) {
277 log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
278 return;
279 }
3b015d40
TG
280
281 r = route_new(&route);
282 if (r < 0) {
283 log_link_error_errno(link, r, "Could not allocate route: %m");
284 return;
285 }
286
3b015d40
TG
287 route->family = AF_INET6;
288 route->table = RT_TABLE_MAIN;
289 route->protocol = RTPROT_RA;
1e7a0e21
LP
290 route->pref = preference;
291 route->gw.in6 = gateway;
292 route->dst_prefixlen = prefixlen;
3b015d40
TG
293 route->lifetime = time_now + lifetime * USEC_PER_SEC;
294
1e7a0e21
LP
295 r = sd_ndisc_router_route_get_address(rt, &route->dst.in6);
296 if (r < 0) {
297 log_link_error_errno(link, r, "Failed to get route address: %m");
298 return;
299 }
300
3b015d40
TG
301 r = route_configure(route, link, ndisc_netlink_handler);
302 if (r < 0) {
1e7a0e21 303 log_link_warning_errno(link, r, "Could not set additional route: %m");
3b015d40
TG
304 link_enter_failed(link);
305 return;
306 }
307
313cefa1 308 link->ndisc_messages++;
9d96e6c3 309}
a13c50e7 310
1e7a0e21
LP
311static void ndisc_rdnss_hash_func(const void *p, struct siphash *state) {
312 const NDiscRDNSS *x = p;
313
314 siphash24_compress(&x->address, sizeof(x->address), state);
315}
316
317static int ndisc_rdnss_compare_func(const void *_a, const void *_b) {
318 const NDiscRDNSS *a = _a, *b = _b;
319
320 return memcmp(&a->address, &b->address, sizeof(a->address));
321}
322
323static const struct hash_ops ndisc_rdnss_hash_ops = {
324 .hash = ndisc_rdnss_hash_func,
325 .compare = ndisc_rdnss_compare_func
326};
327
328static void ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
329 uint32_t lifetime;
330 const struct in6_addr *a;
331 usec_t time_now;
332 int i, n, r;
333
334 assert(link);
335 assert(rt);
336
337 r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
338 if (r < 0) {
339 log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
340 return;
341 }
342
343 r = sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime);
344 if (r < 0) {
345 log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m");
346 return;
347 }
348
349 n = sd_ndisc_router_rdnss_get_addresses(rt, &a);
350 if (n < 0) {
351 log_link_warning_errno(link, n, "Failed to get RDNSS addresses: %m");
352 return;
353 }
354
355 for (i = 0; i < n; i++) {
356 NDiscRDNSS d = {
357 .address = a[i]
358 }, *x;
359
360 if (lifetime == 0) {
361 (void) set_remove(link->ndisc_rdnss, &d);
362 link_dirty(link);
363 continue;
364 }
365
366 x = set_get(link->ndisc_rdnss, &d);
367 if (x) {
368 x->valid_until = time_now + lifetime * USEC_PER_SEC;
369 continue;
370 }
371
372 ndisc_vacuum(link);
373
374 if (set_size(link->ndisc_rdnss) >= NDISC_RDNSS_MAX) {
375 log_link_warning(link, "Too many RDNSS records per link, ignoring.");
376 continue;
377 }
378
379 r = set_ensure_allocated(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops);
380 if (r < 0) {
381 log_oom();
382 return;
383 }
384
385 x = new0(NDiscRDNSS, 1);
386 if (!x) {
387 log_oom();
388 return;
389 }
390
391 x->address = a[i];
392 x->valid_until = time_now + lifetime * USEC_PER_SEC;
393
394 r = set_put(link->ndisc_rdnss, x);
395 if (r < 0) {
396 free(x);
397 log_oom();
398 return;
399 }
400
401 assert(r > 0);
402 link_dirty(link);
403 }
404}
405
406static void ndisc_dnssl_hash_func(const void *p, struct siphash *state) {
407 const NDiscDNSSL *x = p;
408
409 siphash24_compress(NDISC_DNSSL_DOMAIN(x), strlen(NDISC_DNSSL_DOMAIN(x)), state);
410}
411
412static int ndisc_dnssl_compare_func(const void *_a, const void *_b) {
413 const NDiscDNSSL *a = _a, *b = _b;
414
415 return strcmp(NDISC_DNSSL_DOMAIN(a), NDISC_DNSSL_DOMAIN(b));
416}
417
418static const struct hash_ops ndisc_dnssl_hash_ops = {
419 .hash = ndisc_dnssl_hash_func,
420 .compare = ndisc_dnssl_compare_func
421};
422
423static void ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
424 _cleanup_strv_free_ char **l = NULL;
425 uint32_t lifetime;
426 usec_t time_now;
427 char **i;
428 int r;
429
430 assert(link);
431 assert(rt);
432
433 r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
434 if (r < 0) {
435 log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
436 return;
437 }
438
439 r = sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime);
440 if (r < 0) {
441 log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m");
442 return;
443 }
444
445 r = sd_ndisc_router_dnssl_get_domains(rt, &l);
446 if (r < 0) {
447 log_link_warning_errno(link, r, "Failed to get RDNSS addresses: %m");
448 return;
449 }
450
451 STRV_FOREACH(i, l) {
452 struct {
453 NDiscDNSSL header;
454 char domain[strlen(*i)];
455 } s;
456 NDiscDNSSL *x;
457
458 zero(s.header);
459 strcpy(s.domain, *i);
460
461 if (lifetime == 0) {
462 (void) set_remove(link->ndisc_dnssl, &s);
463 link_dirty(link);
464 continue;
465 }
466
467 x = set_get(link->ndisc_dnssl, &s);
468 if (x) {
469 x->valid_until = time_now + lifetime * USEC_PER_SEC;
470 continue;
471 }
472
473 ndisc_vacuum(link);
474
475 if (set_size(link->ndisc_dnssl) >= NDISC_DNSSL_MAX) {
476 log_link_warning(link, "Too many DNSSL records per link, ignoring.");
477 continue;
478 }
479
480 r = set_ensure_allocated(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops);
481 if (r < 0) {
482 log_oom();
483 return;
484 }
485
486 x = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*i) + 1);
487 if (!x) {
488 log_oom();
489 return;
490 }
491
492 strcpy(NDISC_DNSSL_DOMAIN(x), *i);
493 x->valid_until = time_now + lifetime * USEC_PER_SEC;
494
495 r = set_put(link->ndisc_dnssl, x);
496 if (r < 0) {
497 free(x);
498 log_oom();
499 return;
500 }
501
502 assert(r > 0);
503 link_dirty(link);
504 }
505}
506
507static void ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
508 int r;
509
510 assert(link);
511 assert(rt);
512
513 r = sd_ndisc_router_option_rewind(rt);
514 for (;;) {
515 uint8_t type;
516
517 if (r < 0) {
518 log_link_warning_errno(link, r, "Failed to iterate through options: %m");
519 return;
520 }
521 if (r == 0) /* EOF */
522 break;
523
524 r = sd_ndisc_router_option_get_type(rt, &type);
525 if (r < 0) {
526 log_link_warning_errno(link, r, "Failed to get RA option type: %m");
527 return;
528 }
529
530 switch (type) {
531
532 case SD_NDISC_OPTION_PREFIX_INFORMATION: {
533 uint8_t flags;
534
535 r = sd_ndisc_router_prefix_get_flags(rt, &flags);
536 if (r < 0) {
537 log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m");
538 return;
539 }
540
541 if (flags & ND_OPT_PI_FLAG_ONLINK)
542 ndisc_router_process_onlink_prefix(link, rt);
543 if (flags & ND_OPT_PI_FLAG_AUTO)
544 ndisc_router_process_autonomous_prefix(link, rt);
545
546 break;
547 }
548
549 case SD_NDISC_OPTION_ROUTE_INFORMATION:
550 ndisc_router_process_route(link, rt);
551 break;
552
553 case SD_NDISC_OPTION_RDNSS:
554 ndisc_router_process_rdnss(link, rt);
555 break;
556
557 case SD_NDISC_OPTION_DNSSL:
558 ndisc_router_process_dnssl(link, rt);
559 break;
560 }
561
562 r = sd_ndisc_router_option_next(rt);
563 }
564}
565
566static void ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
567 uint64_t flags;
568 int r;
569
570 assert(link);
571 assert(link->network);
572 assert(link->manager);
573 assert(rt);
574
575 r = sd_ndisc_router_get_flags(rt, &flags);
576 if (r < 0) {
577 log_link_warning_errno(link, r, "Failed to get RA flags: %m");
578 return;
579 }
580
581 if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) {
582 /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
583 r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED));
584 if (r < 0 && r != -EBUSY)
585 log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
586 else
587 log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request");
588 }
589
590 ndisc_router_process_default(link, rt);
591 ndisc_router_process_options(link, rt);
592}
593
594static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) {
9d96e6c3 595 Link *link = userdata;
a13c50e7 596
9d96e6c3 597 assert(link);
a13c50e7 598
9d96e6c3
TG
599 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
600 return;
a13c50e7 601
9d96e6c3 602 switch (event) {
1e7a0e21
LP
603
604 case SD_NDISC_EVENT_ROUTER:
605 ndisc_router_handler(link, rt);
606 break;
607
9d96e6c3 608 case SD_NDISC_EVENT_TIMEOUT:
962b0647
TG
609 link->ndisc_configured = true;
610 link_check_ready(link);
611
9d96e6c3
TG
612 break;
613 default:
614 log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event);
a13c50e7
TG
615 }
616}
617
618int ndisc_configure(Link *link) {
619 int r;
620
1e7a0e21
LP
621 assert(link);
622
623 r = sd_ndisc_new(&link->ndisc);
624 if (r < 0)
625 return r;
a13c50e7 626
1e7a0e21 627 r = sd_ndisc_attach_event(link->ndisc, NULL, 0);
a13c50e7
TG
628 if (r < 0)
629 return r;
630
1e7a0e21 631 r = sd_ndisc_set_mac(link->ndisc, &link->mac);
a13c50e7
TG
632 if (r < 0)
633 return r;
634
1e7a0e21 635 r = sd_ndisc_set_ifindex(link->ndisc, link->ifindex);
a13c50e7
TG
636 if (r < 0)
637 return r;
638
1e7a0e21 639 r = sd_ndisc_set_callback(link->ndisc, ndisc_handler, link);
a13c50e7
TG
640 if (r < 0)
641 return r;
642
1e7a0e21
LP
643 return 0;
644}
645
646void ndisc_vacuum(Link *link) {
647 NDiscRDNSS *r;
648 NDiscDNSSL *d;
649 Iterator i;
650 usec_t time_now;
651
652 assert(link);
653
654 /* Removes all RDNSS and DNSSL entries whose validity time has passed */
655
656 time_now = now(clock_boottime_or_monotonic());
657
658 SET_FOREACH(r, link->ndisc_rdnss, i)
659 if (r->valid_until < time_now) {
660 (void) set_remove(link->ndisc_rdnss, r);
661 link_dirty(link);
662 }
a13c50e7 663
1e7a0e21
LP
664 SET_FOREACH(d, link->ndisc_dnssl, i)
665 if (d->valid_until < time_now) {
666 (void) set_remove(link->ndisc_dnssl, d);
667 link_dirty(link);
668 }
a13c50e7 669}