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