]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
1e7a0e21 | 2 | /*** |
810adae9 | 3 | Copyright © 2014 Intel Corporation. All rights reserved. |
1e7a0e21 LP |
4 | ***/ |
5 | ||
6 | #include <netinet/icmp6.h> | |
7 | ||
8 | #include "sd-ndisc.h" | |
9 | ||
10 | #include "alloc-util.h" | |
11 | #include "dns-domain.h" | |
6244184e | 12 | #include "escape.h" |
1e7a0e21 | 13 | #include "hostname-util.h" |
0a970718 | 14 | #include "memory-util.h" |
f5947a5e | 15 | #include "missing_network.h" |
1e7a0e21 | 16 | #include "ndisc-internal.h" |
6e8f5e4c | 17 | #include "ndisc-protocol.h" |
ca34b434 | 18 | #include "ndisc-router-internal.h" |
1e7a0e21 LP |
19 | #include "strv.h" |
20 | ||
8301aa0b | 21 | DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router, sd_ndisc_router, mfree); |
1e7a0e21 LP |
22 | |
23 | sd_ndisc_router *ndisc_router_new(size_t raw_size) { | |
24 | sd_ndisc_router *rt; | |
25 | ||
4e88a46b YW |
26 | if (raw_size > SIZE_MAX - ALIGN(sizeof(sd_ndisc_router))) |
27 | return NULL; | |
28 | ||
1e7a0e21 LP |
29 | rt = malloc0(ALIGN(sizeof(sd_ndisc_router)) + raw_size); |
30 | if (!rt) | |
31 | return NULL; | |
32 | ||
33 | rt->raw_size = raw_size; | |
34 | rt->n_ref = 1; | |
35 | ||
36 | return rt; | |
37 | } | |
38 | ||
3231f624 | 39 | int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret) { |
1e7a0e21 | 40 | assert_return(rt, -EINVAL); |
3231f624 | 41 | assert_return(ret, -EINVAL); |
1e7a0e21 | 42 | |
94876904 | 43 | if (in6_addr_is_null(&rt->address)) |
1e7a0e21 LP |
44 | return -ENODATA; |
45 | ||
3231f624 | 46 | *ret = rt->address; |
1e7a0e21 LP |
47 | return 0; |
48 | } | |
49 | ||
17347053 | 50 | int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret) { |
1e7a0e21 LP |
51 | assert_return(rt, -EINVAL); |
52 | assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP); | |
53 | assert_return(clock_supported(clock), -EOPNOTSUPP); | |
54 | assert_return(ret, -EINVAL); | |
55 | ||
56 | if (!triple_timestamp_is_set(&rt->timestamp)) | |
57 | return -ENODATA; | |
58 | ||
59 | *ret = triple_timestamp_by_clock(&rt->timestamp, clock); | |
60 | return 0; | |
61 | } | |
62 | ||
6197db53 YW |
63 | #define DEFINE_GET_TIMESTAMP(name) \ |
64 | int sd_ndisc_router_##name##_timestamp( \ | |
65 | sd_ndisc_router *rt, \ | |
66 | clockid_t clock, \ | |
67 | uint64_t *ret) { \ | |
68 | \ | |
69 | usec_t s, t; \ | |
70 | int r; \ | |
71 | \ | |
72 | assert_return(rt, -EINVAL); \ | |
73 | assert_return(ret, -EINVAL); \ | |
74 | \ | |
75 | r = sd_ndisc_router_##name(rt, &s); \ | |
76 | if (r < 0) \ | |
77 | return r; \ | |
78 | \ | |
79 | r = sd_ndisc_router_get_timestamp(rt, clock, &t); \ | |
80 | if (r < 0) \ | |
81 | return r; \ | |
82 | \ | |
83 | *ret = time_span_to_stamp(s, t); \ | |
84 | return 0; \ | |
85 | } | |
86 | ||
87 | DEFINE_GET_TIMESTAMP(get_lifetime); | |
88 | DEFINE_GET_TIMESTAMP(prefix_get_valid_lifetime); | |
89 | DEFINE_GET_TIMESTAMP(prefix_get_preferred_lifetime); | |
90 | DEFINE_GET_TIMESTAMP(route_get_lifetime); | |
91 | DEFINE_GET_TIMESTAMP(rdnss_get_lifetime); | |
92 | DEFINE_GET_TIMESTAMP(dnssl_get_lifetime); | |
93 | DEFINE_GET_TIMESTAMP(prefix64_get_lifetime); | |
94 | ||
3231f624 | 95 | int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size) { |
1e7a0e21 LP |
96 | assert_return(rt, -EINVAL); |
97 | assert_return(ret, -EINVAL); | |
3231f624 | 98 | assert_return(ret_size, -EINVAL); |
1e7a0e21 LP |
99 | |
100 | *ret = NDISC_ROUTER_RAW(rt); | |
3231f624 | 101 | *ret_size = rt->raw_size; |
1e7a0e21 LP |
102 | |
103 | return 0; | |
104 | } | |
105 | ||
6e8f5e4c SS |
106 | static bool pref64_option_verify(const struct nd_opt_prefix64_info *p, size_t length) { |
107 | uint16_t lifetime_and_plc; | |
108 | ||
109 | assert(p); | |
110 | ||
111 | if (length != sizeof(struct nd_opt_prefix64_info)) | |
112 | return false; | |
113 | ||
114 | lifetime_and_plc = be16toh(p->lifetime_and_plc); | |
115 | if (pref64_plc_to_prefix_length(lifetime_and_plc, NULL) < 0) | |
116 | return false; | |
117 | ||
118 | return true; | |
119 | } | |
120 | ||
35388783 | 121 | int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) { |
1e7a0e21 LP |
122 | struct nd_router_advert *a; |
123 | const uint8_t *p; | |
124 | bool has_mtu = false, has_flag_extension = false; | |
125 | size_t left; | |
126 | ||
127 | assert(rt); | |
128 | ||
35388783 YW |
129 | if (rt->raw_size < sizeof(struct nd_router_advert)) |
130 | return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), | |
131 | "Too small to be a router advertisement, ignoring."); | |
1e7a0e21 | 132 | |
da890466 | 133 | /* Router advertisement packets are neatly aligned to 64-bit boundaries, hence we can access them directly */ |
1e7a0e21 LP |
134 | a = NDISC_ROUTER_RAW(rt); |
135 | ||
35388783 YW |
136 | if (a->nd_ra_type != ND_ROUTER_ADVERT) |
137 | return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), | |
138 | "Received ND packet that is not a router advertisement, ignoring."); | |
1e7a0e21 | 139 | |
35388783 YW |
140 | if (a->nd_ra_code != 0) |
141 | return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), | |
142 | "Received ND packet with wrong RA code, ignoring."); | |
1e7a0e21 LP |
143 | |
144 | rt->hop_limit = a->nd_ra_curhoplimit; | |
da890466 | 145 | rt->flags = a->nd_ra_flags_reserved; /* the first 8 bits */ |
6197db53 | 146 | rt->lifetime_usec = be16_sec_to_usec(a->nd_ra_router_lifetime, /* max_as_infinity = */ false); |
a68f007a | 147 | rt->reachable_time_usec = be32_msec_to_usec(a->nd_ra_reachable, /* mas_as_infinity = */ false); |
d4c8de21 | 148 | rt->retransmission_time_usec = be32_msec_to_usec(a->nd_ra_retransmit, /* max_as_infinity = */ false); |
1e7a0e21 LP |
149 | |
150 | rt->preference = (rt->flags >> 3) & 3; | |
151 | if (!IN_SET(rt->preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH)) | |
152 | rt->preference = SD_NDISC_PREFERENCE_MEDIUM; | |
153 | ||
154 | p = (const uint8_t*) NDISC_ROUTER_RAW(rt) + sizeof(struct nd_router_advert); | |
155 | left = rt->raw_size - sizeof(struct nd_router_advert); | |
156 | ||
157 | for (;;) { | |
158 | uint8_t type; | |
159 | size_t length; | |
160 | ||
161 | if (left == 0) | |
162 | break; | |
163 | ||
35388783 YW |
164 | if (left < 2) |
165 | return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), | |
166 | "Option lacks header, ignoring datagram."); | |
1e7a0e21 LP |
167 | |
168 | type = p[0]; | |
169 | length = p[1] * 8; | |
170 | ||
35388783 YW |
171 | if (length == 0) |
172 | return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), | |
173 | "Zero-length option, ignoring datagram."); | |
174 | if (left < length) | |
175 | return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), | |
176 | "Option truncated, ignoring datagram."); | |
1e7a0e21 LP |
177 | |
178 | switch (type) { | |
179 | ||
180 | case SD_NDISC_OPTION_PREFIX_INFORMATION: | |
181 | ||
35388783 YW |
182 | if (length != 4*8) |
183 | return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), | |
184 | "Prefix option of invalid size, ignoring datagram."); | |
1e7a0e21 | 185 | |
35388783 YW |
186 | if (p[2] > 128) |
187 | return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), | |
188 | "Bad prefix length, ignoring datagram."); | |
1e7a0e21 LP |
189 | |
190 | break; | |
191 | ||
192 | case SD_NDISC_OPTION_MTU: { | |
193 | uint32_t m; | |
194 | ||
195 | if (has_mtu) { | |
35388783 | 196 | log_ndisc(nd, "MTU option specified twice, ignoring."); |
f3241c61 | 197 | break; |
1e7a0e21 LP |
198 | } |
199 | ||
35388783 YW |
200 | if (length != 8) |
201 | return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), | |
202 | "MTU option of invalid size, ignoring datagram."); | |
1e7a0e21 LP |
203 | |
204 | m = be32toh(*(uint32_t*) (p + 4)); | |
205 | if (m >= IPV6_MIN_MTU) /* ignore invalidly small MTUs */ | |
206 | rt->mtu = m; | |
207 | ||
208 | has_mtu = true; | |
209 | break; | |
210 | } | |
211 | ||
212 | case SD_NDISC_OPTION_ROUTE_INFORMATION: | |
35388783 YW |
213 | if (length < 1*8 || length > 3*8) |
214 | return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), | |
215 | "Route information option of invalid size, ignoring datagram."); | |
1e7a0e21 | 216 | |
35388783 YW |
217 | if (p[2] > 128) |
218 | return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), | |
219 | "Bad route prefix length, ignoring datagram."); | |
1e7a0e21 LP |
220 | |
221 | break; | |
222 | ||
223 | case SD_NDISC_OPTION_RDNSS: | |
35388783 YW |
224 | if (length < 3*8 || (length % (2*8)) != 1*8) |
225 | return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), "RDNSS option has invalid size."); | |
1e7a0e21 LP |
226 | |
227 | break; | |
228 | ||
229 | case SD_NDISC_OPTION_FLAGS_EXTENSION: | |
230 | ||
231 | if (has_flag_extension) { | |
35388783 | 232 | log_ndisc(nd, "Flags extension option specified twice, ignoring."); |
f3241c61 | 233 | break; |
1e7a0e21 LP |
234 | } |
235 | ||
35388783 YW |
236 | if (length < 1*8) |
237 | return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), | |
238 | "Flags extension option has invalid size."); | |
1e7a0e21 LP |
239 | |
240 | /* Add in the additional flags bits */ | |
241 | rt->flags |= | |
242 | ((uint64_t) p[2] << 8) | | |
243 | ((uint64_t) p[3] << 16) | | |
244 | ((uint64_t) p[4] << 24) | | |
245 | ((uint64_t) p[5] << 32) | | |
246 | ((uint64_t) p[6] << 40) | | |
247 | ((uint64_t) p[7] << 48); | |
248 | ||
249 | has_flag_extension = true; | |
250 | break; | |
251 | ||
252 | case SD_NDISC_OPTION_DNSSL: | |
35388783 YW |
253 | if (length < 2*8) |
254 | return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), | |
255 | "DNSSL option has invalid size."); | |
1e7a0e21 LP |
256 | |
257 | break; | |
6e8f5e4c SS |
258 | case SD_NDISC_OPTION_PREF64: { |
259 | if (!pref64_option_verify((struct nd_opt_prefix64_info *) p, length)) | |
260 | log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), | |
261 | "PREF64 prefix has invalid prefix length."); | |
262 | break; | |
263 | }} | |
1e7a0e21 LP |
264 | |
265 | p += length, left -= length; | |
266 | } | |
267 | ||
268 | rt->rindex = sizeof(struct nd_router_advert); | |
269 | return 0; | |
270 | } | |
271 | ||
17347053 | 272 | int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret) { |
1e7a0e21 LP |
273 | assert_return(rt, -EINVAL); |
274 | assert_return(ret, -EINVAL); | |
275 | ||
276 | *ret = rt->hop_limit; | |
277 | return 0; | |
278 | } | |
279 | ||
a68f007a YW |
280 | int sd_ndisc_router_get_reachable_time(sd_ndisc_router *rt, uint64_t *ret) { |
281 | assert_return(rt, -EINVAL); | |
282 | assert_return(ret, -EINVAL); | |
283 | ||
284 | *ret = rt->reachable_time_usec; | |
285 | return 0; | |
286 | } | |
287 | ||
d4c8de21 MM |
288 | int sd_ndisc_router_get_retransmission_time(sd_ndisc_router *rt, uint64_t *ret) { |
289 | assert_return(rt, -EINVAL); | |
290 | assert_return(ret, -EINVAL); | |
291 | ||
292 | *ret = rt->retransmission_time_usec; | |
293 | return 0; | |
294 | } | |
295 | ||
3231f624 | 296 | int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret) { |
1e7a0e21 | 297 | assert_return(rt, -EINVAL); |
3231f624 | 298 | assert_return(ret, -EINVAL); |
1e7a0e21 | 299 | |
3231f624 | 300 | *ret = rt->flags; |
1e7a0e21 LP |
301 | return 0; |
302 | } | |
303 | ||
6197db53 | 304 | int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) { |
1e7a0e21 | 305 | assert_return(rt, -EINVAL); |
1e7a0e21 | 306 | |
828b5dbf YW |
307 | if (ret) |
308 | *ret = rt->lifetime_usec; | |
309 | ||
310 | return rt->lifetime_usec > 0; /* Indicate if the router is still valid or not. */ | |
1e7a0e21 LP |
311 | } |
312 | ||
17347053 | 313 | int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret) { |
1e7a0e21 LP |
314 | assert_return(rt, -EINVAL); |
315 | assert_return(ret, -EINVAL); | |
316 | ||
317 | *ret = rt->preference; | |
318 | return 0; | |
319 | } | |
320 | ||
17347053 | 321 | int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) { |
1e7a0e21 LP |
322 | assert_return(rt, -EINVAL); |
323 | assert_return(ret, -EINVAL); | |
324 | ||
325 | if (rt->mtu <= 0) | |
326 | return -ENODATA; | |
327 | ||
328 | *ret = rt->mtu; | |
329 | return 0; | |
330 | } | |
331 | ||
17347053 | 332 | int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) { |
1e7a0e21 LP |
333 | assert_return(rt, -EINVAL); |
334 | ||
335 | assert(rt->raw_size >= sizeof(struct nd_router_advert)); | |
336 | rt->rindex = sizeof(struct nd_router_advert); | |
337 | ||
338 | return rt->rindex < rt->raw_size; | |
339 | } | |
340 | ||
17347053 | 341 | int sd_ndisc_router_option_next(sd_ndisc_router *rt) { |
1e7a0e21 LP |
342 | size_t length; |
343 | ||
344 | assert_return(rt, -EINVAL); | |
345 | ||
346 | if (rt->rindex == rt->raw_size) /* EOF */ | |
347 | return -ESPIPE; | |
348 | ||
349 | if (rt->rindex + 2 > rt->raw_size) /* Truncated message */ | |
350 | return -EBADMSG; | |
351 | ||
352 | length = NDISC_ROUTER_OPTION_LENGTH(rt); | |
353 | if (rt->rindex + length > rt->raw_size) | |
354 | return -EBADMSG; | |
355 | ||
356 | rt->rindex += length; | |
357 | return rt->rindex < rt->raw_size; | |
358 | } | |
359 | ||
17347053 | 360 | int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) { |
1e7a0e21 LP |
361 | assert_return(rt, -EINVAL); |
362 | assert_return(ret, -EINVAL); | |
363 | ||
364 | if (rt->rindex == rt->raw_size) /* EOF */ | |
365 | return -ESPIPE; | |
366 | ||
367 | if (rt->rindex + 2 > rt->raw_size) /* Truncated message */ | |
368 | return -EBADMSG; | |
369 | ||
370 | *ret = NDISC_ROUTER_OPTION_TYPE(rt); | |
371 | return 0; | |
372 | } | |
373 | ||
17347053 | 374 | int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) { |
1e7a0e21 LP |
375 | uint8_t k; |
376 | int r; | |
377 | ||
378 | assert_return(rt, -EINVAL); | |
379 | ||
380 | r = sd_ndisc_router_option_get_type(rt, &k); | |
381 | if (r < 0) | |
382 | return r; | |
383 | ||
384 | return type == k; | |
385 | } | |
386 | ||
3231f624 | 387 | int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size) { |
1e7a0e21 LP |
388 | size_t length; |
389 | ||
390 | assert_return(rt, -EINVAL); | |
391 | assert_return(ret, -EINVAL); | |
3231f624 | 392 | assert_return(ret_size, -EINVAL); |
1e7a0e21 LP |
393 | |
394 | /* Note that this returns the full option, including the option header */ | |
395 | ||
396 | if (rt->rindex + 2 > rt->raw_size) | |
397 | return -EBADMSG; | |
398 | ||
399 | length = NDISC_ROUTER_OPTION_LENGTH(rt); | |
400 | if (rt->rindex + length > rt->raw_size) | |
401 | return -EBADMSG; | |
402 | ||
403 | *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; | |
3231f624 | 404 | *ret_size = length; |
1e7a0e21 LP |
405 | |
406 | return 0; | |
407 | } | |
408 | ||
409 | static int get_prefix_info(sd_ndisc_router *rt, struct nd_opt_prefix_info **ret) { | |
410 | struct nd_opt_prefix_info *ri; | |
411 | size_t length; | |
412 | int r; | |
413 | ||
414 | assert(rt); | |
415 | assert(ret); | |
416 | ||
417 | r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_PREFIX_INFORMATION); | |
418 | if (r < 0) | |
419 | return r; | |
420 | if (r == 0) | |
421 | return -EMEDIUMTYPE; | |
422 | ||
423 | length = NDISC_ROUTER_OPTION_LENGTH(rt); | |
424 | if (length != sizeof(struct nd_opt_prefix_info)) | |
425 | return -EBADMSG; | |
426 | ||
427 | ri = (struct nd_opt_prefix_info*) ((uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex); | |
428 | if (ri->nd_opt_pi_prefix_len > 128) | |
429 | return -EBADMSG; | |
430 | ||
431 | *ret = ri; | |
432 | return 0; | |
433 | } | |
434 | ||
6197db53 | 435 | int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint64_t *ret) { |
1e7a0e21 LP |
436 | struct nd_opt_prefix_info *ri; |
437 | int r; | |
438 | ||
439 | assert_return(rt, -EINVAL); | |
440 | assert_return(ret, -EINVAL); | |
441 | ||
442 | r = get_prefix_info(rt, &ri); | |
443 | if (r < 0) | |
444 | return r; | |
445 | ||
6197db53 | 446 | *ret = be32_sec_to_usec(ri->nd_opt_pi_valid_time, /* max_as_infinity = */ true); |
1e7a0e21 LP |
447 | return 0; |
448 | } | |
449 | ||
6197db53 | 450 | int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint64_t *ret) { |
1e7a0e21 LP |
451 | struct nd_opt_prefix_info *pi; |
452 | int r; | |
453 | ||
454 | assert_return(rt, -EINVAL); | |
455 | assert_return(ret, -EINVAL); | |
456 | ||
457 | r = get_prefix_info(rt, &pi); | |
458 | if (r < 0) | |
459 | return r; | |
460 | ||
6197db53 | 461 | *ret = be32_sec_to_usec(pi->nd_opt_pi_preferred_time, /* max_as_infinity = */ true); |
1e7a0e21 LP |
462 | return 0; |
463 | } | |
464 | ||
17347053 | 465 | int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret) { |
1e7a0e21 | 466 | struct nd_opt_prefix_info *pi; |
9f702d00 | 467 | uint8_t flags; |
1e7a0e21 LP |
468 | int r; |
469 | ||
470 | assert_return(rt, -EINVAL); | |
471 | assert_return(ret, -EINVAL); | |
472 | ||
473 | r = get_prefix_info(rt, &pi); | |
474 | if (r < 0) | |
475 | return r; | |
476 | ||
9f702d00 JT |
477 | flags = pi->nd_opt_pi_flags_reserved; |
478 | ||
479 | if ((flags & ND_OPT_PI_FLAG_AUTO) && (pi->nd_opt_pi_prefix_len != 64)) { | |
35388783 | 480 | log_ndisc(NULL, "Invalid prefix length, ignoring prefix for stateless autoconfiguration."); |
9f702d00 JT |
481 | flags &= ~ND_OPT_PI_FLAG_AUTO; |
482 | } | |
483 | ||
484 | *ret = flags; | |
1e7a0e21 LP |
485 | return 0; |
486 | } | |
487 | ||
3231f624 | 488 | int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret) { |
1e7a0e21 LP |
489 | struct nd_opt_prefix_info *pi; |
490 | int r; | |
491 | ||
492 | assert_return(rt, -EINVAL); | |
3231f624 | 493 | assert_return(ret, -EINVAL); |
1e7a0e21 LP |
494 | |
495 | r = get_prefix_info(rt, &pi); | |
496 | if (r < 0) | |
497 | return r; | |
498 | ||
3231f624 | 499 | *ret = pi->nd_opt_pi_prefix; |
1e7a0e21 LP |
500 | return 0; |
501 | } | |
502 | ||
17347053 | 503 | int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) { |
1e7a0e21 LP |
504 | struct nd_opt_prefix_info *pi; |
505 | int r; | |
506 | ||
507 | assert_return(rt, -EINVAL); | |
508 | assert_return(ret, -EINVAL); | |
509 | ||
510 | r = get_prefix_info(rt, &pi); | |
511 | if (r < 0) | |
512 | return r; | |
513 | ||
514 | if (pi->nd_opt_pi_prefix_len > 128) | |
515 | return -EBADMSG; | |
516 | ||
517 | *ret = pi->nd_opt_pi_prefix_len; | |
518 | return 0; | |
519 | } | |
520 | ||
521 | static int get_route_info(sd_ndisc_router *rt, uint8_t **ret) { | |
522 | uint8_t *ri; | |
523 | size_t length; | |
524 | int r; | |
525 | ||
526 | assert(rt); | |
527 | assert(ret); | |
528 | ||
529 | r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_ROUTE_INFORMATION); | |
530 | if (r < 0) | |
531 | return r; | |
532 | if (r == 0) | |
533 | return -EMEDIUMTYPE; | |
534 | ||
535 | length = NDISC_ROUTER_OPTION_LENGTH(rt); | |
536 | if (length < 1*8 || length > 3*8) | |
537 | return -EBADMSG; | |
538 | ||
539 | ri = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; | |
540 | ||
541 | if (ri[2] > 128) | |
542 | return -EBADMSG; | |
543 | ||
544 | *ret = ri; | |
545 | return 0; | |
546 | } | |
547 | ||
6197db53 | 548 | int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) { |
1e7a0e21 LP |
549 | uint8_t *ri; |
550 | int r; | |
551 | ||
552 | assert_return(rt, -EINVAL); | |
553 | assert_return(ret, -EINVAL); | |
554 | ||
555 | r = get_route_info(rt, &ri); | |
556 | if (r < 0) | |
557 | return r; | |
558 | ||
6197db53 | 559 | *ret = unaligned_be32_sec_to_usec(ri + 4, /* max_as_infinity = */ true); |
1e7a0e21 LP |
560 | return 0; |
561 | } | |
562 | ||
3231f624 | 563 | int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret) { |
1e7a0e21 LP |
564 | uint8_t *ri; |
565 | int r; | |
566 | ||
567 | assert_return(rt, -EINVAL); | |
3231f624 | 568 | assert_return(ret, -EINVAL); |
1e7a0e21 LP |
569 | |
570 | r = get_route_info(rt, &ri); | |
571 | if (r < 0) | |
572 | return r; | |
573 | ||
3231f624 YW |
574 | zero(*ret); |
575 | memcpy(ret, ri + 8, NDISC_ROUTER_OPTION_LENGTH(rt) - 8); | |
1e7a0e21 LP |
576 | |
577 | return 0; | |
578 | } | |
579 | ||
17347053 | 580 | int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) { |
1e7a0e21 LP |
581 | uint8_t *ri; |
582 | int r; | |
583 | ||
584 | assert_return(rt, -EINVAL); | |
585 | assert_return(ret, -EINVAL); | |
586 | ||
587 | r = get_route_info(rt, &ri); | |
588 | if (r < 0) | |
589 | return r; | |
590 | ||
591 | *ret = ri[2]; | |
592 | return 0; | |
593 | } | |
594 | ||
17347053 | 595 | int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret) { |
1e7a0e21 LP |
596 | uint8_t *ri; |
597 | int r; | |
598 | ||
599 | assert_return(rt, -EINVAL); | |
600 | assert_return(ret, -EINVAL); | |
601 | ||
602 | r = get_route_info(rt, &ri); | |
603 | if (r < 0) | |
604 | return r; | |
605 | ||
3912d49d | 606 | if (!IN_SET((ri[3] >> 3) & 3, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH)) |
25f7271c | 607 | return -EOPNOTSUPP; |
1e7a0e21 | 608 | |
3912d49d | 609 | *ret = (ri[3] >> 3) & 3; |
1e7a0e21 LP |
610 | return 0; |
611 | } | |
612 | ||
613 | static int get_rdnss_info(sd_ndisc_router *rt, uint8_t **ret) { | |
614 | size_t length; | |
615 | int r; | |
616 | ||
617 | assert(rt); | |
618 | assert(ret); | |
619 | ||
620 | r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_RDNSS); | |
621 | if (r < 0) | |
622 | return r; | |
623 | if (r == 0) | |
624 | return -EMEDIUMTYPE; | |
625 | ||
626 | length = NDISC_ROUTER_OPTION_LENGTH(rt); | |
627 | if (length < 3*8 || (length % (2*8)) != 1*8) | |
628 | return -EBADMSG; | |
629 | ||
630 | *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; | |
631 | return 0; | |
632 | } | |
633 | ||
17347053 | 634 | int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret) { |
1e7a0e21 LP |
635 | uint8_t *ri; |
636 | int r; | |
637 | ||
638 | assert_return(rt, -EINVAL); | |
639 | assert_return(ret, -EINVAL); | |
640 | ||
641 | r = get_rdnss_info(rt, &ri); | |
642 | if (r < 0) | |
643 | return r; | |
644 | ||
645 | *ret = (const struct in6_addr*) (ri + 8); | |
646 | return (NDISC_ROUTER_OPTION_LENGTH(rt) - 8) / 16; | |
647 | } | |
648 | ||
6197db53 | 649 | int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) { |
1e7a0e21 LP |
650 | uint8_t *ri; |
651 | int r; | |
652 | ||
653 | assert_return(rt, -EINVAL); | |
654 | assert_return(ret, -EINVAL); | |
655 | ||
656 | r = get_rdnss_info(rt, &ri); | |
657 | if (r < 0) | |
658 | return r; | |
659 | ||
6197db53 | 660 | *ret = unaligned_be32_sec_to_usec(ri + 4, /* max_as_infinity = */ true); |
1e7a0e21 LP |
661 | return 0; |
662 | } | |
663 | ||
664 | static int get_dnssl_info(sd_ndisc_router *rt, uint8_t **ret) { | |
665 | size_t length; | |
666 | int r; | |
667 | ||
668 | assert(rt); | |
669 | assert(ret); | |
670 | ||
671 | r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_DNSSL); | |
672 | if (r < 0) | |
673 | return r; | |
674 | if (r == 0) | |
675 | return -EMEDIUMTYPE; | |
676 | ||
677 | length = NDISC_ROUTER_OPTION_LENGTH(rt); | |
678 | if (length < 2*8) | |
679 | return -EBADMSG; | |
680 | ||
681 | *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; | |
682 | return 0; | |
683 | } | |
684 | ||
17347053 | 685 | int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret) { |
1e7a0e21 LP |
686 | _cleanup_strv_free_ char **l = NULL; |
687 | _cleanup_free_ char *e = NULL; | |
319a4f4b | 688 | size_t n = 0, left; |
1e7a0e21 LP |
689 | uint8_t *ri, *p; |
690 | bool first = true; | |
691 | int r; | |
692 | unsigned k = 0; | |
693 | ||
694 | assert_return(rt, -EINVAL); | |
695 | assert_return(ret, -EINVAL); | |
696 | ||
697 | r = get_dnssl_info(rt, &ri); | |
698 | if (r < 0) | |
699 | return r; | |
700 | ||
701 | p = ri + 8; | |
702 | left = NDISC_ROUTER_OPTION_LENGTH(rt) - 8; | |
703 | ||
704 | for (;;) { | |
705 | if (left == 0) { | |
706 | ||
707 | if (n > 0) /* Not properly NUL terminated */ | |
708 | return -EBADMSG; | |
709 | ||
710 | break; | |
711 | } | |
712 | ||
713 | if (*p == 0) { | |
714 | /* Found NUL termination */ | |
715 | ||
716 | if (n > 0) { | |
717 | _cleanup_free_ char *normalized = NULL; | |
718 | ||
719 | e[n] = 0; | |
7470cc4c | 720 | r = dns_name_normalize(e, 0, &normalized); |
6244184e YW |
721 | if (r < 0) { |
722 | _cleanup_free_ char *escaped = cescape(e); | |
723 | log_debug_errno(r, "Failed to normalize advertised domain name \"%s\": %m", strna(escaped)); | |
724 | /* Here, do not propagate error code from dns_name_normalize() except for ENOMEM. */ | |
725 | return r == -ENOMEM ? -ENOMEM : -EBADMSG; | |
726 | } | |
1e7a0e21 LP |
727 | |
728 | /* Ignore the root domain name or "localhost" and friends */ | |
729 | if (!is_localhost(normalized) && | |
730 | !dns_name_is_root(normalized)) { | |
731 | ||
732 | if (strv_push(&l, normalized) < 0) | |
733 | return -ENOMEM; | |
734 | ||
735 | normalized = NULL; | |
736 | k++; | |
737 | } | |
738 | } | |
739 | ||
740 | n = 0; | |
741 | first = true; | |
742 | p++, left--; | |
743 | continue; | |
744 | } | |
745 | ||
746 | /* Check for compression (which is not allowed) */ | |
747 | if (*p > 63) | |
748 | return -EBADMSG; | |
749 | ||
750 | if (1U + *p + 1U > left) | |
751 | return -EBADMSG; | |
752 | ||
319a4f4b | 753 | if (!GREEDY_REALLOC(e, n + !first + DNS_LABEL_ESCAPED_MAX + 1U)) |
1e7a0e21 LP |
754 | return -ENOMEM; |
755 | ||
756 | if (first) | |
757 | first = false; | |
758 | else | |
759 | e[n++] = '.'; | |
760 | ||
761 | r = dns_label_escape((char*) p+1, *p, e + n, DNS_LABEL_ESCAPED_MAX); | |
6244184e YW |
762 | if (r < 0) { |
763 | _cleanup_free_ char *escaped = cescape_length((const char*) p+1, *p); | |
764 | log_debug_errno(r, "Failed to escape advertised domain name \"%s\": %m", strna(escaped)); | |
765 | /* Here, do not propagate error code from dns_label_escape() except for ENOMEM. */ | |
766 | return r == -ENOMEM ? -ENOMEM : -EBADMSG; | |
767 | } | |
1e7a0e21 LP |
768 | |
769 | n += r; | |
770 | ||
771 | left -= 1 + *p; | |
772 | p += 1 + *p; | |
773 | } | |
774 | ||
775 | if (strv_isempty(l)) { | |
776 | *ret = NULL; | |
777 | return 0; | |
778 | } | |
779 | ||
1cc6c93a | 780 | *ret = TAKE_PTR(l); |
1e7a0e21 LP |
781 | |
782 | return k; | |
783 | } | |
784 | ||
6197db53 | 785 | int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) { |
1e7a0e21 LP |
786 | uint8_t *ri; |
787 | int r; | |
788 | ||
789 | assert_return(rt, -EINVAL); | |
3231f624 | 790 | assert_return(ret, -EINVAL); |
1e7a0e21 LP |
791 | |
792 | r = get_dnssl_info(rt, &ri); | |
793 | if (r < 0) | |
794 | return r; | |
795 | ||
6197db53 | 796 | *ret = unaligned_be32_sec_to_usec(ri + 4, /* max_as_infinity = */ true); |
1e7a0e21 LP |
797 | return 0; |
798 | } | |
9747955d | 799 | |
3231f624 | 800 | int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router *rt, const char **ret, size_t *ret_size) { |
9747955d RP |
801 | int r; |
802 | const char *nd_opt_captive_portal; | |
803 | size_t length; | |
804 | ||
805 | assert_return(rt, -EINVAL); | |
3231f624 YW |
806 | assert_return(ret, -EINVAL); |
807 | assert_return(ret_size, -EINVAL); | |
9747955d RP |
808 | |
809 | r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_CAPTIVE_PORTAL); | |
810 | if (r < 0) | |
811 | return r; | |
812 | if (r == 0) | |
813 | return -EMEDIUMTYPE; | |
814 | ||
815 | r = sd_ndisc_router_option_get_raw(rt, (void *)&nd_opt_captive_portal, &length); | |
816 | if (r < 0) | |
817 | return r; | |
818 | ||
819 | /* The length field has units of 8 octets */ | |
820 | assert(length % 8 == 0); | |
821 | if (length == 0) | |
822 | return -EBADMSG; | |
823 | ||
824 | /* Check that the message is not truncated by an embedded NUL. | |
825 | * NUL padding to a multiple of 8 is expected. */ | |
826 | size_t size = strnlen(nd_opt_captive_portal + 2, length - 2); | |
827 | if (DIV_ROUND_UP(size + 2, 8) != length / 8) | |
828 | return -EBADMSG; | |
829 | ||
830 | /* Let's not return an empty buffer */ | |
831 | if (size == 0) { | |
3231f624 | 832 | *ret = NULL; |
9747955d RP |
833 | *ret_size = 0; |
834 | return 0; | |
835 | } | |
836 | ||
3231f624 | 837 | *ret = nd_opt_captive_portal + 2; |
9747955d RP |
838 | *ret_size = size; |
839 | ||
840 | return 0; | |
841 | } | |
6e8f5e4c SS |
842 | |
843 | static int get_pref64_prefix_info(sd_ndisc_router *rt, struct nd_opt_prefix64_info **ret) { | |
844 | struct nd_opt_prefix64_info *ri; | |
845 | size_t length; | |
846 | int r; | |
847 | ||
848 | assert(rt); | |
849 | assert(ret); | |
850 | ||
851 | r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_PREF64); | |
852 | if (r < 0) | |
853 | return r; | |
854 | if (r == 0) | |
855 | return -EMEDIUMTYPE; | |
856 | ||
857 | length = NDISC_ROUTER_OPTION_LENGTH(rt); | |
858 | if (length != sizeof(struct nd_opt_prefix64_info)) | |
859 | return -EBADMSG; | |
860 | ||
861 | ri = (struct nd_opt_prefix64_info *) ((uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex); | |
862 | if (!pref64_option_verify(ri, length)) | |
863 | return -EBADMSG; | |
864 | ||
865 | *ret = ri; | |
866 | return 0; | |
867 | } | |
868 | ||
3231f624 | 869 | int sd_ndisc_router_prefix64_get_prefix(sd_ndisc_router *rt, struct in6_addr *ret) { |
6e8f5e4c SS |
870 | struct nd_opt_prefix64_info *pi; |
871 | struct in6_addr a = {}; | |
872 | unsigned prefixlen; | |
873 | int r; | |
874 | ||
875 | assert_return(rt, -EINVAL); | |
3231f624 | 876 | assert_return(ret, -EINVAL); |
6e8f5e4c SS |
877 | |
878 | r = get_pref64_prefix_info(rt, &pi); | |
879 | if (r < 0) | |
880 | return r; | |
881 | ||
882 | r = sd_ndisc_router_prefix64_get_prefixlen(rt, &prefixlen); | |
883 | if (r < 0) | |
884 | return r; | |
885 | ||
886 | memcpy(&a, pi->prefix, sizeof(pi->prefix)); | |
887 | in6_addr_mask(&a, prefixlen); | |
888 | /* extra safety check for refusing malformed prefix. */ | |
889 | if (memcmp(&a, pi->prefix, sizeof(pi->prefix)) != 0) | |
890 | return -EBADMSG; | |
891 | ||
3231f624 | 892 | *ret = a; |
6e8f5e4c SS |
893 | return 0; |
894 | } | |
895 | ||
896 | int sd_ndisc_router_prefix64_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) { | |
897 | struct nd_opt_prefix64_info *pi; | |
898 | uint16_t lifetime_prefix_len; | |
899 | uint8_t prefix_len; | |
900 | int r; | |
901 | ||
902 | assert_return(rt, -EINVAL); | |
903 | assert_return(ret, -EINVAL); | |
904 | ||
905 | r = get_pref64_prefix_info(rt, &pi); | |
906 | if (r < 0) | |
907 | return r; | |
908 | ||
909 | lifetime_prefix_len = be16toh(pi->lifetime_and_plc); | |
910 | pref64_plc_to_prefix_length(lifetime_prefix_len, &prefix_len); | |
911 | ||
912 | *ret = prefix_len; | |
913 | return 0; | |
914 | } | |
915 | ||
6197db53 | 916 | int sd_ndisc_router_prefix64_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) { |
6e8f5e4c SS |
917 | struct nd_opt_prefix64_info *pi; |
918 | uint16_t lifetime_prefix_len; | |
919 | int r; | |
920 | ||
921 | assert_return(rt, -EINVAL); | |
922 | assert_return(ret, -EINVAL); | |
923 | ||
924 | r = get_pref64_prefix_info(rt, &pi); | |
925 | if (r < 0) | |
926 | return r; | |
927 | ||
928 | lifetime_prefix_len = be16toh(pi->lifetime_and_plc); | |
929 | ||
6197db53 | 930 | *ret = (lifetime_prefix_len & PREF64_SCALED_LIFETIME_MASK) * USEC_PER_SEC; |
6e8f5e4c SS |
931 | return 0; |
932 | } |