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