chiark / gitweb /
Add missing includes in header files
[elogind.git] / src / libsystemd-network / sd-icmp6-nd.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/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 static 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
91 static 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
109 static void icmp6_nd_notify(sd_icmp6_nd *nd, int event)
110 {
111         if (nd->callback)
112                 nd->callback(nd, event, nd->userdata);
113 }
114
115 int 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
125 int 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
134 int 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
140                 zero(nd->mac_addr);
141
142         return 0;
143
144 }
145
146 int 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
165 int 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
173 sd_event *sd_icmp6_nd_get_event(sd_icmp6_nd *nd) {
174         assert(nd);
175
176         return nd->event;
177 }
178
179 sd_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
187 static 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
197 sd_icmp6_nd *sd_icmp6_nd_unref(sd_icmp6_nd *nd) {
198         if (nd && REFCNT_DEC(nd->n_ref) == 0) {
199                 ICMP6Prefix *prefix, *p;
200
201                 icmp6_nd_init(nd);
202                 sd_icmp6_nd_detach_event(nd);
203
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
210                 free(nd);
211         }
212
213         return NULL;
214 }
215
216 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 int 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;
231         nd->fd = -1;
232
233         LIST_HEAD_INIT(nd->prefixes);
234
235         *ret = nd;
236         nd = NULL;
237
238         return 0;
239 }
240
241 int 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
253 static 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
270                 nd->expired_prefix = prefix;
271                 icmp6_nd_notify(nd,
272                                 ICMP6_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED);
273                 nd->expired_prefix = NULL;
274
275                 prefix = icmp6_prefix_unref(prefix);
276
277                 break;
278         }
279
280         return 0;
281 }
282
283 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         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
311 error:
312         if (r < 0)
313                 prefix->timeout_valid =
314                         sd_event_source_unref(prefix->timeout_valid);
315
316         return r;
317 }
318
319 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         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
339 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         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
354 int 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
360 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         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
379 int 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
395 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         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
467 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         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) {
486                 struct nd_opt_mtu *opt_mtu;
487                 uint32_t mtu;
488                 struct nd_opt_prefix_info *opt_prefix;
489
490                 if (opt_hdr->nd_opt_len == 0)
491                         return -ENOMSG;
492
493                 switch (opt_hdr->nd_opt_type) {
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;
507
508                 case ND_OPT_PREFIX_INFORMATION:
509                         opt_prefix = opt;
510
511                         icmp6_ra_prefix_update(nd, len, opt_prefix);
512
513                         break;
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
528 static int icmp6_router_advertisment_recv(sd_event_source *s, int fd,
529                                           uint32_t revents, void *userdata)
530 {
531         sd_icmp6_nd *nd = userdata;
532         int r, buflen = 0;
533         ssize_t len;
534         _cleanup_free_ struct nd_router_advert *ra = NULL;
535         int event = ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE;
536
537         assert(s);
538         assert(nd);
539         assert(nd->event);
540
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");
552                 return 0;
553         }
554
555         if (ra->nd_ra_type != ND_ROUTER_ADVERT)
556                 return 0;
557
558         if (ra->nd_ra_code != 0)
559                 return 0;
560
561         nd->timeout = sd_event_source_unref(nd->timeout);
562
563         nd->state = ICMP6_ROUTER_ADVERTISMENT_LISTEN;
564
565         if (ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER )
566                 event = ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER;
567
568         if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
569                 event = ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED;
570
571         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         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         }
583
584         icmp6_nd_notify(nd, event);
585
586         return 0;
587 }
588
589 static 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
621                 r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
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
629                 r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
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                 }
643
644                 r = sd_event_source_set_description(nd->timeout, "icmp6-timeout");
645                 if (r < 0) {
646                         icmp6_nd_notify(nd, r);
647                         return 0;
648                 }
649         }
650
651         return 0;
652 }
653
654 int 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
667 int 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
694         r = sd_event_source_set_description(nd->recv, "icmp6-receive-message");
695         if (r < 0)
696                 goto error;
697
698         r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
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);
704         if (r < 0)
705                 goto error;
706
707         r = sd_event_source_set_description(nd->timeout, "icmp6-timeout");
708 error:
709         if (r < 0)
710                 icmp6_nd_init(nd);
711         else
712                 log_icmp6_nd(client, "Start Router Solicitation");
713
714         return r;
715 }