]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-ndisc-router.c
Merge pull request #32326 from jonathan-conder/man_pam_loadkey
[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"
238ed432 13#include "string-table.h"
1e7a0e21 14
c34cb1d6
YW
15static 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
24DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router, sd_ndisc_router, ndisc_router_free);
1e7a0e21 25
c34cb1d6 26sd_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
44int 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 50int 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 56int 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
87DEFINE_GET_TIMESTAMP(get_lifetime);
88DEFINE_GET_TIMESTAMP(prefix_get_valid_lifetime);
89DEFINE_GET_TIMESTAMP(prefix_get_preferred_lifetime);
90DEFINE_GET_TIMESTAMP(route_get_lifetime);
91DEFINE_GET_TIMESTAMP(rdnss_get_lifetime);
92DEFINE_GET_TIMESTAMP(dnssl_get_lifetime);
93DEFINE_GET_TIMESTAMP(prefix64_get_lifetime);
94
35388783 95int 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 133int 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
141int 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
149int 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 157int 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
167int 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 188int 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 197int 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
205static 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
211DEFINE_STRING_TABLE_LOOKUP_TO_STRING(ndisc_router_preference, int);
212
b43c2221
YW
213int 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 219int 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 231int 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 243int 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 250int 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 256int 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
267int 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 280int 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 309DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, flags, uint8_t);
9ca04752 310DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, prefixlen, uint8_t);
c0edd6b3
YW
311DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, address, struct in6_addr);
312DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, valid_lifetime, uint64_t);
313DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, preferred_lifetime, uint64_t);
1e7a0e21 314
9ca04752
YW
315DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, preference, uint8_t);
316DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, prefixlen, uint8_t);
c0edd6b3
YW
317DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, address, struct in6_addr);
318DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, lifetime, uint64_t);
1e7a0e21 319
c0edd6b3 320DEFINE_GETTER(rdnss, SD_NDISC_OPTION_RDNSS, lifetime, uint64_t);
1e7a0e21 321
c0edd6b3 322int 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 338DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, domains, char**);
c0edd6b3 339DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, lifetime, uint64_t);
1e7a0e21 340
9ca04752 341DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefixlen, uint8_t);
c0edd6b3
YW
342DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefix, struct in6_addr);
343DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, lifetime, uint64_t);