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