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