]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-ndisc-router.c
man/systemd-sysext: list ephemeral/ephemeral-import in the list of options
[thirdparty/systemd.git] / src / libsystemd-network / sd-ndisc-router.c
CommitLineData
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
17static 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
26DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router, sd_ndisc_router, ndisc_router_free);
1e7a0e21 27
c34cb1d6 28sd_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
46int 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 52int 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 58int 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
89DEFINE_GET_TIMESTAMP(get_lifetime);
90DEFINE_GET_TIMESTAMP(prefix_get_valid_lifetime);
91DEFINE_GET_TIMESTAMP(prefix_get_preferred_lifetime);
92DEFINE_GET_TIMESTAMP(route_get_lifetime);
93DEFINE_GET_TIMESTAMP(rdnss_get_lifetime);
94DEFINE_GET_TIMESTAMP(dnssl_get_lifetime);
95DEFINE_GET_TIMESTAMP(prefix64_get_lifetime);
0c90d1d2 96DEFINE_GET_TIMESTAMP(encrypted_dns_get_lifetime);
6197db53 97
35388783 98int 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 136int 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
144int 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
152int 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 160int 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
170int 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 191int 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 200int 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 208static 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
215DEFINE_STRING_TABLE_LOOKUP_TO_STRING(ndisc_router_preference, int);
216
b43c2221
YW
217int 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 223int 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 235int 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 247int 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 254int 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 260int 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
271int 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 284int 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 313DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, flags, uint8_t);
9ca04752 314DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, prefixlen, uint8_t);
c0edd6b3
YW
315DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, address, struct in6_addr);
316DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, valid_lifetime, uint64_t);
317DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, preferred_lifetime, uint64_t);
1e7a0e21 318
9ca04752
YW
319DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, preference, uint8_t);
320DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, prefixlen, uint8_t);
c0edd6b3
YW
321DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, address, struct in6_addr);
322DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, lifetime, uint64_t);
1e7a0e21 323
c0edd6b3 324DEFINE_GETTER(rdnss, SD_NDISC_OPTION_RDNSS, lifetime, uint64_t);
1e7a0e21 325
c0edd6b3 326int 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 342DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, domains, char**);
c0edd6b3 343DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, lifetime, uint64_t);
1e7a0e21 344
9ca04752 345DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefixlen, uint8_t);
c0edd6b3
YW
346DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefix, struct in6_addr);
347DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, lifetime, uint64_t);
0c90d1d2
RP
348
349DEFINE_GETTER(encrypted_dns, SD_NDISC_OPTION_ENCRYPTED_DNS, lifetime, uint64_t);
350DEFINE_GETTER(encrypted_dns, SD_NDISC_OPTION_ENCRYPTED_DNS, resolver, sd_dns_resolver*);