]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
e3169126 | 2 | /*** |
810adae9 | 3 | Copyright © 2014 Intel Corporation. All rights reserved. |
e3169126 PF |
4 | ***/ |
5 | ||
6 | #include <netinet/icmp6.h> | |
07630cea | 7 | #include <netinet/in.h> |
e3169126 | 8 | |
07630cea LP |
9 | #include "sd-ndisc.h" |
10 | ||
b5efdb8a | 11 | #include "alloc-util.h" |
349d51e7 | 12 | #include "ether-addr-util.h" |
ff4b0321 | 13 | #include "event-util.h" |
1e7a0e21 | 14 | #include "fd-util.h" |
07630cea | 15 | #include "icmp6-util.h" |
9d96e6c3 | 16 | #include "in-addr-util.h" |
0a970718 | 17 | #include "memory-util.h" |
1e7a0e21 | 18 | #include "ndisc-internal.h" |
696eb2b8 | 19 | #include "ndisc-neighbor-internal.h" |
44e8cf30 | 20 | #include "ndisc-redirect-internal.h" |
ca34b434 | 21 | #include "ndisc-router-internal.h" |
61a9fa8f | 22 | #include "network-common.h" |
1bd6f895 | 23 | #include "random-util.h" |
5cdf13c7 | 24 | #include "set.h" |
940367a0 | 25 | #include "socket-util.h" |
a2dcda32 | 26 | #include "string-table.h" |
d7fa4380 | 27 | #include "string-util.h" |
e3169126 | 28 | |
1bd6f895 | 29 | #define NDISC_TIMEOUT_NO_RA_USEC (NDISC_ROUTER_SOLICITATION_INTERVAL * NDISC_MAX_ROUTER_SOLICITATIONS) |
e3169126 | 30 | |
a2dcda32 | 31 | static const char * const ndisc_event_table[_SD_NDISC_EVENT_MAX] = { |
696eb2b8 YW |
32 | [SD_NDISC_EVENT_TIMEOUT] = "timeout", |
33 | [SD_NDISC_EVENT_ROUTER] = "router", | |
34 | [SD_NDISC_EVENT_NEIGHBOR] = "neighbor", | |
44e8cf30 | 35 | [SD_NDISC_EVENT_REDIRECT] = "redirect", |
a2dcda32 YW |
36 | }; |
37 | ||
2324fd3a | 38 | DEFINE_STRING_TABLE_LOOKUP(ndisc_event, sd_ndisc_event_t); |
a2dcda32 | 39 | |
28eef158 | 40 | static void ndisc_callback(sd_ndisc *ndisc, sd_ndisc_event_t event, void *message) { |
1e7a0e21 | 41 | assert(ndisc); |
a2dcda32 | 42 | assert(event >= 0 && event < _SD_NDISC_EVENT_MAX); |
e3169126 | 43 | |
35388783 YW |
44 | if (!ndisc->callback) |
45 | return (void) log_ndisc(ndisc, "Received '%s' event.", ndisc_event_to_string(event)); | |
9d96e6c3 | 46 | |
35388783 | 47 | log_ndisc(ndisc, "Invoking callback for '%s' event.", ndisc_event_to_string(event)); |
28eef158 | 48 | ndisc->callback(ndisc, event, message, ndisc->userdata); |
5624c480 PF |
49 | } |
50 | ||
787e71e4 YW |
51 | int sd_ndisc_is_running(sd_ndisc *nd) { |
52 | if (!nd) | |
53 | return false; | |
54 | ||
55 | return sd_event_source_get_enabled(nd->recv_event_source, NULL) > 0; | |
56 | } | |
57 | ||
17347053 | 58 | int sd_ndisc_set_callback( |
a1140666 | 59 | sd_ndisc *nd, |
a1140666 LP |
60 | sd_ndisc_callback_t callback, |
61 | void *userdata) { | |
62 | ||
63 | assert_return(nd, -EINVAL); | |
e3169126 PF |
64 | |
65 | nd->callback = callback; | |
66 | nd->userdata = userdata; | |
67 | ||
68 | return 0; | |
69 | } | |
70 | ||
17347053 | 71 | int sd_ndisc_set_ifindex(sd_ndisc *nd, int ifindex) { |
2f8e7633 LP |
72 | assert_return(nd, -EINVAL); |
73 | assert_return(ifindex > 0, -EINVAL); | |
787e71e4 | 74 | assert_return(!sd_ndisc_is_running(nd), -EBUSY); |
e3169126 | 75 | |
2f8e7633 | 76 | nd->ifindex = ifindex; |
e3169126 PF |
77 | return 0; |
78 | } | |
79 | ||
61a9fa8f YW |
80 | int sd_ndisc_set_ifname(sd_ndisc *nd, const char *ifname) { |
81 | assert_return(nd, -EINVAL); | |
82 | assert_return(ifname, -EINVAL); | |
83 | ||
84 | if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE)) | |
85 | return -EINVAL; | |
86 | ||
87 | return free_and_strdup(&nd->ifname, ifname); | |
88 | } | |
89 | ||
5977b71f YW |
90 | int sd_ndisc_get_ifname(sd_ndisc *nd, const char **ret) { |
91 | int r; | |
92 | ||
93 | assert_return(nd, -EINVAL); | |
61a9fa8f | 94 | |
5977b71f YW |
95 | r = get_ifname(nd->ifindex, &nd->ifname); |
96 | if (r < 0) | |
97 | return r; | |
98 | ||
99 | if (ret) | |
100 | *ret = nd->ifname; | |
101 | ||
102 | return 0; | |
61a9fa8f YW |
103 | } |
104 | ||
25413fbf YW |
105 | int sd_ndisc_set_link_local_address(sd_ndisc *nd, const struct in6_addr *addr) { |
106 | assert_return(nd, -EINVAL); | |
107 | assert_return(!addr || in6_addr_is_link_local(addr), -EINVAL); | |
108 | ||
109 | if (addr) | |
110 | nd->link_local_addr = *addr; | |
111 | else | |
112 | zero(nd->link_local_addr); | |
113 | ||
114 | return 0; | |
115 | } | |
116 | ||
17347053 | 117 | int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr) { |
a1140666 | 118 | assert_return(nd, -EINVAL); |
e3169126 PF |
119 | |
120 | if (mac_addr) | |
1e7a0e21 | 121 | nd->mac_addr = *mac_addr; |
e3169126 | 122 | else |
eccaf899 | 123 | zero(nd->mac_addr); |
e3169126 PF |
124 | |
125 | return 0; | |
e3169126 PF |
126 | } |
127 | ||
17347053 | 128 | int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority) { |
e3169126 PF |
129 | int r; |
130 | ||
131 | assert_return(nd, -EINVAL); | |
787e71e4 | 132 | assert_return(!sd_ndisc_is_running(nd), -EBUSY); |
e3169126 PF |
133 | assert_return(!nd->event, -EBUSY); |
134 | ||
135 | if (event) | |
136 | nd->event = sd_event_ref(event); | |
137 | else { | |
138 | r = sd_event_default(&nd->event); | |
139 | if (r < 0) | |
140 | return 0; | |
141 | } | |
142 | ||
143 | nd->event_priority = priority; | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
17347053 | 148 | int sd_ndisc_detach_event(sd_ndisc *nd) { |
1e7a0e21 | 149 | |
e3169126 | 150 | assert_return(nd, -EINVAL); |
787e71e4 | 151 | assert_return(!sd_ndisc_is_running(nd), -EBUSY); |
e3169126 PF |
152 | |
153 | nd->event = sd_event_unref(nd->event); | |
e3169126 PF |
154 | return 0; |
155 | } | |
156 | ||
17347053 | 157 | sd_event *sd_ndisc_get_event(sd_ndisc *nd) { |
a1140666 | 158 | assert_return(nd, NULL); |
e3169126 PF |
159 | |
160 | return nd->event; | |
161 | } | |
162 | ||
165ad41b | 163 | static void ndisc_reset(sd_ndisc *nd) { |
e3169126 PF |
164 | assert(nd); |
165 | ||
ff4b0321 YW |
166 | (void) event_source_disable(nd->timeout_event_source); |
167 | (void) event_source_disable(nd->timeout_no_ra); | |
1bd6f895 | 168 | nd->retransmit_time = 0; |
eb2f7502 | 169 | nd->recv_event_source = sd_event_source_disable_unref(nd->recv_event_source); |
1e7a0e21 | 170 | nd->fd = safe_close(nd->fd); |
e3169126 PF |
171 | } |
172 | ||
8301aa0b YW |
173 | static sd_ndisc *ndisc_free(sd_ndisc *nd) { |
174 | assert(nd); | |
e3169126 | 175 | |
5c4c338a | 176 | ndisc_reset(nd); |
eb2f7502 YW |
177 | |
178 | sd_event_source_unref(nd->timeout_event_source); | |
179 | sd_event_source_unref(nd->timeout_no_ra); | |
4d7b83da | 180 | sd_ndisc_detach_event(nd); |
eb2f7502 | 181 | |
61a9fa8f | 182 | free(nd->ifname); |
6b430fdb | 183 | return mfree(nd); |
e3169126 PF |
184 | } |
185 | ||
8301aa0b YW |
186 | DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc, sd_ndisc, ndisc_free); |
187 | ||
17347053 | 188 | int sd_ndisc_new(sd_ndisc **ret) { |
4afd3348 | 189 | _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL; |
e3169126 | 190 | |
a1140666 | 191 | assert_return(ret, -EINVAL); |
e3169126 | 192 | |
144faa8e | 193 | nd = new(sd_ndisc, 1); |
e3169126 PF |
194 | if (!nd) |
195 | return -ENOMEM; | |
196 | ||
144faa8e YW |
197 | *nd = (sd_ndisc) { |
198 | .n_ref = 1, | |
254d1313 | 199 | .fd = -EBADF, |
144faa8e | 200 | }; |
e3169126 | 201 | |
1cc6c93a | 202 | *ret = TAKE_PTR(nd); |
e3169126 PF |
203 | |
204 | return 0; | |
205 | } | |
206 | ||
c34cb1d6 YW |
207 | static int ndisc_handle_router(sd_ndisc *nd, ICMP6Packet *packet) { |
208 | _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL; | |
f6e0ce66 | 209 | int r; |
d77bde34 | 210 | |
f6e0ce66 | 211 | assert(nd); |
c34cb1d6 YW |
212 | assert(packet); |
213 | ||
214 | rt = ndisc_router_new(packet); | |
215 | if (!rt) | |
216 | return -ENOMEM; | |
d77bde34 | 217 | |
35388783 | 218 | r = ndisc_router_parse(nd, rt); |
f6e0ce66 | 219 | if (r < 0) |
ab8a8a4e | 220 | return r; |
09667885 | 221 | |
e7cb8047 | 222 | (void) event_source_disable(nd->timeout_event_source); |
31db4c1b | 223 | (void) event_source_disable(nd->timeout_no_ra); |
e7cb8047 | 224 | |
238ed432 YW |
225 | if (DEBUG_LOGGING) { |
226 | _cleanup_free_ char *s = NULL; | |
227 | struct in6_addr a; | |
228 | uint64_t flags; | |
229 | uint8_t pref; | |
230 | usec_t lifetime; | |
231 | ||
232 | r = sd_ndisc_router_get_sender_address(rt, &a); | |
233 | if (r < 0) | |
234 | return r; | |
235 | ||
236 | r = sd_ndisc_router_get_flags(rt, &flags); | |
237 | if (r < 0) | |
238 | return r; | |
239 | ||
240 | r = ndisc_router_flags_to_string(flags, &s); | |
241 | if (r < 0) | |
242 | return r; | |
243 | ||
244 | r = sd_ndisc_router_get_preference(rt, &pref); | |
245 | if (r < 0) | |
246 | return r; | |
247 | ||
248 | r = sd_ndisc_router_get_lifetime(rt, &lifetime); | |
249 | if (r < 0) | |
250 | return r; | |
251 | ||
a925620f | 252 | log_ndisc(nd, "Received Router Advertisement from %s: flags=0x%0*"PRIx64"(%s), preference=%s, lifetime=%s", |
238ed432 YW |
253 | IN6_ADDR_TO_STRING(&a), |
254 | flags & UINT64_C(0x00ffffffffffff00) ? 14 : 2, flags, /* suppress too many zeros if no extension */ | |
a925620f | 255 | s ?: "none", |
238ed432 YW |
256 | ndisc_router_preference_to_string(pref), |
257 | FORMAT_TIMESPAN(lifetime, USEC_PER_SEC)); | |
258 | } | |
09667885 | 259 | |
1e7a0e21 | 260 | ndisc_callback(nd, SD_NDISC_EVENT_ROUTER, rt); |
09667885 PF |
261 | return 0; |
262 | } | |
263 | ||
696eb2b8 YW |
264 | static int ndisc_handle_neighbor(sd_ndisc *nd, ICMP6Packet *packet) { |
265 | _cleanup_(sd_ndisc_neighbor_unrefp) sd_ndisc_neighbor *na = NULL; | |
696eb2b8 YW |
266 | int r; |
267 | ||
268 | assert(nd); | |
269 | assert(packet); | |
270 | ||
271 | na = ndisc_neighbor_new(packet); | |
272 | if (!na) | |
273 | return -ENOMEM; | |
274 | ||
275 | r = ndisc_neighbor_parse(nd, na); | |
276 | if (r < 0) | |
277 | return r; | |
278 | ||
238ed432 YW |
279 | if (DEBUG_LOGGING) { |
280 | struct in6_addr a; | |
696eb2b8 | 281 | |
238ed432 YW |
282 | r = sd_ndisc_neighbor_get_sender_address(na, &a); |
283 | if (r < 0) | |
284 | return r; | |
285 | ||
286 | log_ndisc(nd, "Received Neighbor Advertisement from %s: Router=%s, Solicited=%s, Override=%s", | |
287 | IN6_ADDR_TO_STRING(&a), | |
288 | yes_no(sd_ndisc_neighbor_is_router(na) > 0), | |
289 | yes_no(sd_ndisc_neighbor_is_solicited(na) > 0), | |
290 | yes_no(sd_ndisc_neighbor_is_override(na) > 0)); | |
291 | } | |
696eb2b8 YW |
292 | |
293 | ndisc_callback(nd, SD_NDISC_EVENT_NEIGHBOR, na); | |
294 | return 0; | |
295 | } | |
296 | ||
44e8cf30 YW |
297 | static int ndisc_handle_redirect(sd_ndisc *nd, ICMP6Packet *packet) { |
298 | _cleanup_(sd_ndisc_redirect_unrefp) sd_ndisc_redirect *rd = NULL; | |
44e8cf30 YW |
299 | int r; |
300 | ||
301 | assert(nd); | |
302 | assert(packet); | |
303 | ||
304 | rd = ndisc_redirect_new(packet); | |
305 | if (!rd) | |
306 | return -ENOMEM; | |
307 | ||
308 | r = ndisc_redirect_parse(nd, rd); | |
309 | if (r < 0) | |
310 | return r; | |
311 | ||
238ed432 YW |
312 | if (DEBUG_LOGGING) { |
313 | struct in6_addr sender, target, dest; | |
314 | ||
315 | r = sd_ndisc_redirect_get_sender_address(rd, &sender); | |
316 | if (r < 0) | |
317 | return r; | |
318 | ||
319 | r = sd_ndisc_redirect_get_target_address(rd, &target); | |
320 | if (r < 0) | |
321 | return r; | |
44e8cf30 | 322 | |
238ed432 YW |
323 | r = sd_ndisc_redirect_get_destination_address(rd, &dest); |
324 | if (r < 0) | |
325 | return r; | |
326 | ||
327 | log_ndisc(nd, "Received Redirect message from %s: Target=%s, Destination=%s", | |
328 | IN6_ADDR_TO_STRING(&sender), | |
329 | IN6_ADDR_TO_STRING(&target), | |
330 | IN6_ADDR_TO_STRING(&dest)); | |
331 | } | |
44e8cf30 YW |
332 | |
333 | ndisc_callback(nd, SD_NDISC_EVENT_REDIRECT, rd); | |
334 | return 0; | |
335 | } | |
336 | ||
1e7a0e21 | 337 | static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) { |
c34cb1d6 | 338 | _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL; |
99534007 | 339 | sd_ndisc *nd = ASSERT_PTR(userdata); |
88d5a3db | 340 | int r; |
e3169126 PF |
341 | |
342 | assert(s); | |
e3169126 PF |
343 | assert(nd->event); |
344 | ||
c34cb1d6 YW |
345 | r = icmp6_packet_receive(fd, &packet); |
346 | if (r < 0) { | |
347 | log_ndisc_errno(nd, r, "Failed to receive ICMPv6 packet, ignoring: %m"); | |
35388783 YW |
348 | return 0; |
349 | } | |
cddf4d81 | 350 | |
4961f566 YW |
351 | /* The function icmp6_receive() accepts the null source address, but RFC 4861 Section 6.1.2 states |
352 | * that hosts MUST discard messages with the null source address. */ | |
c34cb1d6 | 353 | if (in6_addr_is_null(&packet->sender_address)) { |
189eedda YW |
354 | log_ndisc(nd, "Received an ICMPv6 packet from null address, ignoring."); |
355 | return 0; | |
356 | } | |
4961f566 | 357 | |
25413fbf YW |
358 | if (in6_addr_equal(&packet->sender_address, &nd->link_local_addr)) { |
359 | log_ndisc(nd, "Received an ICMPv6 packet sent by the same interface, ignoring."); | |
360 | return 0; | |
361 | } | |
362 | ||
c34cb1d6 YW |
363 | r = icmp6_packet_get_type(packet); |
364 | if (r < 0) { | |
365 | log_ndisc_errno(nd, r, "Received an invalid ICMPv6 packet, ignoring: %m"); | |
366 | return 0; | |
367 | } | |
368 | ||
369 | switch (r) { | |
370 | case ND_ROUTER_ADVERT: | |
371 | (void) ndisc_handle_router(nd, packet); | |
372 | break; | |
373 | ||
696eb2b8 YW |
374 | case ND_NEIGHBOR_ADVERT: |
375 | (void) ndisc_handle_neighbor(nd, packet); | |
376 | break; | |
377 | ||
44e8cf30 YW |
378 | case ND_REDIRECT: |
379 | (void) ndisc_handle_redirect(nd, packet); | |
380 | break; | |
381 | ||
c34cb1d6 YW |
382 | default: |
383 | log_ndisc(nd, "Received an ICMPv6 packet with unexpected type %i, ignoring.", r); | |
384 | } | |
385 | ||
ab8a8a4e | 386 | return 0; |
e3169126 PF |
387 | } |
388 | ||
349d51e7 | 389 | static int ndisc_send_router_solicitation(sd_ndisc *nd) { |
349d51e7 YW |
390 | static const struct nd_router_solicit header = { |
391 | .nd_rs_type = ND_ROUTER_SOLICIT, | |
392 | }; | |
393 | ||
394 | _cleanup_set_free_ Set *options = NULL; | |
395 | int r; | |
396 | ||
397 | assert(nd); | |
398 | ||
399 | if (!ether_addr_is_null(&nd->mac_addr)) { | |
ff944339 | 400 | r = ndisc_option_set_link_layer_address(&options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &nd->mac_addr); |
349d51e7 YW |
401 | if (r < 0) |
402 | return r; | |
403 | } | |
404 | ||
ac336e75 | 405 | return ndisc_send(nd->fd, &IN6_ADDR_ALL_ROUTERS_MULTICAST, &header.nd_rs_hdr, options, USEC_INFINITY); |
349d51e7 YW |
406 | } |
407 | ||
1bd6f895 PF |
408 | static usec_t ndisc_timeout_compute_random(usec_t val) { |
409 | /* compute a time that is random within ±10% of the given value */ | |
410 | return val - val / 10 + | |
411 | (random_u64() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC; | |
412 | } | |
413 | ||
1e7a0e21 | 414 | static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) { |
99534007 | 415 | sd_ndisc *nd = ASSERT_PTR(userdata); |
1bd6f895 | 416 | usec_t time_now; |
e3169126 PF |
417 | int r; |
418 | ||
419 | assert(s); | |
e3169126 PF |
420 | assert(nd->event); |
421 | ||
ba4e0427 | 422 | assert_se(sd_event_now(nd->event, CLOCK_BOOTTIME, &time_now) >= 0); |
e3169126 | 423 | |
1bd6f895 PF |
424 | if (!nd->retransmit_time) |
425 | nd->retransmit_time = ndisc_timeout_compute_random(NDISC_ROUTER_SOLICITATION_INTERVAL); | |
426 | else { | |
427 | if (nd->retransmit_time > NDISC_MAX_ROUTER_SOLICITATION_INTERVAL / 2) | |
428 | nd->retransmit_time = ndisc_timeout_compute_random(NDISC_MAX_ROUTER_SOLICITATION_INTERVAL); | |
429 | else | |
430 | nd->retransmit_time += ndisc_timeout_compute_random(nd->retransmit_time); | |
1e7a0e21 | 431 | } |
9021bb9f | 432 | |
ff4b0321 | 433 | r = event_reset_time(nd->event, &nd->timeout_event_source, |
ba4e0427 | 434 | CLOCK_BOOTTIME, |
ff4b0321 YW |
435 | time_now + nd->retransmit_time, 10 * USEC_PER_MSEC, |
436 | ndisc_timeout, nd, | |
437 | nd->event_priority, "ndisc-timeout-no-ra", true); | |
1bd6f895 PF |
438 | if (r < 0) |
439 | goto fail; | |
440 | ||
349d51e7 | 441 | r = ndisc_send_router_solicitation(nd); |
852bf938 YW |
442 | if (r < 0) |
443 | log_ndisc_errno(nd, r, "Failed to send Router Solicitation, next solicitation in %s, ignoring: %m", | |
444 | FORMAT_TIMESPAN(nd->retransmit_time, USEC_PER_SEC)); | |
445 | else | |
446 | log_ndisc(nd, "Sent Router Solicitation, next solicitation in %s", | |
447 | FORMAT_TIMESPAN(nd->retransmit_time, USEC_PER_SEC)); | |
1bd6f895 | 448 | |
e3169126 | 449 | return 0; |
b9e7b1cf LP |
450 | |
451 | fail: | |
76f713df | 452 | (void) sd_ndisc_stop(nd); |
b9e7b1cf | 453 | return 0; |
e3169126 PF |
454 | } |
455 | ||
1bd6f895 | 456 | static int ndisc_timeout_no_ra(sd_event_source *s, uint64_t usec, void *userdata) { |
99534007 | 457 | sd_ndisc *nd = ASSERT_PTR(userdata); |
1bd6f895 PF |
458 | |
459 | assert(s); | |
1bd6f895 | 460 | |
35388783 | 461 | log_ndisc(nd, "No RA received before link confirmation timeout"); |
1bd6f895 | 462 | |
ff4b0321 | 463 | (void) event_source_disable(nd->timeout_no_ra); |
1bd6f895 PF |
464 | ndisc_callback(nd, SD_NDISC_EVENT_TIMEOUT, NULL); |
465 | ||
466 | return 0; | |
467 | } | |
468 | ||
17347053 | 469 | int sd_ndisc_stop(sd_ndisc *nd) { |
c8bae363 YW |
470 | if (!nd) |
471 | return 0; | |
836cf090 | 472 | |
787e71e4 | 473 | if (!sd_ndisc_is_running(nd)) |
c1c9b211 LP |
474 | return 0; |
475 | ||
35388783 | 476 | log_ndisc(nd, "Stopping IPv6 Router Solicitation client"); |
836cf090 | 477 | |
5c4c338a | 478 | ndisc_reset(nd); |
1e7a0e21 | 479 | return 1; |
836cf090 PF |
480 | } |
481 | ||
6a27ca08 | 482 | static int ndisc_setup_recv_event(sd_ndisc *nd) { |
e3169126 PF |
483 | int r; |
484 | ||
6a27ca08 YW |
485 | assert(nd); |
486 | assert(nd->event); | |
487 | assert(nd->ifindex > 0); | |
e3169126 | 488 | |
6a27ca08 YW |
489 | _cleanup_close_ int fd = -EBADF; |
490 | fd = icmp6_bind(nd->ifindex, /* is_router = */ false); | |
491 | if (fd < 0) | |
492 | return fd; | |
e3169126 | 493 | |
6a27ca08 YW |
494 | _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; |
495 | r = sd_event_add_io(nd->event, &s, fd, EPOLLIN, ndisc_recv, nd); | |
496 | if (r < 0) | |
497 | return r; | |
e3169126 | 498 | |
6a27ca08 | 499 | r = sd_event_source_set_priority(s, nd->event_priority); |
1bd6f895 | 500 | if (r < 0) |
6a27ca08 YW |
501 | return r; |
502 | ||
503 | (void) sd_event_source_set_description(s, "ndisc-receive-router-message"); | |
504 | ||
505 | nd->fd = TAKE_FD(fd); | |
506 | nd->recv_event_source = TAKE_PTR(s); | |
507 | return 1; | |
508 | } | |
509 | ||
510 | static int ndisc_setup_timer(sd_ndisc *nd) { | |
511 | int r; | |
1bd6f895 | 512 | |
6a27ca08 YW |
513 | assert(nd); |
514 | assert(nd->event); | |
1e7a0e21 | 515 | |
6a27ca08 YW |
516 | r = event_reset_time_relative(nd->event, &nd->timeout_event_source, |
517 | CLOCK_BOOTTIME, | |
518 | USEC_PER_SEC / 2, 1 * USEC_PER_SEC, /* See RFC 8415 sec. 18.2.1 */ | |
519 | ndisc_timeout, nd, | |
520 | nd->event_priority, "ndisc-timeout", true); | |
e3169126 | 521 | if (r < 0) |
6a27ca08 | 522 | return r; |
e3169126 | 523 | |
6a27ca08 YW |
524 | r = event_reset_time_relative(nd->event, &nd->timeout_no_ra, |
525 | CLOCK_BOOTTIME, | |
526 | NDISC_TIMEOUT_NO_RA_USEC, 10 * USEC_PER_MSEC, | |
527 | ndisc_timeout_no_ra, nd, | |
528 | nd->event_priority, "ndisc-timeout-no-ra", true); | |
e3169126 | 529 | if (r < 0) |
6a27ca08 | 530 | return r; |
e3169126 | 531 | |
6a27ca08 YW |
532 | return 0; |
533 | } | |
9021bb9f | 534 | |
6a27ca08 YW |
535 | int sd_ndisc_start(sd_ndisc *nd) { |
536 | int r; | |
537 | ||
538 | assert_return(nd, -EINVAL); | |
539 | assert_return(nd->event, -EINVAL); | |
540 | assert_return(nd->ifindex > 0, -EINVAL); | |
541 | ||
542 | if (sd_ndisc_is_running(nd)) | |
543 | return 0; | |
544 | ||
545 | r = ndisc_setup_recv_event(nd); | |
e3169126 | 546 | if (r < 0) |
5c4c338a | 547 | goto fail; |
e3169126 | 548 | |
6a27ca08 | 549 | r = ndisc_setup_timer(nd); |
9021bb9f | 550 | if (r < 0) |
5c4c338a | 551 | goto fail; |
e3169126 | 552 | |
35388783 | 553 | log_ndisc(nd, "Started IPv6 Router Solicitation client"); |
1e7a0e21 | 554 | return 1; |
e3169126 | 555 | |
5c4c338a LP |
556 | fail: |
557 | ndisc_reset(nd); | |
e3169126 PF |
558 | return r; |
559 | } |