]>
Commit | Line | Data |
---|---|---|
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ | |
2 | /*** | |
3 | Copyright © 2014 Intel Corporation. All rights reserved. | |
4 | ***/ | |
5 | ||
6 | #include <netinet/icmp6.h> | |
7 | ||
8 | #include "sd-ndisc.h" | |
9 | ||
10 | #include "alloc-util.h" | |
11 | #include "ndisc-internal.h" | |
12 | #include "ndisc-router-internal.h" | |
13 | #include "set.h" | |
14 | #include "string-table.h" | |
15 | #include "string-util.h" | |
16 | ||
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); | |
22 | set_free(rt->options); | |
23 | return mfree(rt); | |
24 | } | |
25 | ||
26 | DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router, sd_ndisc_router, ndisc_router_free); | |
27 | ||
28 | sd_ndisc_router* ndisc_router_new(ICMP6Packet *packet) { | |
29 | sd_ndisc_router *rt; | |
30 | ||
31 | assert(packet); | |
32 | ||
33 | rt = new(sd_ndisc_router, 1); | |
34 | if (!rt) | |
35 | return NULL; | |
36 | ||
37 | *rt = (sd_ndisc_router) { | |
38 | .n_ref = 1, | |
39 | .packet = icmp6_packet_ref(packet), | |
40 | .iterator = ITERATOR_FIRST, | |
41 | }; | |
42 | ||
43 | return rt; | |
44 | } | |
45 | ||
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 | ||
52 | int sd_ndisc_router_get_sender_address(sd_ndisc_router *rt, struct in6_addr *ret) { | |
53 | assert_return(rt, -EINVAL); | |
54 | ||
55 | return icmp6_packet_get_sender_address(rt->packet, ret); | |
56 | } | |
57 | ||
58 | int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret) { | |
59 | assert_return(rt, -EINVAL); | |
60 | assert_return(ret, -EINVAL); | |
61 | ||
62 | return icmp6_packet_get_timestamp(rt->packet, clock, ret); | |
63 | } | |
64 | ||
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); | |
96 | DEFINE_GET_TIMESTAMP(encrypted_dns_get_lifetime); | |
97 | ||
98 | int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) { | |
99 | const struct nd_router_advert *a; | |
100 | int r; | |
101 | ||
102 | assert(rt); | |
103 | assert(rt->packet); | |
104 | ||
105 | if (rt->packet->raw_size < sizeof(struct nd_router_advert)) | |
106 | return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), | |
107 | "Too small to be a router advertisement, ignoring."); | |
108 | ||
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); | |
112 | ||
113 | rt->hop_limit = a->nd_ra_curhoplimit; | |
114 | rt->flags = a->nd_ra_flags_reserved; /* the first 8 bits */ | |
115 | rt->lifetime_usec = be16_sec_to_usec(a->nd_ra_router_lifetime, /* max_as_infinity = */ false); | |
116 | rt->reachable_time_usec = be32_msec_to_usec(a->nd_ra_reachable, /* max_as_infinity = */ false); | |
117 | rt->retransmission_time_usec = be32_msec_to_usec(a->nd_ra_retransmit, /* max_as_infinity = */ false); | |
118 | ||
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). */ | |
125 | rt->preference = (rt->flags >> 3) & 3; | |
126 | if (rt->preference == SD_NDISC_PREFERENCE_RESERVED) | |
127 | rt->preference = SD_NDISC_PREFERENCE_MEDIUM; | |
128 | ||
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"); | |
132 | ||
133 | return 0; | |
134 | } | |
135 | ||
136 | int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret) { | |
137 | assert_return(rt, -EINVAL); | |
138 | assert_return(ret, -EINVAL); | |
139 | ||
140 | *ret = rt->hop_limit; | |
141 | return 0; | |
142 | } | |
143 | ||
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 | ||
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 | ||
160 | int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret) { | |
161 | assert_return(rt, -EINVAL); | |
162 | assert_return(ret, -EINVAL); | |
163 | ||
164 | sd_ndisc_option *p = ndisc_option_get_by_type(rt->options, SD_NDISC_OPTION_FLAGS_EXTENSION); | |
165 | ||
166 | *ret = rt->flags | (p ? p->extended_flags : 0); | |
167 | return 0; | |
168 | } | |
169 | ||
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 | ||
191 | int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) { | |
192 | assert_return(rt, -EINVAL); | |
193 | ||
194 | if (ret) | |
195 | *ret = rt->lifetime_usec; | |
196 | ||
197 | return rt->lifetime_usec > 0; /* Indicate if the router is still valid or not. */ | |
198 | } | |
199 | ||
200 | int sd_ndisc_router_get_preference(sd_ndisc_router *rt, uint8_t *ret) { | |
201 | assert_return(rt, -EINVAL); | |
202 | assert_return(ret, -EINVAL); | |
203 | ||
204 | *ret = rt->preference; | |
205 | return 0; | |
206 | } | |
207 | ||
208 | static const char* const ndisc_router_preference_table[] = { | |
209 | [SD_NDISC_PREFERENCE_LOW] = "low", | |
210 | [SD_NDISC_PREFERENCE_MEDIUM] = "medium", | |
211 | [SD_NDISC_PREFERENCE_HIGH] = "high", | |
212 | [SD_NDISC_PREFERENCE_RESERVED] = "reserved", | |
213 | }; | |
214 | ||
215 | DEFINE_STRING_TABLE_LOOKUP_TO_STRING(ndisc_router_preference, int); | |
216 | ||
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 | ||
223 | int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) { | |
224 | assert_return(rt, -EINVAL); | |
225 | assert_return(ret, -EINVAL); | |
226 | ||
227 | sd_ndisc_option *p = ndisc_option_get_by_type(rt->options, SD_NDISC_OPTION_MTU); | |
228 | if (!p) | |
229 | return -ENODATA; | |
230 | ||
231 | *ret = p->mtu; | |
232 | return 0; | |
233 | } | |
234 | ||
235 | int sd_ndisc_router_get_captive_portal(sd_ndisc_router *rt, const char **ret) { | |
236 | assert_return(rt, -EINVAL); | |
237 | assert_return(ret, -EINVAL); | |
238 | ||
239 | sd_ndisc_option *p = ndisc_option_get_by_type(rt->options, SD_NDISC_OPTION_CAPTIVE_PORTAL); | |
240 | if (!p) | |
241 | return -ENODATA; | |
242 | ||
243 | *ret = p->captive_portal; | |
244 | return 0; | |
245 | } | |
246 | ||
247 | int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) { | |
248 | assert_return(rt, -EINVAL); | |
249 | ||
250 | rt->iterator = ITERATOR_FIRST; | |
251 | return sd_ndisc_router_option_next(rt); | |
252 | } | |
253 | ||
254 | int sd_ndisc_router_option_next(sd_ndisc_router *rt) { | |
255 | assert_return(rt, -EINVAL); | |
256 | ||
257 | return set_iterate(rt->options, &rt->iterator, (void**) &rt->current_option); | |
258 | } | |
259 | ||
260 | int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) { | |
261 | assert_return(rt, -EINVAL); | |
262 | assert_return(ret, -EINVAL); | |
263 | ||
264 | if (!rt->current_option) | |
265 | return -ENODATA; | |
266 | ||
267 | *ret = rt->current_option->type; | |
268 | return 0; | |
269 | } | |
270 | ||
271 | int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) { | |
272 | uint8_t t; | |
273 | int r; | |
274 | ||
275 | assert_return(rt, -EINVAL); | |
276 | ||
277 | r = sd_ndisc_router_option_get_type(rt, &t); | |
278 | if (r < 0) | |
279 | return r; | |
280 | ||
281 | return t == type; | |
282 | } | |
283 | ||
284 | int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const uint8_t **ret, size_t *ret_size) { | |
285 | assert_return(rt, -EINVAL); | |
286 | ||
287 | if (!rt->current_option) | |
288 | return -ENODATA; | |
289 | ||
290 | return ndisc_option_parse(rt->packet, rt->current_option->offset, NULL, ret_size, ret); | |
291 | } | |
292 | ||
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 | } | |
312 | ||
313 | DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, flags, uint8_t); | |
314 | DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, prefixlen, uint8_t); | |
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); | |
318 | ||
319 | DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, preference, uint8_t); | |
320 | DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, prefixlen, uint8_t); | |
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); | |
323 | ||
324 | DEFINE_GETTER(rdnss, SD_NDISC_OPTION_RDNSS, lifetime, uint64_t); | |
325 | ||
326 | int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret) { | |
327 | int r; | |
328 | ||
329 | assert_return(rt, -EINVAL); | |
330 | assert_return(ret, -EINVAL); | |
331 | ||
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 | ||
338 | *ret = rt->current_option->rdnss.addresses; | |
339 | return (int) rt->current_option->rdnss.n_addresses; | |
340 | } | |
341 | ||
342 | DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, domains, char**); | |
343 | DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, lifetime, uint64_t); | |
344 | ||
345 | DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefixlen, uint8_t); | |
346 | DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefix, struct in6_addr); | |
347 | DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, lifetime, uint64_t); | |
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*); |