/src/systemd/src/libsystemd-network/sd-radv.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | | /*** |
3 | | Copyright © 2017 Intel Corporation. All rights reserved. |
4 | | ***/ |
5 | | |
6 | | #include <linux/ipv6.h> |
7 | | #include <netinet/icmp6.h> |
8 | | #include <netinet/in.h> |
9 | | |
10 | | #include "sd-ndisc-protocol.h" |
11 | | #include "sd-ndisc-router-solicit.h" |
12 | | #include "sd-radv.h" |
13 | | |
14 | | #include "alloc-util.h" |
15 | | #include "event-util.h" |
16 | | #include "fd-util.h" |
17 | | #include "icmp6-packet.h" |
18 | | #include "icmp6-util.h" |
19 | | #include "in-addr-util.h" |
20 | | #include "memory-util.h" |
21 | | #include "ndisc-option.h" |
22 | | #include "ndisc-router-solicit-internal.h" |
23 | | #include "network-common.h" |
24 | | #include "radv-internal.h" |
25 | | #include "random-util.h" |
26 | | #include "set.h" |
27 | | #include "socket-util.h" |
28 | | #include "string-util.h" |
29 | | |
30 | 0 | int sd_radv_new(sd_radv **ret) { |
31 | 0 | _cleanup_(sd_radv_unrefp) sd_radv *ra = NULL; |
32 | |
|
33 | 0 | assert_return(ret, -EINVAL); |
34 | | |
35 | 0 | ra = new(sd_radv, 1); |
36 | 0 | if (!ra) |
37 | 0 | return -ENOMEM; |
38 | | |
39 | 0 | *ra = (sd_radv) { |
40 | 0 | .n_ref = 1, |
41 | 0 | .fd = -EBADF, |
42 | 0 | .lifetime_usec = RADV_DEFAULT_ROUTER_LIFETIME_USEC, |
43 | 0 | }; |
44 | |
|
45 | 0 | *ret = TAKE_PTR(ra); |
46 | |
|
47 | 0 | return 0; |
48 | 0 | } |
49 | | |
50 | 0 | int sd_radv_attach_event(sd_radv *ra, sd_event *event, int64_t priority) { |
51 | 0 | int r; |
52 | |
|
53 | 0 | assert_return(ra, -EINVAL); |
54 | 0 | assert_return(!ra->event, -EBUSY); |
55 | | |
56 | 0 | if (event) |
57 | 0 | ra->event = sd_event_ref(event); |
58 | 0 | else { |
59 | 0 | r = sd_event_default(&ra->event); |
60 | 0 | if (r < 0) |
61 | 0 | return 0; |
62 | 0 | } |
63 | | |
64 | 0 | ra->event_priority = priority; |
65 | |
|
66 | 0 | return 0; |
67 | 0 | } |
68 | | |
69 | 0 | int sd_radv_detach_event(sd_radv *ra) { |
70 | 0 | assert_return(ra, -EINVAL); |
71 | | |
72 | 0 | ra->event = sd_event_unref(ra->event); |
73 | 0 | return 0; |
74 | 0 | } |
75 | | |
76 | 0 | sd_event *sd_radv_get_event(sd_radv *ra) { |
77 | 0 | assert_return(ra, NULL); |
78 | | |
79 | 0 | return ra->event; |
80 | 0 | } |
81 | | |
82 | 0 | int sd_radv_is_running(sd_radv *ra) { |
83 | 0 | if (!ra) |
84 | 0 | return false; |
85 | | |
86 | 0 | return ra->state != RADV_STATE_IDLE; |
87 | 0 | } |
88 | | |
89 | 0 | static void radv_reset(sd_radv *ra) { |
90 | 0 | assert(ra); |
91 | |
|
92 | 0 | (void) event_source_disable(ra->timeout_event_source); |
93 | |
|
94 | 0 | ra->recv_event_source = sd_event_source_disable_unref(ra->recv_event_source); |
95 | |
|
96 | 0 | ra->ra_sent = 0; |
97 | 0 | } |
98 | | |
99 | 0 | static sd_radv *radv_free(sd_radv *ra) { |
100 | 0 | if (!ra) |
101 | 0 | return NULL; |
102 | | |
103 | 0 | radv_reset(ra); |
104 | |
|
105 | 0 | sd_event_source_unref(ra->timeout_event_source); |
106 | 0 | sd_radv_detach_event(ra); |
107 | |
|
108 | 0 | ra->fd = safe_close(ra->fd); |
109 | 0 | free(ra->ifname); |
110 | |
|
111 | 0 | set_free(ra->options); |
112 | |
|
113 | 0 | return mfree(ra); |
114 | 0 | } |
115 | | |
116 | | DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv, sd_radv, radv_free); |
117 | | |
118 | 0 | static bool router_lifetime_is_valid(usec_t lifetime_usec) { |
119 | 0 | assert_cc(RADV_MAX_ROUTER_LIFETIME_USEC <= UINT16_MAX * USEC_PER_SEC); |
120 | 0 | return lifetime_usec == 0 || |
121 | 0 | (lifetime_usec >= RADV_MIN_ROUTER_LIFETIME_USEC && |
122 | 0 | lifetime_usec <= RADV_MAX_ROUTER_LIFETIME_USEC); |
123 | 0 | } |
124 | | |
125 | 0 | static int radv_send_router_on_stop(sd_radv *ra) { |
126 | 0 | static const struct nd_router_advert adv = { |
127 | 0 | .nd_ra_type = ND_ROUTER_ADVERT, |
128 | 0 | }; |
129 | |
|
130 | 0 | _cleanup_set_free_ Set *options = NULL; |
131 | 0 | struct ether_addr mac_addr; |
132 | 0 | usec_t time_now; |
133 | 0 | int r; |
134 | |
|
135 | 0 | assert(ra); |
136 | |
|
137 | 0 | r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now); |
138 | 0 | if (r < 0) |
139 | 0 | return r; |
140 | | |
141 | | /* On stop, we only send source link-layer address option. */ |
142 | 0 | if (ndisc_option_get_mac(ra->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &mac_addr) >= 0) { |
143 | 0 | r = ndisc_option_set_link_layer_address(&options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &mac_addr); |
144 | 0 | if (r < 0) |
145 | 0 | return r; |
146 | 0 | } |
147 | | |
148 | 0 | return ndisc_send(ra->fd, &IN6_ADDR_ALL_NODES_MULTICAST, &adv.nd_ra_hdr, options, time_now); |
149 | 0 | } |
150 | | |
151 | 0 | static int radv_send_router(sd_radv *ra, const struct in6_addr *dst) { |
152 | 0 | assert(ra); |
153 | |
|
154 | 0 | struct nd_router_advert adv = { |
155 | 0 | .nd_ra_type = ND_ROUTER_ADVERT, |
156 | 0 | .nd_ra_router_lifetime = usec_to_be16_sec(ra->lifetime_usec), |
157 | 0 | .nd_ra_reachable = usec_to_be32_msec(ra->reachable_usec), |
158 | 0 | .nd_ra_retransmit = usec_to_be32_msec(ra->retransmit_usec), |
159 | 0 | }; |
160 | 0 | usec_t time_now; |
161 | 0 | int r; |
162 | |
|
163 | 0 | r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now); |
164 | 0 | if (r < 0) |
165 | 0 | return r; |
166 | | |
167 | | /* The nd_ra_curhoplimit and nd_ra_flags_reserved fields cannot specified with nd_ra_router_lifetime |
168 | | * simultaneously in the structured initializer in the above. */ |
169 | 0 | adv.nd_ra_curhoplimit = ra->hop_limit; |
170 | | /* RFC 4191, Section 2.2, |
171 | | * "...If the Router Lifetime is zero, the preference value MUST be set to (00) by the sender..." */ |
172 | 0 | adv.nd_ra_flags_reserved = ra->flags | (ra->lifetime_usec > 0 ? (ra->preference << 3) : 0); |
173 | |
|
174 | 0 | return ndisc_send(ra->fd, |
175 | 0 | (dst && in6_addr_is_set(dst)) ? dst : &IN6_ADDR_ALL_NODES_MULTICAST, |
176 | 0 | &adv.nd_ra_hdr, ra->options, time_now); |
177 | 0 | } |
178 | | |
179 | 0 | static int radv_process_packet(sd_radv *ra, ICMP6Packet *packet) { |
180 | 0 | int r; |
181 | |
|
182 | 0 | assert(ra); |
183 | 0 | assert(packet); |
184 | |
|
185 | 0 | if (icmp6_packet_get_type(packet) != ND_ROUTER_SOLICIT) |
186 | 0 | return log_radv_errno(ra, SYNTHETIC_ERRNO(EBADMSG), "Received ICMP6 packet with unexpected type, ignoring."); |
187 | | |
188 | 0 | _cleanup_(sd_ndisc_router_solicit_unrefp) sd_ndisc_router_solicit *rs = NULL; |
189 | 0 | rs = ndisc_router_solicit_new(packet); |
190 | 0 | if (!rs) |
191 | 0 | return log_oom_debug(); |
192 | | |
193 | 0 | r = ndisc_router_solicit_parse(ra, rs); |
194 | 0 | if (r < 0) |
195 | 0 | return r; |
196 | | |
197 | 0 | struct in6_addr src; |
198 | 0 | r = sd_ndisc_router_solicit_get_sender_address(rs, &src); |
199 | 0 | if (r == -ENODATA) /* null address is allowed */ |
200 | 0 | return sd_radv_send(ra); /* When an unsolicited RA, we need to also update timer. */ |
201 | 0 | if (r < 0) |
202 | 0 | return log_radv_errno(ra, r, "Failed to get sender address of RS, ignoring: %m"); |
203 | 0 | if (in6_addr_equal(&src, &ra->ipv6ll)) |
204 | | /* This should be definitely caused by a misconfiguration. If we send RA to ourself, the |
205 | | * kernel complains about that. Let's ignore the packet. */ |
206 | 0 | return log_radv_errno(ra, SYNTHETIC_ERRNO(EADDRINUSE), "Received RS from the same interface, ignoring."); |
207 | | |
208 | 0 | r = radv_send_router(ra, &src); |
209 | 0 | if (r < 0) |
210 | 0 | return log_radv_errno(ra, r, "Unable to send solicited Router Advertisement to %s, ignoring: %m", IN6_ADDR_TO_STRING(&src)); |
211 | | |
212 | 0 | log_radv(ra, "Sent solicited Router Advertisement to %s.", IN6_ADDR_TO_STRING(&src)); |
213 | 0 | return 0; |
214 | 0 | } |
215 | | |
216 | 0 | static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) { |
217 | 0 | _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL; |
218 | 0 | sd_radv *ra = ASSERT_PTR(userdata); |
219 | 0 | int r; |
220 | |
|
221 | 0 | assert(fd >= 0); |
222 | |
|
223 | 0 | r = icmp6_packet_receive(fd, &packet); |
224 | 0 | if (r < 0) { |
225 | 0 | log_radv_errno(ra, r, "Failed to receive ICMPv6 packet, ignoring: %m"); |
226 | 0 | return 0; |
227 | 0 | } |
228 | | |
229 | 0 | (void) radv_process_packet(ra, packet); |
230 | 0 | return 0; |
231 | 0 | } |
232 | | |
233 | 0 | static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) { |
234 | 0 | sd_radv *ra = ASSERT_PTR(userdata); |
235 | |
|
236 | 0 | if (sd_radv_send(ra) < 0) |
237 | 0 | (void) sd_radv_stop(ra); |
238 | |
|
239 | 0 | return 0; |
240 | 0 | } |
241 | | |
242 | 0 | int sd_radv_send(sd_radv *ra) { |
243 | 0 | usec_t min_timeout, max_timeout, time_now, timeout; |
244 | 0 | int r; |
245 | |
|
246 | 0 | assert_return(ra, -EINVAL); |
247 | 0 | assert_return(ra->event, -EINVAL); |
248 | 0 | assert_return(sd_radv_is_running(ra), -EINVAL); |
249 | 0 | assert(router_lifetime_is_valid(ra->lifetime_usec)); |
250 | |
|
251 | 0 | r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now); |
252 | 0 | if (r < 0) |
253 | 0 | return r; |
254 | | |
255 | 0 | r = radv_send_router(ra, NULL); |
256 | 0 | if (r < 0) |
257 | 0 | return log_radv_errno(ra, r, "Unable to send Router Advertisement: %m"); |
258 | | |
259 | 0 | ra->ra_sent++; |
260 | | |
261 | | /* RFC 4861, Section 6.2.4, sending initial Router Advertisements */ |
262 | 0 | if (ra->ra_sent <= RADV_MAX_INITIAL_RTR_ADVERTISEMENTS) |
263 | 0 | max_timeout = RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC; |
264 | 0 | else |
265 | 0 | max_timeout = RADV_DEFAULT_MAX_TIMEOUT_USEC; |
266 | | |
267 | | /* RFC 4861, Section 6.2.1, lifetime must be at least MaxRtrAdvInterval, |
268 | | * so lower the interval here */ |
269 | 0 | if (ra->lifetime_usec > 0) |
270 | 0 | max_timeout = MIN(max_timeout, ra->lifetime_usec); |
271 | |
|
272 | 0 | if (max_timeout >= 9 * USEC_PER_SEC) |
273 | 0 | min_timeout = max_timeout / 3; |
274 | 0 | else |
275 | 0 | min_timeout = max_timeout * 3 / 4; |
276 | | |
277 | | /* RFC 4861, Section 6.2.1. |
278 | | * MaxRtrAdvInterval MUST be no less than 4 seconds and no greater than 1800 seconds. |
279 | | * MinRtrAdvInterval MUST be no less than 3 seconds and no greater than .75 * MaxRtrAdvInterval. */ |
280 | 0 | assert(max_timeout >= RADV_MIN_MAX_TIMEOUT_USEC); |
281 | 0 | assert(max_timeout <= RADV_MAX_MAX_TIMEOUT_USEC); |
282 | 0 | assert(min_timeout >= RADV_MIN_MIN_TIMEOUT_USEC); |
283 | 0 | assert(min_timeout <= max_timeout * 3 / 4); |
284 | |
|
285 | 0 | timeout = min_timeout + random_u64_range(max_timeout - min_timeout); |
286 | 0 | log_radv(ra, "Sent unsolicited Router Advertisement. Next advertisement will be in %s.", |
287 | 0 | FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); |
288 | |
|
289 | 0 | return event_reset_time( |
290 | 0 | ra->event, &ra->timeout_event_source, |
291 | 0 | CLOCK_BOOTTIME, |
292 | 0 | usec_add(time_now, timeout), MSEC_PER_SEC, |
293 | 0 | radv_timeout, ra, |
294 | 0 | ra->event_priority, "radv-timeout", true); |
295 | 0 | } |
296 | | |
297 | 0 | int sd_radv_stop(sd_radv *ra) { |
298 | 0 | int r; |
299 | |
|
300 | 0 | if (!sd_radv_is_running(ra)) |
301 | 0 | return 0; /* Already stopped. */ |
302 | | |
303 | 0 | log_radv(ra, "Stopping IPv6 Router Advertisement daemon"); |
304 | | |
305 | | /* RFC 4861, Section 6.2.5: |
306 | | * the router SHOULD transmit one or more (but not more than MAX_FINAL_RTR_ADVERTISEMENTS) final |
307 | | * multicast Router Advertisements on the interface with a Router Lifetime field of zero. */ |
308 | 0 | r = radv_send_router_on_stop(ra); |
309 | 0 | if (r < 0) |
310 | 0 | log_radv_errno(ra, r, "Unable to send last Router Advertisement with router lifetime set to zero, ignoring: %m"); |
311 | |
|
312 | 0 | radv_reset(ra); |
313 | 0 | ra->fd = safe_close(ra->fd); |
314 | 0 | ra->state = RADV_STATE_IDLE; |
315 | |
|
316 | 0 | return 0; |
317 | 0 | } |
318 | | |
319 | 0 | static int radv_setup_recv_event(sd_radv *ra) { |
320 | 0 | int r; |
321 | |
|
322 | 0 | assert(ra); |
323 | 0 | assert(ra->event); |
324 | 0 | assert(ra->ifindex > 0); |
325 | |
|
326 | 0 | _cleanup_close_ int fd = -EBADF; |
327 | 0 | fd = icmp6_bind(ra->ifindex, /* is_router = */ true); |
328 | 0 | if (fd < 0) |
329 | 0 | return fd; |
330 | | |
331 | 0 | _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; |
332 | 0 | r = sd_event_add_io(ra->event, &s, fd, EPOLLIN, radv_recv, ra); |
333 | 0 | if (r < 0) |
334 | 0 | return r; |
335 | | |
336 | 0 | r = sd_event_source_set_priority(s, ra->event_priority); |
337 | 0 | if (r < 0) |
338 | 0 | return r; |
339 | | |
340 | 0 | (void) sd_event_source_set_description(s, "radv-receive-message"); |
341 | |
|
342 | 0 | ra->fd = TAKE_FD(fd); |
343 | 0 | ra->recv_event_source = TAKE_PTR(s); |
344 | 0 | return 0; |
345 | 0 | } |
346 | | |
347 | 0 | int sd_radv_start(sd_radv *ra) { |
348 | 0 | int r; |
349 | |
|
350 | 0 | assert_return(ra, -EINVAL); |
351 | 0 | assert_return(ra->event, -EINVAL); |
352 | 0 | assert_return(ra->ifindex > 0, -EINVAL); |
353 | | |
354 | 0 | if (sd_radv_is_running(ra)) |
355 | 0 | return 0; /* Already started. */ |
356 | | |
357 | 0 | r = radv_setup_recv_event(ra); |
358 | 0 | if (r < 0) |
359 | 0 | goto fail; |
360 | | |
361 | 0 | r = event_reset_time(ra->event, &ra->timeout_event_source, |
362 | 0 | CLOCK_BOOTTIME, |
363 | 0 | 0, 0, |
364 | 0 | radv_timeout, ra, |
365 | 0 | ra->event_priority, "radv-timeout", true); |
366 | 0 | if (r < 0) |
367 | 0 | goto fail; |
368 | | |
369 | 0 | ra->state = RADV_STATE_ADVERTISING; |
370 | |
|
371 | 0 | log_radv(ra, "Started IPv6 Router Advertisement daemon"); |
372 | |
|
373 | 0 | return 0; |
374 | | |
375 | 0 | fail: |
376 | 0 | radv_reset(ra); |
377 | |
|
378 | 0 | return r; |
379 | 0 | } |
380 | | |
381 | 0 | int sd_radv_set_ifindex(sd_radv *ra, int ifindex) { |
382 | 0 | assert_return(ra, -EINVAL); |
383 | 0 | assert_return(!sd_radv_is_running(ra), -EBUSY); |
384 | 0 | assert_return(ifindex > 0, -EINVAL); |
385 | | |
386 | 0 | ra->ifindex = ifindex; |
387 | 0 | return 0; |
388 | 0 | } |
389 | | |
390 | 0 | int sd_radv_set_ifname(sd_radv *ra, const char *ifname) { |
391 | 0 | assert_return(ra, -EINVAL); |
392 | 0 | assert_return(ifname, -EINVAL); |
393 | | |
394 | 0 | if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE)) |
395 | 0 | return -EINVAL; |
396 | | |
397 | 0 | return free_and_strdup(&ra->ifname, ifname); |
398 | 0 | } |
399 | | |
400 | 0 | int sd_radv_get_ifname(sd_radv *ra, const char **ret) { |
401 | 0 | int r; |
402 | |
|
403 | 0 | assert_return(ra, -EINVAL); |
404 | | |
405 | 0 | r = get_ifname(ra->ifindex, &ra->ifname); |
406 | 0 | if (r < 0) |
407 | 0 | return r; |
408 | | |
409 | 0 | if (ret) |
410 | 0 | *ret = ra->ifname; |
411 | |
|
412 | 0 | return 0; |
413 | 0 | } |
414 | | |
415 | 0 | int sd_radv_set_link_local_address(sd_radv *ra, const struct in6_addr *addr) { |
416 | 0 | assert_return(ra, -EINVAL); |
417 | 0 | assert_return(!addr || in6_addr_is_link_local(addr), -EINVAL); |
418 | | |
419 | 0 | if (addr) |
420 | 0 | ra->ipv6ll = *addr; |
421 | 0 | else |
422 | 0 | zero(ra->ipv6ll); |
423 | |
|
424 | 0 | return 0; |
425 | 0 | } |
426 | | |
427 | | /* Managing RA header. */ |
428 | | |
429 | 0 | int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit) { |
430 | 0 | assert_return(ra, -EINVAL); |
431 | | |
432 | 0 | ra->hop_limit = hop_limit; |
433 | 0 | return 0; |
434 | 0 | } |
435 | | |
436 | 0 | int sd_radv_set_reachable_time(sd_radv *ra, uint64_t usec) { |
437 | 0 | assert_return(ra, -EINVAL); |
438 | | |
439 | 0 | ra->reachable_usec = usec; |
440 | 0 | return 0; |
441 | 0 | } |
442 | | |
443 | 0 | int sd_radv_set_retransmit(sd_radv *ra, uint64_t usec) { |
444 | 0 | assert_return(ra, -EINVAL); |
445 | | |
446 | 0 | ra->retransmit_usec = usec; |
447 | 0 | return 0; |
448 | 0 | } |
449 | | |
450 | 0 | int sd_radv_set_router_lifetime(sd_radv *ra, uint64_t usec) { |
451 | 0 | assert_return(ra, -EINVAL); |
452 | | |
453 | 0 | if (!router_lifetime_is_valid(usec)) |
454 | 0 | return -EINVAL; |
455 | | |
456 | 0 | ra->lifetime_usec = usec; |
457 | 0 | return 0; |
458 | 0 | } |
459 | | |
460 | 0 | int sd_radv_set_managed_information(sd_radv *ra, int b) { |
461 | 0 | assert_return(ra, -EINVAL); |
462 | | |
463 | 0 | SET_FLAG(ra->flags, ND_RA_FLAG_MANAGED, b); |
464 | 0 | return 0; |
465 | 0 | } |
466 | | |
467 | 0 | int sd_radv_set_other_information(sd_radv *ra, int b) { |
468 | 0 | assert_return(ra, -EINVAL); |
469 | | |
470 | 0 | SET_FLAG(ra->flags, ND_RA_FLAG_OTHER, b); |
471 | 0 | return 0; |
472 | 0 | } |
473 | | |
474 | 0 | int sd_radv_set_preference(sd_radv *ra, uint8_t preference) { |
475 | 0 | assert_return(ra, -EINVAL); |
476 | 0 | assert_return(IN_SET(preference, |
477 | 0 | SD_NDISC_PREFERENCE_LOW, |
478 | 0 | SD_NDISC_PREFERENCE_MEDIUM, |
479 | 0 | SD_NDISC_PREFERENCE_HIGH), -EINVAL); |
480 | | |
481 | 0 | ra->preference = preference; |
482 | 0 | return 0; |
483 | 0 | } |
484 | | |
485 | | /* Managing options. */ |
486 | | |
487 | 0 | int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) { |
488 | 0 | assert_return(ra, -EINVAL); |
489 | | |
490 | 0 | return ndisc_option_set_link_layer_address(&ra->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, mac_addr); |
491 | 0 | } |
492 | | |
493 | 0 | void sd_radv_unset_mac(sd_radv *ra) { |
494 | 0 | if (!ra) |
495 | 0 | return; |
496 | | |
497 | 0 | ndisc_option_remove_by_type(ra->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS); |
498 | 0 | } |
499 | | |
500 | | int sd_radv_add_prefix( |
501 | | sd_radv *ra, |
502 | | const struct in6_addr *prefix, |
503 | | uint8_t prefixlen, |
504 | | uint8_t flags, |
505 | | uint64_t valid_lifetime_usec, |
506 | | uint64_t preferred_lifetime_usec, |
507 | | uint64_t valid_until, |
508 | 0 | uint64_t preferred_until) { |
509 | |
|
510 | 0 | assert_return(ra, -EINVAL); |
511 | 0 | assert_return(prefix, -EINVAL); |
512 | | |
513 | 0 | sd_ndisc_option *opt; |
514 | 0 | SET_FOREACH(opt, ra->options) { |
515 | 0 | if (opt->type != SD_NDISC_OPTION_PREFIX_INFORMATION) |
516 | 0 | continue; |
517 | | |
518 | 0 | if (!in6_addr_prefix_intersect(&opt->prefix.address, opt->prefix.prefixlen, prefix, prefixlen)) |
519 | 0 | continue; /* no intersection */ |
520 | | |
521 | 0 | if (opt->prefix.prefixlen == prefixlen) |
522 | 0 | break; /* same prefix */ |
523 | | |
524 | 0 | return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST), |
525 | 0 | "IPv6 prefix %s conflicts with %s, ignoring.", |
526 | 0 | IN6_ADDR_PREFIX_TO_STRING(prefix, prefixlen), |
527 | 0 | IN6_ADDR_PREFIX_TO_STRING(&opt->prefix.address, opt->prefix.prefixlen)); |
528 | 0 | } |
529 | | |
530 | 0 | return ndisc_option_set_prefix( |
531 | 0 | &ra->options, |
532 | 0 | flags, |
533 | 0 | prefixlen, |
534 | 0 | prefix, |
535 | 0 | valid_lifetime_usec, |
536 | 0 | preferred_lifetime_usec, |
537 | 0 | valid_until, |
538 | 0 | preferred_until); |
539 | 0 | } |
540 | | |
541 | | void sd_radv_remove_prefix( |
542 | | sd_radv *ra, |
543 | | const struct in6_addr *prefix, |
544 | 0 | uint8_t prefixlen) { |
545 | |
|
546 | 0 | if (!ra || !prefix) |
547 | 0 | return; |
548 | | |
549 | 0 | ndisc_option_remove(ra->options, |
550 | 0 | &(sd_ndisc_option) { |
551 | 0 | .type = SD_NDISC_OPTION_PREFIX_INFORMATION, |
552 | 0 | .prefix.prefixlen = prefixlen, |
553 | 0 | .prefix.address = *prefix, |
554 | 0 | }); |
555 | 0 | } |
556 | | |
557 | 0 | int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) { |
558 | 0 | assert_return(ra, -EINVAL); |
559 | 0 | assert_return(mtu >= IPV6_MIN_MTU, -EINVAL); |
560 | | |
561 | 0 | return ndisc_option_set_mtu(&ra->options, mtu); |
562 | 0 | } |
563 | | |
564 | 0 | void sd_radv_unset_mtu(sd_radv *ra) { |
565 | 0 | if (!ra) |
566 | 0 | return; |
567 | | |
568 | 0 | ndisc_option_remove_by_type(ra->options, SD_NDISC_OPTION_MTU); |
569 | 0 | } |
570 | | |
571 | 0 | int sd_radv_set_home_agent(sd_radv *ra, uint16_t preference, uint64_t lifetime_usec, uint64_t valid_until) { |
572 | 0 | assert_return(ra, -EINVAL); |
573 | | |
574 | 0 | ra->flags |= ND_RA_FLAG_HOME_AGENT; |
575 | 0 | return ndisc_option_set_home_agent(&ra->options, preference, lifetime_usec, valid_until); |
576 | 0 | } |
577 | | |
578 | 0 | void sd_radv_unset_home_agent(sd_radv *ra) { |
579 | 0 | if (!ra) |
580 | 0 | return; |
581 | | |
582 | 0 | ra->flags &= ~ND_RA_FLAG_HOME_AGENT; |
583 | 0 | ndisc_option_remove_by_type(ra->options, SD_NDISC_OPTION_HOME_AGENT); |
584 | 0 | } |
585 | | |
586 | | int sd_radv_add_route( |
587 | | sd_radv *ra, |
588 | | const struct in6_addr *prefix, |
589 | | uint8_t prefixlen, |
590 | | uint8_t preference, |
591 | | uint64_t lifetime_usec, |
592 | 0 | uint64_t valid_until) { |
593 | |
|
594 | 0 | assert_return(ra, -EINVAL); |
595 | 0 | assert_return(prefix, -EINVAL); |
596 | | |
597 | 0 | return ndisc_option_set_route( |
598 | 0 | &ra->options, |
599 | 0 | preference, |
600 | 0 | prefixlen, |
601 | 0 | prefix, |
602 | 0 | lifetime_usec, |
603 | 0 | valid_until); |
604 | 0 | } |
605 | | |
606 | | void sd_radv_remove_route( |
607 | | sd_radv *ra, |
608 | | const struct in6_addr *prefix, |
609 | 0 | uint8_t prefixlen) { |
610 | |
|
611 | 0 | if (!ra || !prefix) |
612 | 0 | return; |
613 | | |
614 | 0 | ndisc_option_remove(ra->options, |
615 | 0 | &(sd_ndisc_option) { |
616 | 0 | .type = SD_NDISC_OPTION_ROUTE_INFORMATION, |
617 | 0 | .route.prefixlen = prefixlen, |
618 | 0 | .route.address = *prefix, |
619 | 0 | }); |
620 | 0 | } |
621 | | |
622 | | int sd_radv_add_rdnss( |
623 | | sd_radv *ra, |
624 | | size_t n_dns, |
625 | | const struct in6_addr *dns, |
626 | | uint64_t lifetime_usec, |
627 | 0 | uint64_t valid_until) { |
628 | |
|
629 | 0 | assert_return(ra, -EINVAL); |
630 | 0 | assert_return(dns, -EINVAL); |
631 | | |
632 | 0 | return ndisc_option_set_rdnss( |
633 | 0 | &ra->options, |
634 | 0 | n_dns, |
635 | 0 | dns, |
636 | 0 | lifetime_usec, |
637 | 0 | valid_until); |
638 | 0 | } |
639 | | |
640 | 0 | void sd_radv_clear_rdnss(sd_radv *ra) { |
641 | 0 | if (!ra) |
642 | 0 | return; |
643 | | |
644 | 0 | sd_ndisc_option *opt; |
645 | 0 | SET_FOREACH(opt, ra->options) |
646 | 0 | if (opt->type == SD_NDISC_OPTION_RDNSS) |
647 | 0 | ndisc_option_remove(ra->options, opt); |
648 | 0 | } |
649 | | |
650 | | int sd_radv_add_dnssl( |
651 | | sd_radv *ra, |
652 | | char * const *domains, |
653 | | uint64_t lifetime_usec, |
654 | 0 | uint64_t valid_until) { |
655 | |
|
656 | 0 | assert_return(ra, -EINVAL); |
657 | | |
658 | 0 | return ndisc_option_set_dnssl( |
659 | 0 | &ra->options, |
660 | 0 | domains, |
661 | 0 | lifetime_usec, |
662 | 0 | valid_until); |
663 | 0 | } |
664 | | |
665 | 0 | void sd_radv_clear_dnssl(sd_radv *ra) { |
666 | 0 | if (!ra) |
667 | 0 | return; |
668 | | |
669 | 0 | sd_ndisc_option *opt; |
670 | 0 | SET_FOREACH(opt, ra->options) |
671 | 0 | if (opt->type == SD_NDISC_OPTION_DNSSL) |
672 | 0 | ndisc_option_remove(ra->options, opt); |
673 | 0 | } |
674 | | |
675 | 0 | int sd_radv_set_captive_portal(sd_radv *ra, const char *portal) { |
676 | 0 | assert_return(ra, -EINVAL); |
677 | 0 | assert_return(portal, -EINVAL); |
678 | | |
679 | 0 | return ndisc_option_set_captive_portal(&ra->options, portal); |
680 | 0 | } |
681 | | |
682 | 0 | void sd_radv_unset_captive_portal(sd_radv *ra) { |
683 | 0 | if (!ra) |
684 | 0 | return; |
685 | | |
686 | 0 | ndisc_option_remove_by_type(ra->options, SD_NDISC_OPTION_CAPTIVE_PORTAL); |
687 | 0 | } |
688 | | |
689 | | int sd_radv_add_prefix64( |
690 | | sd_radv *ra, |
691 | | const struct in6_addr *prefix, |
692 | | uint8_t prefixlen, |
693 | | uint64_t lifetime_usec, |
694 | 0 | uint64_t valid_until) { |
695 | |
|
696 | 0 | assert_return(ra, -EINVAL); |
697 | 0 | assert_return(prefix, -EINVAL); |
698 | | |
699 | 0 | return ndisc_option_set_prefix64( |
700 | 0 | &ra->options, |
701 | 0 | prefixlen, |
702 | 0 | prefix, |
703 | 0 | lifetime_usec, |
704 | 0 | valid_until); |
705 | 0 | } |
706 | | |
707 | | void sd_radv_remove_prefix64( |
708 | | sd_radv *ra, |
709 | | const struct in6_addr *prefix, |
710 | 0 | uint8_t prefixlen) { |
711 | |
|
712 | 0 | if (!ra || !prefix) |
713 | 0 | return; |
714 | | |
715 | 0 | ndisc_option_remove(ra->options, |
716 | 0 | &(sd_ndisc_option) { |
717 | 0 | .type = SD_NDISC_OPTION_PREF64, |
718 | 0 | .prefix64.prefixlen = prefixlen, |
719 | 0 | .prefix64.prefix = *prefix, |
720 | 0 | }); |
721 | 0 | } |