]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-ndisc.c
networkd: ndisc - handle router advertisement in userspace
[thirdparty/systemd.git] / src / libsystemd-network / sd-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 #include <netinet/in.h>
22 #include <netinet/ip6.h>
23 #include <stdbool.h>
24 #include <string.h>
25 #include <sys/ioctl.h>
26
27 #include "sd-ndisc.h"
28
29 #include "alloc-util.h"
30 #include "async.h"
31 #include "icmp6-util.h"
32 #include "in-addr-util.h"
33 #include "list.h"
34 #include "socket-util.h"
35
36 #define NDISC_ROUTER_SOLICITATION_INTERVAL 4 * USEC_PER_SEC
37 #define NDISC_MAX_ROUTER_SOLICITATIONS 3
38
39 enum NDiscState {
40 NDISC_STATE_IDLE,
41 NDISC_STATE_SOLICITATION_SENT,
42 NDISC_STATE_ADVERTISMENT_LISTEN,
43 _NDISC_STATE_MAX,
44 _NDISC_STATE_INVALID = -1,
45 };
46
47 #define IP6_MIN_MTU (unsigned)1280
48 #define ICMP6_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr))
49 #define NDISC_OPT_LEN_UNITS 8
50
51 #define ND_RA_FLAG_PREF 0x18
52 #define ND_RA_FLAG_PREF_LOW 0x03
53 #define ND_RA_FLAG_PREF_MEDIUM 0x0
54 #define ND_RA_FLAG_PREF_HIGH 0x1
55 #define ND_RA_FLAG_PREF_INVALID 0x2
56
57 typedef struct NDiscPrefix NDiscPrefix;
58
59 struct NDiscPrefix {
60 unsigned n_ref;
61
62 sd_ndisc *nd;
63
64 LIST_FIELDS(NDiscPrefix, prefixes);
65
66 uint8_t len;
67 usec_t valid_until;
68 struct in6_addr addr;
69 };
70
71 struct sd_ndisc {
72 unsigned n_ref;
73
74 enum NDiscState state;
75 sd_event *event;
76 int event_priority;
77 int index;
78 struct ether_addr mac_addr;
79 uint32_t mtu;
80 LIST_HEAD(NDiscPrefix, prefixes);
81 int fd;
82 sd_event_source *recv;
83 sd_event_source *timeout;
84 int nd_sent;
85 sd_ndisc_router_callback_t router_callback;
86 sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback;
87 sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback;
88 sd_ndisc_callback_t callback;
89 void *userdata;
90 };
91
92 #define log_ndisc(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "NDisc CLIENT: " fmt, ##__VA_ARGS__)
93
94 static NDiscPrefix *ndisc_prefix_unref(NDiscPrefix *prefix) {
95
96 if (!prefix)
97 return NULL;
98
99 assert(prefix->n_ref > 0);
100 prefix->n_ref--;
101
102 if (prefix->n_ref > 0)
103 return NULL;
104
105 if (prefix->nd)
106 LIST_REMOVE(prefixes, prefix->nd->prefixes, prefix);
107
108 free(prefix);
109
110 return NULL;
111 }
112
113 static int ndisc_prefix_new(sd_ndisc *nd, NDiscPrefix **ret) {
114 _cleanup_free_ NDiscPrefix *prefix = NULL;
115
116 assert(ret);
117
118 prefix = new0(NDiscPrefix, 1);
119 if (!prefix)
120 return -ENOMEM;
121
122 prefix->n_ref = 1;
123 LIST_INIT(prefixes, prefix);
124 prefix->nd = nd;
125
126 *ret = prefix;
127 prefix = NULL;
128
129 return 0;
130 }
131
132 int sd_ndisc_set_callback(sd_ndisc *nd,
133 sd_ndisc_router_callback_t router_callback,
134 sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback,
135 sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback,
136 sd_ndisc_callback_t callback,
137 void *userdata) {
138 assert(nd);
139
140 nd->router_callback = router_callback;
141 nd->prefix_onlink_callback = prefix_onlink_callback;
142 nd->prefix_autonomous_callback = prefix_autonomous_callback;
143 nd->callback = callback;
144 nd->userdata = userdata;
145
146 return 0;
147 }
148
149 int sd_ndisc_set_index(sd_ndisc *nd, int interface_index) {
150 assert(nd);
151 assert(interface_index >= -1);
152
153 nd->index = interface_index;
154
155 return 0;
156 }
157
158 int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr) {
159 assert(nd);
160
161 if (mac_addr)
162 memcpy(&nd->mac_addr, mac_addr, sizeof(nd->mac_addr));
163 else
164 zero(nd->mac_addr);
165
166 return 0;
167
168 }
169
170 int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int priority) {
171 int r;
172
173 assert_return(nd, -EINVAL);
174 assert_return(!nd->event, -EBUSY);
175
176 if (event)
177 nd->event = sd_event_ref(event);
178 else {
179 r = sd_event_default(&nd->event);
180 if (r < 0)
181 return 0;
182 }
183
184 nd->event_priority = priority;
185
186 return 0;
187 }
188
189 int sd_ndisc_detach_event(sd_ndisc *nd) {
190 assert_return(nd, -EINVAL);
191
192 nd->event = sd_event_unref(nd->event);
193
194 return 0;
195 }
196
197 sd_event *sd_ndisc_get_event(sd_ndisc *nd) {
198 assert(nd);
199
200 return nd->event;
201 }
202
203 sd_ndisc *sd_ndisc_ref(sd_ndisc *nd) {
204
205 if (!nd)
206 return NULL;
207
208 assert(nd->n_ref > 0);
209 nd->n_ref++;
210
211 return nd;
212 }
213
214 static int ndisc_init(sd_ndisc *nd) {
215 assert(nd);
216
217 nd->recv = sd_event_source_unref(nd->recv);
218 nd->fd = asynchronous_close(nd->fd);
219 nd->timeout = sd_event_source_unref(nd->timeout);
220
221 return 0;
222 }
223
224 sd_ndisc *sd_ndisc_unref(sd_ndisc *nd) {
225 NDiscPrefix *prefix, *p;
226
227 if (!nd)
228 return NULL;
229
230 assert(nd->n_ref > 0);
231 nd->n_ref--;
232
233 if (nd->n_ref > 0)
234 return NULL;
235
236 ndisc_init(nd);
237 sd_ndisc_detach_event(nd);
238
239 LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes)
240 prefix = ndisc_prefix_unref(prefix);
241
242 free(nd);
243
244 return NULL;
245 }
246
247 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ndisc*, sd_ndisc_unref);
248 #define _cleanup_sd_ndisc_free_ _cleanup_(sd_ndisc_unrefp)
249
250 int sd_ndisc_new(sd_ndisc **ret) {
251 _cleanup_sd_ndisc_free_ sd_ndisc *nd = NULL;
252
253 assert(ret);
254
255 nd = new0(sd_ndisc, 1);
256 if (!nd)
257 return -ENOMEM;
258
259 nd->n_ref = 1;
260
261 nd->index = -1;
262 nd->fd = -1;
263
264 LIST_HEAD_INIT(nd->prefixes);
265
266 *ret = nd;
267 nd = NULL;
268
269 return 0;
270 }
271
272 int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu) {
273 assert_return(nd, -EINVAL);
274 assert_return(mtu, -EINVAL);
275
276 if (nd->mtu == 0)
277 return -ENOMSG;
278
279 *mtu = nd->mtu;
280
281 return 0;
282 }
283
284 static int prefix_match(const struct in6_addr *prefix, uint8_t prefixlen,
285 const struct in6_addr *addr,
286 uint8_t addr_prefixlen) {
287 uint8_t bytes, mask, len;
288
289 assert_return(prefix, -EINVAL);
290 assert_return(addr, -EINVAL);
291
292 len = MIN(prefixlen, addr_prefixlen);
293
294 bytes = len / 8;
295 mask = 0xff << (8 - len % 8);
296
297 if (memcmp(prefix, addr, bytes) != 0 ||
298 (prefix->s6_addr[bytes] & mask) != (addr->s6_addr[bytes] & mask))
299 return -EADDRNOTAVAIL;
300
301 return 0;
302 }
303
304 static int ndisc_prefix_match(sd_ndisc *nd, const struct in6_addr *addr,
305 uint8_t addr_len, NDiscPrefix **result) {
306 NDiscPrefix *prefix, *p;
307 usec_t time_now;
308 int r;
309
310 assert(nd);
311
312 r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
313 if (r < 0)
314 return r;
315
316 LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) {
317 if (prefix->valid_until < time_now) {
318 prefix = ndisc_prefix_unref(prefix);
319
320 continue;
321 }
322
323 if (prefix_match(&prefix->addr, prefix->len, addr, addr_len) >= 0) {
324 *result = prefix;
325 return 0;
326 }
327 }
328
329 return -EADDRNOTAVAIL;
330 }
331
332 static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len,
333 const struct nd_opt_prefix_info *prefix_opt) {
334 NDiscPrefix *prefix;
335 uint32_t lifetime_valid, lifetime_preferred;
336 usec_t time_now;
337 char time_string[FORMAT_TIMESPAN_MAX];
338 int r;
339
340 assert(nd);
341 assert(prefix_opt);
342
343 if (len < prefix_opt->nd_opt_pi_len)
344 return -ENOMSG;
345
346 if (!(prefix_opt->nd_opt_pi_flags_reserved & (ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO)))
347 return 0;
348
349 if (in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &prefix_opt->nd_opt_pi_prefix) > 0)
350 return 0;
351
352 lifetime_valid = be32toh(prefix_opt->nd_opt_pi_valid_time);
353 lifetime_preferred = be32toh(prefix_opt->nd_opt_pi_preferred_time);
354
355 if (lifetime_valid < lifetime_preferred)
356 return 0;
357
358 r = ndisc_prefix_match(nd, &prefix_opt->nd_opt_pi_prefix,
359 prefix_opt->nd_opt_pi_prefix_len, &prefix);
360
361 if (r < 0 && r != -EADDRNOTAVAIL)
362 return r;
363
364 /* if router advertisment prefix valid timeout is zero, the timeout
365 callback will be called immediately to clean up the prefix */
366
367 if (r == -EADDRNOTAVAIL) {
368 r = ndisc_prefix_new(nd, &prefix);
369 if (r < 0)
370 return r;
371
372 prefix->len = prefix_opt->nd_opt_pi_prefix_len;
373
374 memcpy(&prefix->addr, &prefix_opt->nd_opt_pi_prefix,
375 sizeof(prefix->addr));
376
377 log_ndisc(nd, "New prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
378 SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr),
379 prefix->len, lifetime_valid,
380 format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC));
381
382 LIST_PREPEND(prefixes, nd->prefixes, prefix);
383
384 } else {
385 if (prefix->len != prefix_opt->nd_opt_pi_prefix_len) {
386 uint8_t prefixlen;
387
388 prefixlen = MIN(prefix->len, prefix_opt->nd_opt_pi_prefix_len);
389
390 log_ndisc(nd, "Prefix length mismatch %d/%d using %d",
391 prefix->len,
392 prefix_opt->nd_opt_pi_prefix_len,
393 prefixlen);
394
395 prefix->len = prefixlen;
396 }
397
398 log_ndisc(nd, "Update prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
399 SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr),
400 prefix->len, lifetime_valid,
401 format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC));
402 }
403
404 r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
405 if (r < 0)
406 return r;
407
408 prefix->valid_until = time_now + lifetime_valid * USEC_PER_SEC;
409
410 if ((prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) && nd->prefix_onlink_callback)
411 nd->prefix_onlink_callback(nd, &prefix->addr, prefix->len, prefix->valid_until, nd->userdata);
412
413 if ((prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) && nd->prefix_autonomous_callback)
414 nd->prefix_autonomous_callback(nd, &prefix->addr, prefix->len, lifetime_preferred, lifetime_valid,
415 nd->userdata);
416
417 return 0;
418 }
419
420 static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra,
421 ssize_t len) {
422 void *opt;
423 struct nd_opt_hdr *opt_hdr;
424
425 assert_return(nd, -EINVAL);
426 assert_return(ra, -EINVAL);
427
428 len -= sizeof(*ra);
429 if (len < NDISC_OPT_LEN_UNITS) {
430 log_ndisc(nd, "Router Advertisement below minimum length");
431
432 return -ENOMSG;
433 }
434
435 opt = ra + 1;
436 opt_hdr = opt;
437
438 while (len != 0 && len >= opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS) {
439 struct nd_opt_mtu *opt_mtu;
440 uint32_t mtu;
441 struct nd_opt_prefix_info *opt_prefix;
442
443 if (opt_hdr->nd_opt_len == 0)
444 return -ENOMSG;
445
446 switch (opt_hdr->nd_opt_type) {
447 case ND_OPT_MTU:
448 opt_mtu = opt;
449
450 mtu = be32toh(opt_mtu->nd_opt_mtu_mtu);
451
452 if (mtu != nd->mtu) {
453 nd->mtu = MAX(mtu, IP6_MIN_MTU);
454
455 log_ndisc(nd, "Router Advertisement link MTU %d using %d",
456 mtu, nd->mtu);
457 }
458
459 break;
460
461 case ND_OPT_PREFIX_INFORMATION:
462 opt_prefix = opt;
463
464 ndisc_prefix_update(nd, len, opt_prefix);
465
466 break;
467 }
468
469 len -= opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS;
470 opt = (void *)((char *)opt +
471 opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS);
472 opt_hdr = opt;
473 }
474
475 if (len > 0)
476 log_ndisc(nd, "Router Advertisement contains %zd bytes of trailing garbage", len);
477
478 return 0;
479 }
480
481 static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
482 _cleanup_free_ struct nd_router_advert *ra = NULL;
483 sd_ndisc *nd = userdata;
484 int r, buflen = 0, pref, stateful;
485 union sockaddr_union router = {};
486 socklen_t router_len = sizeof(router);
487 unsigned lifetime;
488 ssize_t len;
489
490 assert(s);
491 assert(nd);
492 assert(nd->event);
493
494 r = ioctl(fd, FIONREAD, &buflen);
495 if (r < 0 || buflen <= 0)
496 buflen = ICMP6_RECV_SIZE;
497
498 ra = malloc(buflen);
499 if (!ra)
500 return -ENOMEM;
501
502 len = recvfrom(fd, ra, buflen, 0, &router.sa, &router_len);
503 if (len < 0) {
504 log_ndisc(nd, "Could not receive message from ICMPv6 socket: %m");
505 return 0;
506 } else if (router_len != sizeof(router.in6) && router_len != 0) {
507 log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)router_len);
508 return 0;
509 }
510
511 if (ra->nd_ra_type != ND_ROUTER_ADVERT)
512 return 0;
513
514 if (ra->nd_ra_code != 0)
515 return 0;
516
517 nd->timeout = sd_event_source_unref(nd->timeout);
518
519 nd->state = NDISC_STATE_ADVERTISMENT_LISTEN;
520
521 stateful = ra->nd_ra_flags_reserved & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER);
522 pref = ra->nd_ra_flags_reserved & ND_RA_FLAG_PREF >> 3;
523
524 switch (pref) {
525 case ND_RA_FLAG_PREF_LOW:
526 case ND_RA_FLAG_PREF_HIGH:
527 break;
528 default:
529 pref = ND_RA_FLAG_PREF_MEDIUM;
530 break;
531 }
532
533 lifetime = be16toh(ra->nd_ra_router_lifetime);
534
535 log_ndisc(nd, "Received Router Advertisement: flags %s preference %s lifetime %u sec",
536 stateful & ND_RA_FLAG_MANAGED ? "MANAGED" : stateful & ND_RA_FLAG_OTHER ? "OTHER" : "none",
537 pref == ND_RA_FLAG_PREF_HIGH ? "high" : pref == ND_RA_FLAG_PREF_LOW ? "low" : "medium",
538 lifetime);
539
540 r = ndisc_ra_parse(nd, ra, len);
541 if (r < 0) {
542 log_ndisc(nd, "Could not parse Router Advertisement: %s", strerror(-r));
543 return 0;
544 }
545
546 if (nd->router_callback)
547 nd->router_callback(nd, stateful, router_len != 0 ? &router.in6.sin6_addr : NULL, lifetime, pref, nd->userdata);
548
549 return 0;
550 }
551
552 static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
553 sd_ndisc *nd = userdata;
554 uint64_t time_now, next_timeout;
555 struct ether_addr unset = { };
556 struct ether_addr *addr = NULL;
557 int r;
558
559 assert(s);
560 assert(nd);
561 assert(nd->event);
562
563 nd->timeout = sd_event_source_unref(nd->timeout);
564
565 if (nd->nd_sent >= NDISC_MAX_ROUTER_SOLICITATIONS) {
566 if (nd->callback)
567 nd->callback(nd, SD_NDISC_EVENT_TIMEOUT, nd->userdata);
568 nd->state = NDISC_STATE_ADVERTISMENT_LISTEN;
569 } else {
570 if (memcmp(&nd->mac_addr, &unset, sizeof(struct ether_addr)))
571 addr = &nd->mac_addr;
572
573 r = icmp6_send_router_solicitation(nd->fd, addr);
574 if (r < 0)
575 log_ndisc(nd, "Error sending Router Solicitation");
576 else {
577 nd->state = NDISC_STATE_SOLICITATION_SENT;
578 log_ndisc(nd, "Sent Router Solicitation");
579 }
580
581 nd->nd_sent++;
582
583 assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
584
585 next_timeout = time_now + NDISC_ROUTER_SOLICITATION_INTERVAL;
586
587 r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
588 next_timeout, 0,
589 ndisc_router_solicitation_timeout, nd);
590 if (r < 0) {
591 /* we cannot continue if we are unable to rearm the timer */
592 sd_ndisc_stop(nd);
593 return 0;
594 }
595
596 r = sd_event_source_set_priority(nd->timeout, nd->event_priority);
597 if (r < 0)
598 return 0;
599
600 r = sd_event_source_set_description(nd->timeout, "ndisc-timeout");
601 if (r < 0)
602 return 0;
603 }
604
605 return 0;
606 }
607
608 int sd_ndisc_stop(sd_ndisc *nd) {
609 assert_return(nd, -EINVAL);
610 assert_return(nd->event, -EINVAL);
611
612 log_ndisc(client, "Stop NDisc");
613
614 ndisc_init(nd);
615
616 nd->state = NDISC_STATE_IDLE;
617
618 if (nd->callback)
619 nd->callback(nd, SD_NDISC_EVENT_STOP, nd->userdata);
620
621 return 0;
622 }
623
624 int sd_ndisc_router_discovery_start(sd_ndisc *nd) {
625 int r;
626
627 assert(nd);
628 assert(nd->event);
629
630 if (nd->state != NDISC_STATE_IDLE)
631 return -EINVAL;
632
633 if (nd->index < 1)
634 return -EINVAL;
635
636 r = icmp6_bind_router_solicitation(nd->index);
637 if (r < 0)
638 return r;
639
640 nd->fd = r;
641
642 r = sd_event_add_io(nd->event, &nd->recv, nd->fd, EPOLLIN,
643 ndisc_router_advertisment_recv, nd);
644 if (r < 0)
645 goto error;
646
647 r = sd_event_source_set_priority(nd->recv, nd->event_priority);
648 if (r < 0)
649 goto error;
650
651 r = sd_event_source_set_description(nd->recv, "ndisc-receive-message");
652 if (r < 0)
653 goto error;
654
655 r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
656 0, 0, ndisc_router_solicitation_timeout, nd);
657 if (r < 0)
658 goto error;
659
660 r = sd_event_source_set_priority(nd->timeout, nd->event_priority);
661 if (r < 0)
662 goto error;
663
664 r = sd_event_source_set_description(nd->timeout, "ndisc-timeout");
665 error:
666 if (r < 0)
667 ndisc_init(nd);
668 else
669 log_ndisc(client, "Start Router Solicitation");
670
671 return r;
672 }