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