]>
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" | |
1e7a0e21 | 11 | #include "ndisc-internal.h" |
ca34b434 | 12 | #include "ndisc-router-internal.h" |
5cdf13c7 | 13 | #include "set.h" |
238ed432 | 14 | #include "string-table.h" |
5cdf13c7 | 15 | #include "string-util.h" |
1e7a0e21 | 16 | |
c34cb1d6 YW |
17 | static sd_ndisc_router* ndisc_router_free(sd_ndisc_router *rt) { |
18 | if (!rt) | |
19 | return NULL; | |
20 | ||
21 | icmp6_packet_unref(rt->packet); | |
c0edd6b3 | 22 | set_free(rt->options); |
c34cb1d6 YW |
23 | return mfree(rt); |
24 | } | |
25 | ||
26 | DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router, sd_ndisc_router, ndisc_router_free); | |
1e7a0e21 | 27 | |
c34cb1d6 | 28 | sd_ndisc_router* ndisc_router_new(ICMP6Packet *packet) { |
1e7a0e21 LP |
29 | sd_ndisc_router *rt; |
30 | ||
c34cb1d6 | 31 | assert(packet); |
4e88a46b | 32 | |
c34cb1d6 | 33 | rt = new(sd_ndisc_router, 1); |
1e7a0e21 LP |
34 | if (!rt) |
35 | return NULL; | |
36 | ||
c34cb1d6 YW |
37 | *rt = (sd_ndisc_router) { |
38 | .n_ref = 1, | |
39 | .packet = icmp6_packet_ref(packet), | |
c0edd6b3 | 40 | .iterator = ITERATOR_FIRST, |
c34cb1d6 | 41 | }; |
1e7a0e21 LP |
42 | |
43 | return rt; | |
44 | } | |
45 | ||
95d3570b YW |
46 | int sd_ndisc_router_set_sender_address(sd_ndisc_router *rt, const struct in6_addr *addr) { |
47 | assert_return(rt, -EINVAL); | |
48 | ||
49 | return icmp6_packet_set_sender_address(rt->packet, addr); | |
50 | } | |
51 | ||
9ca04752 | 52 | int sd_ndisc_router_get_sender_address(sd_ndisc_router *rt, struct in6_addr *ret) { |
1e7a0e21 | 53 | assert_return(rt, -EINVAL); |
1e7a0e21 | 54 | |
c34cb1d6 | 55 | return icmp6_packet_get_sender_address(rt->packet, ret); |
1e7a0e21 LP |
56 | } |
57 | ||
17347053 | 58 | int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret) { |
1e7a0e21 | 59 | assert_return(rt, -EINVAL); |
1e7a0e21 LP |
60 | assert_return(ret, -EINVAL); |
61 | ||
c34cb1d6 | 62 | return icmp6_packet_get_timestamp(rt->packet, clock, ret); |
1e7a0e21 LP |
63 | } |
64 | ||
6197db53 YW |
65 | #define DEFINE_GET_TIMESTAMP(name) \ |
66 | int sd_ndisc_router_##name##_timestamp( \ | |
67 | sd_ndisc_router *rt, \ | |
68 | clockid_t clock, \ | |
69 | uint64_t *ret) { \ | |
70 | \ | |
71 | usec_t s, t; \ | |
72 | int r; \ | |
73 | \ | |
74 | assert_return(rt, -EINVAL); \ | |
75 | assert_return(ret, -EINVAL); \ | |
76 | \ | |
77 | r = sd_ndisc_router_##name(rt, &s); \ | |
78 | if (r < 0) \ | |
79 | return r; \ | |
80 | \ | |
81 | r = sd_ndisc_router_get_timestamp(rt, clock, &t); \ | |
82 | if (r < 0) \ | |
83 | return r; \ | |
84 | \ | |
85 | *ret = time_span_to_stamp(s, t); \ | |
86 | return 0; \ | |
87 | } | |
88 | ||
89 | DEFINE_GET_TIMESTAMP(get_lifetime); | |
90 | DEFINE_GET_TIMESTAMP(prefix_get_valid_lifetime); | |
91 | DEFINE_GET_TIMESTAMP(prefix_get_preferred_lifetime); | |
92 | DEFINE_GET_TIMESTAMP(route_get_lifetime); | |
93 | DEFINE_GET_TIMESTAMP(rdnss_get_lifetime); | |
94 | DEFINE_GET_TIMESTAMP(dnssl_get_lifetime); | |
95 | DEFINE_GET_TIMESTAMP(prefix64_get_lifetime); | |
0c90d1d2 | 96 | DEFINE_GET_TIMESTAMP(encrypted_dns_get_lifetime); |
6197db53 | 97 | |
35388783 | 98 | int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) { |
c34cb1d6 | 99 | const struct nd_router_advert *a; |
c34cb1d6 | 100 | int r; |
1e7a0e21 LP |
101 | |
102 | assert(rt); | |
c34cb1d6 | 103 | assert(rt->packet); |
1e7a0e21 | 104 | |
c34cb1d6 | 105 | if (rt->packet->raw_size < sizeof(struct nd_router_advert)) |
35388783 YW |
106 | return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), |
107 | "Too small to be a router advertisement, ignoring."); | |
1e7a0e21 | 108 | |
c34cb1d6 YW |
109 | a = (const struct nd_router_advert*) rt->packet->raw_packet; |
110 | assert(a->nd_ra_type == ND_ROUTER_ADVERT); | |
111 | assert(a->nd_ra_code == 0); | |
1e7a0e21 LP |
112 | |
113 | rt->hop_limit = a->nd_ra_curhoplimit; | |
da890466 | 114 | rt->flags = a->nd_ra_flags_reserved; /* the first 8 bits */ |
6197db53 | 115 | rt->lifetime_usec = be16_sec_to_usec(a->nd_ra_router_lifetime, /* max_as_infinity = */ false); |
5cdec544 | 116 | rt->reachable_time_usec = be32_msec_to_usec(a->nd_ra_reachable, /* max_as_infinity = */ false); |
d4c8de21 | 117 | rt->retransmission_time_usec = be32_msec_to_usec(a->nd_ra_retransmit, /* max_as_infinity = */ false); |
1e7a0e21 | 118 | |
c0edd6b3 YW |
119 | /* RFC 4191 section 2.2 |
120 | * Prf (Default Router Preference) | |
121 | * 2-bit signed integer. Indicates whether to prefer this router over other default routers. If the | |
122 | * Router Lifetime is zero, the preference value MUST be set to (00) by the sender and MUST be | |
123 | * ignored by the receiver. If the Reserved (10) value is received, the receiver MUST treat the value | |
124 | * as if it were (00). */ | |
1e7a0e21 | 125 | rt->preference = (rt->flags >> 3) & 3; |
c0edd6b3 | 126 | if (rt->preference == SD_NDISC_PREFERENCE_RESERVED) |
1e7a0e21 LP |
127 | rt->preference = SD_NDISC_PREFERENCE_MEDIUM; |
128 | ||
c0edd6b3 YW |
129 | r = ndisc_parse_options(rt->packet, &rt->options); |
130 | if (r < 0) | |
131 | return log_ndisc_errno(nd, r, "Failed to parse NDisc options in router advertisement message, ignoring: %m"); | |
1e7a0e21 | 132 | |
1e7a0e21 LP |
133 | return 0; |
134 | } | |
135 | ||
17347053 | 136 | int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret) { |
1e7a0e21 LP |
137 | assert_return(rt, -EINVAL); |
138 | assert_return(ret, -EINVAL); | |
139 | ||
140 | *ret = rt->hop_limit; | |
141 | return 0; | |
142 | } | |
143 | ||
a68f007a YW |
144 | int sd_ndisc_router_get_reachable_time(sd_ndisc_router *rt, uint64_t *ret) { |
145 | assert_return(rt, -EINVAL); | |
146 | assert_return(ret, -EINVAL); | |
147 | ||
148 | *ret = rt->reachable_time_usec; | |
149 | return 0; | |
150 | } | |
151 | ||
d4c8de21 MM |
152 | int sd_ndisc_router_get_retransmission_time(sd_ndisc_router *rt, uint64_t *ret) { |
153 | assert_return(rt, -EINVAL); | |
154 | assert_return(ret, -EINVAL); | |
155 | ||
156 | *ret = rt->retransmission_time_usec; | |
157 | return 0; | |
158 | } | |
159 | ||
3231f624 | 160 | int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret) { |
1e7a0e21 | 161 | assert_return(rt, -EINVAL); |
3231f624 | 162 | assert_return(ret, -EINVAL); |
1e7a0e21 | 163 | |
697c3693 | 164 | sd_ndisc_option *p = ndisc_option_get_by_type(rt->options, SD_NDISC_OPTION_FLAGS_EXTENSION); |
c0edd6b3 YW |
165 | |
166 | *ret = rt->flags | (p ? p->extended_flags : 0); | |
1e7a0e21 LP |
167 | return 0; |
168 | } | |
169 | ||
238ed432 YW |
170 | int ndisc_router_flags_to_string(uint64_t flags, char **ret) { |
171 | _cleanup_free_ char *s = NULL; | |
172 | ||
173 | assert(ret); | |
174 | ||
175 | if (FLAGS_SET(flags, ND_RA_FLAG_MANAGED) && | |
176 | !strextend_with_separator(&s, ", ", "managed")) | |
177 | return -ENOMEM; | |
178 | ||
179 | if (FLAGS_SET(flags, ND_RA_FLAG_OTHER) && | |
180 | !strextend_with_separator(&s, ", ", "other")) | |
181 | return -ENOMEM; | |
182 | ||
183 | if (FLAGS_SET(flags, ND_RA_FLAG_HOME_AGENT) && | |
184 | !strextend_with_separator(&s, ", ", "home-agent")) | |
185 | return -ENOMEM; | |
186 | ||
187 | *ret = TAKE_PTR(s); | |
188 | return 0; | |
189 | } | |
190 | ||
6197db53 | 191 | int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) { |
1e7a0e21 | 192 | assert_return(rt, -EINVAL); |
1e7a0e21 | 193 | |
828b5dbf YW |
194 | if (ret) |
195 | *ret = rt->lifetime_usec; | |
196 | ||
197 | return rt->lifetime_usec > 0; /* Indicate if the router is still valid or not. */ | |
1e7a0e21 LP |
198 | } |
199 | ||
9ca04752 | 200 | int sd_ndisc_router_get_preference(sd_ndisc_router *rt, uint8_t *ret) { |
1e7a0e21 LP |
201 | assert_return(rt, -EINVAL); |
202 | assert_return(ret, -EINVAL); | |
203 | ||
204 | *ret = rt->preference; | |
205 | return 0; | |
206 | } | |
207 | ||
238ed432 | 208 | static const char* const ndisc_router_preference_table[] = { |
a925620f YW |
209 | [SD_NDISC_PREFERENCE_LOW] = "low", |
210 | [SD_NDISC_PREFERENCE_MEDIUM] = "medium", | |
211 | [SD_NDISC_PREFERENCE_HIGH] = "high", | |
212 | [SD_NDISC_PREFERENCE_RESERVED] = "reserved", | |
238ed432 YW |
213 | }; |
214 | ||
215 | DEFINE_STRING_TABLE_LOOKUP_TO_STRING(ndisc_router_preference, int); | |
216 | ||
b43c2221 YW |
217 | int sd_ndisc_router_get_sender_mac(sd_ndisc_router *rt, struct ether_addr *ret) { |
218 | assert_return(rt, -EINVAL); | |
219 | ||
220 | return ndisc_option_get_mac(rt->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, ret); | |
221 | } | |
222 | ||
17347053 | 223 | int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) { |
1e7a0e21 LP |
224 | assert_return(rt, -EINVAL); |
225 | assert_return(ret, -EINVAL); | |
226 | ||
697c3693 | 227 | sd_ndisc_option *p = ndisc_option_get_by_type(rt->options, SD_NDISC_OPTION_MTU); |
c0edd6b3 | 228 | if (!p) |
1e7a0e21 LP |
229 | return -ENODATA; |
230 | ||
c0edd6b3 | 231 | *ret = p->mtu; |
1e7a0e21 LP |
232 | return 0; |
233 | } | |
234 | ||
9ca04752 | 235 | int sd_ndisc_router_get_captive_portal(sd_ndisc_router *rt, const char **ret) { |
1e7a0e21 LP |
236 | assert_return(rt, -EINVAL); |
237 | assert_return(ret, -EINVAL); | |
238 | ||
697c3693 | 239 | sd_ndisc_option *p = ndisc_option_get_by_type(rt->options, SD_NDISC_OPTION_CAPTIVE_PORTAL); |
c0edd6b3 YW |
240 | if (!p) |
241 | return -ENODATA; | |
1e7a0e21 | 242 | |
c0edd6b3 | 243 | *ret = p->captive_portal; |
1e7a0e21 LP |
244 | return 0; |
245 | } | |
246 | ||
c0edd6b3 | 247 | int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) { |
1e7a0e21 | 248 | assert_return(rt, -EINVAL); |
9f702d00 | 249 | |
c0edd6b3 YW |
250 | rt->iterator = ITERATOR_FIRST; |
251 | return sd_ndisc_router_option_next(rt); | |
1e7a0e21 LP |
252 | } |
253 | ||
c0edd6b3 | 254 | int sd_ndisc_router_option_next(sd_ndisc_router *rt) { |
1e7a0e21 | 255 | assert_return(rt, -EINVAL); |
1e7a0e21 | 256 | |
c0edd6b3 | 257 | return set_iterate(rt->options, &rt->iterator, (void**) &rt->current_option); |
1e7a0e21 LP |
258 | } |
259 | ||
c0edd6b3 | 260 | int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) { |
1e7a0e21 LP |
261 | assert_return(rt, -EINVAL); |
262 | assert_return(ret, -EINVAL); | |
263 | ||
c0edd6b3 YW |
264 | if (!rt->current_option) |
265 | return -ENODATA; | |
1e7a0e21 | 266 | |
c0edd6b3 | 267 | *ret = rt->current_option->type; |
1e7a0e21 LP |
268 | return 0; |
269 | } | |
270 | ||
c0edd6b3 YW |
271 | int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) { |
272 | uint8_t t; | |
1e7a0e21 LP |
273 | int r; |
274 | ||
275 | assert_return(rt, -EINVAL); | |
1e7a0e21 | 276 | |
c0edd6b3 | 277 | r = sd_ndisc_router_option_get_type(rt, &t); |
1e7a0e21 LP |
278 | if (r < 0) |
279 | return r; | |
280 | ||
c0edd6b3 | 281 | return t == type; |
1e7a0e21 LP |
282 | } |
283 | ||
9ca04752 | 284 | int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const uint8_t **ret, size_t *ret_size) { |
1e7a0e21 | 285 | assert_return(rt, -EINVAL); |
1e7a0e21 | 286 | |
c0edd6b3 YW |
287 | if (!rt->current_option) |
288 | return -ENODATA; | |
1e7a0e21 | 289 | |
9ca04752 | 290 | return ndisc_option_parse(rt->packet, rt->current_option->offset, NULL, ret_size, ret); |
1e7a0e21 LP |
291 | } |
292 | ||
c0edd6b3 YW |
293 | #define DEFINE_GETTER(name, type, element, element_type) \ |
294 | int sd_ndisc_router_##name##_get_##element( \ | |
295 | sd_ndisc_router *rt, \ | |
296 | element_type *ret) { \ | |
297 | \ | |
298 | int r; \ | |
299 | \ | |
300 | assert_return(rt, -EINVAL); \ | |
301 | assert_return(ret, -EINVAL); \ | |
302 | \ | |
303 | r = sd_ndisc_router_option_is_type(rt, type); \ | |
304 | if (r < 0) \ | |
305 | return r; \ | |
306 | if (r == 0) \ | |
307 | return -EMEDIUMTYPE; \ | |
308 | \ | |
309 | *ret = rt->current_option->name.element; \ | |
310 | return 0; \ | |
311 | } | |
1e7a0e21 | 312 | |
c0edd6b3 | 313 | DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, flags, uint8_t); |
9ca04752 | 314 | DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, prefixlen, uint8_t); |
c0edd6b3 YW |
315 | DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, address, struct in6_addr); |
316 | DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, valid_lifetime, uint64_t); | |
317 | DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, preferred_lifetime, uint64_t); | |
1e7a0e21 | 318 | |
9ca04752 YW |
319 | DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, preference, uint8_t); |
320 | DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, prefixlen, uint8_t); | |
c0edd6b3 YW |
321 | DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, address, struct in6_addr); |
322 | DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, lifetime, uint64_t); | |
1e7a0e21 | 323 | |
c0edd6b3 | 324 | DEFINE_GETTER(rdnss, SD_NDISC_OPTION_RDNSS, lifetime, uint64_t); |
1e7a0e21 | 325 | |
c0edd6b3 | 326 | int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret) { |
1e7a0e21 LP |
327 | int r; |
328 | ||
329 | assert_return(rt, -EINVAL); | |
330 | assert_return(ret, -EINVAL); | |
331 | ||
1e7a0e21 LP |
332 | r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_RDNSS); |
333 | if (r < 0) | |
334 | return r; | |
335 | if (r == 0) | |
336 | return -EMEDIUMTYPE; | |
337 | ||
c0edd6b3 YW |
338 | *ret = rt->current_option->rdnss.addresses; |
339 | return (int) rt->current_option->rdnss.n_addresses; | |
1e7a0e21 LP |
340 | } |
341 | ||
9ca04752 | 342 | DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, domains, char**); |
c0edd6b3 | 343 | DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, lifetime, uint64_t); |
1e7a0e21 | 344 | |
9ca04752 | 345 | DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefixlen, uint8_t); |
c0edd6b3 YW |
346 | DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefix, struct in6_addr); |
347 | DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, lifetime, uint64_t); | |
0c90d1d2 RP |
348 | |
349 | DEFINE_GETTER(encrypted_dns, SD_NDISC_OPTION_ENCRYPTED_DNS, lifetime, uint64_t); | |
350 | DEFINE_GETTER(encrypted_dns, SD_NDISC_OPTION_ENCRYPTED_DNS, resolver, sd_dns_resolver*); |