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