Coverage Report

Created: 2026-04-29 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/systemd/src/libsystemd-network/ndisc-option.c
Line
Count
Source
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3
#include <linux/ipv6.h>
4
#include <netinet/icmp6.h>
5
6
#include "sd-ndisc-protocol.h"
7
8
#include "alloc-util.h"
9
#include "dns-def.h"
10
#include "dns-domain.h"
11
#include "dns-packet.h"
12
#include "dns-resolver-internal.h"
13
#include "ether-addr-util.h"
14
#include "hash-funcs.h"
15
#include "hostname-util.h"
16
#include "icmp6-packet.h"
17
#include "icmp6-util.h"
18
#include "in-addr-util.h"
19
#include "iovec-util.h"
20
#include "ndisc-option.h"
21
#include "network-common.h"
22
#include "set.h"
23
#include "siphash24.h"
24
#include "string-util.h"
25
#include "strv.h"
26
#include "unaligned.h"
27
28
/* RFC does not say anything about the maximum number of options, but let's limit the number of options for
29
 * safety. Typically, the number of options in an ICMPv6 message should be only a few. */
30
66.2k
#define MAX_OPTIONS 128
31
32
int ndisc_option_parse(
33
                ICMP6Packet *p,
34
                size_t offset,
35
                uint8_t *ret_type,
36
                size_t *ret_len,
37
88.8k
                const uint8_t **ret_opt) {
38
39
88.8k
        assert(p);
40
41
88.8k
        if (offset == p->raw_size)
42
0
                return -ESPIPE; /* end of the packet */
43
44
88.8k
        if (offset > p->raw_size)
45
0
                return -EBADMSG;
46
47
88.8k
        if (p->raw_size - offset < sizeof(struct nd_opt_hdr))
48
142
                return -EBADMSG;
49
50
88.7k
        assert_cc(alignof(struct nd_opt_hdr) == 1);
51
88.7k
        const struct nd_opt_hdr *hdr = (const struct nd_opt_hdr*) (p->raw_packet + offset);
52
88.7k
        if (hdr->nd_opt_len == 0)
53
57
                return -EBADMSG;
54
55
88.6k
        size_t len = hdr->nd_opt_len * 8;
56
88.6k
        if (p->raw_size - offset < len)
57
174
                return -EBADMSG;
58
59
88.5k
        if (ret_type)
60
88.5k
                *ret_type = hdr->nd_opt_type;
61
88.5k
        if (ret_len)
62
88.5k
                *ret_len = len;
63
88.5k
        if (ret_opt)
64
88.5k
                *ret_opt = p->raw_packet + offset;
65
66
88.5k
        return 0;
67
88.6k
}
68
69
75.4k
static sd_ndisc_option* ndisc_option_new(uint8_t type, size_t offset) {
70
75.4k
        sd_ndisc_option *p = new0(sd_ndisc_option, 1); /* use new0() here to make the fuzzers silent. */
71
75.4k
        if (!p)
72
0
                return NULL;
73
74
        /* As the same reason in the above, do not use the structured initializer here. */
75
75.4k
        p->type = type;
76
75.4k
        p->offset = offset;
77
78
75.4k
        return p;
79
75.4k
}
80
81
0
static void ndisc_raw_done(sd_ndisc_raw *raw) {
82
0
        if (!raw)
83
0
                return;
84
85
0
        free(raw->bytes);
86
0
}
87
88
1.06k
static void ndisc_rdnss_done(sd_ndisc_rdnss *rdnss) {
89
1.06k
        if (!rdnss)
90
0
                return;
91
92
1.06k
        free(rdnss->addresses);
93
1.06k
}
94
95
4.57k
static void ndisc_dnssl_done(sd_ndisc_dnssl *dnssl) {
96
4.57k
        if (!dnssl)
97
0
                return;
98
99
4.57k
        strv_free(dnssl->domains);
100
4.57k
}
101
102
1.23k
static void ndisc_dnr_done(sd_ndisc_dnr *dnr) {
103
1.23k
        if (!dnr)
104
0
                return;
105
106
1.23k
        sd_dns_resolver_unref(dnr->resolver);
107
1.23k
}
108
109
75.4k
sd_ndisc_option* ndisc_option_free(sd_ndisc_option *option) {
110
75.4k
        if (!option)
111
0
                return NULL;
112
113
75.4k
        switch (option->type) {
114
0
        case 0:
115
0
                ndisc_raw_done(&option->raw);
116
0
                break;
117
118
1.06k
        case SD_NDISC_OPTION_RDNSS:
119
1.06k
                ndisc_rdnss_done(&option->rdnss);
120
1.06k
                break;
121
122
4.57k
        case SD_NDISC_OPTION_DNSSL:
123
4.57k
                ndisc_dnssl_done(&option->dnssl);
124
4.57k
                break;
125
126
782
        case SD_NDISC_OPTION_CAPTIVE_PORTAL:
127
782
                free(option->captive_portal);
128
782
                break;
129
130
1.23k
        case SD_NDISC_OPTION_ENCRYPTED_DNS:
131
1.23k
                ndisc_dnr_done(&option->encrypted_dns);
132
1.23k
                break;
133
75.4k
        }
134
135
75.4k
        return mfree(option);
136
75.4k
}
137
138
193k
static int ndisc_option_compare_func(const sd_ndisc_option *x, const sd_ndisc_option *y) {
139
193k
        int r;
140
141
193k
        assert(x);
142
193k
        assert(y);
143
144
193k
        r = CMP(x->type, y->type);
145
193k
        if (r != 0)
146
56.2k
                return r;
147
148
136k
        switch (x->type) {
149
0
        case 0:
150
0
                return memcmp_nn(x->raw.bytes, x->raw.length, y->raw.bytes, y->raw.length);
151
152
7.01k
        case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
153
7.57k
        case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
154
7.72k
        case SD_NDISC_OPTION_REDIRECTED_HEADER:
155
8.19k
        case SD_NDISC_OPTION_MTU:
156
9.24k
        case SD_NDISC_OPTION_HOME_AGENT:
157
9.80k
        case SD_NDISC_OPTION_FLAGS_EXTENSION:
158
10.2k
        case SD_NDISC_OPTION_CAPTIVE_PORTAL:
159
                /* These options cannot be specified multiple times. */
160
10.2k
                return 0;
161
162
4.36k
        case SD_NDISC_OPTION_PREFIX_INFORMATION:
163
                /* Should not specify the same prefix multiple times. */
164
4.36k
                r = CMP(x->prefix.prefixlen, y->prefix.prefixlen);
165
4.36k
                if (r != 0)
166
2.05k
                        return r;
167
168
2.31k
                return memcmp(&x->prefix.address, &y->prefix.address, sizeof(struct in6_addr));
169
170
3.78k
        case SD_NDISC_OPTION_ROUTE_INFORMATION:
171
3.78k
                r = CMP(x->route.prefixlen, y->route.prefixlen);
172
3.78k
                if (r != 0)
173
1.77k
                        return r;
174
175
2.01k
                return memcmp(&x->route.address, &y->route.address, sizeof(struct in6_addr));
176
177
4.06k
        case SD_NDISC_OPTION_PREF64:
178
4.06k
                r = CMP(x->prefix64.prefixlen, y->prefix64.prefixlen);
179
4.06k
                if (r != 0)
180
2.18k
                        return r;
181
182
1.87k
                return memcmp(&x->prefix64.prefix, &y->prefix64.prefix, sizeof(struct in6_addr));
183
184
114k
        default:
185
                /* DNSSL, RDNSS, and other unsupported options can be specified multiple times. */
186
114k
                return trivial_compare_func(x, y);
187
136k
        }
188
136k
}
189
190
179k
static void ndisc_option_hash_func(const sd_ndisc_option *option, struct siphash *state) {
191
179k
        assert(option);
192
179k
        assert(state);
193
194
179k
        siphash24_compress_typesafe(option->type, state);
195
196
179k
        switch (option->type) {
197
0
        case 0:
198
0
                siphash24_compress(option->raw.bytes, option->raw.length, state);
199
0
                break;
200
201
18.7k
        case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
202
20.8k
        case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
203
21.2k
        case SD_NDISC_OPTION_REDIRECTED_HEADER:
204
24.5k
        case SD_NDISC_OPTION_MTU:
205
27.9k
        case SD_NDISC_OPTION_HOME_AGENT:
206
30.4k
        case SD_NDISC_OPTION_FLAGS_EXTENSION:
207
32.2k
        case SD_NDISC_OPTION_CAPTIVE_PORTAL:
208
32.2k
                break;
209
210
6.52k
        case SD_NDISC_OPTION_PREFIX_INFORMATION:
211
6.52k
                siphash24_compress_typesafe(option->prefix.prefixlen, state);
212
6.52k
                siphash24_compress_typesafe(option->prefix.address, state);
213
6.52k
                break;
214
215
7.74k
        case SD_NDISC_OPTION_ROUTE_INFORMATION:
216
7.74k
                siphash24_compress_typesafe(option->route.prefixlen, state);
217
7.74k
                siphash24_compress_typesafe(option->route.address, state);
218
7.74k
                break;
219
220
6.89k
        case SD_NDISC_OPTION_PREF64:
221
6.89k
                siphash24_compress_typesafe(option->prefix64.prefixlen, state);
222
6.89k
                siphash24_compress_typesafe(option->prefix64.prefix, state);
223
6.89k
                break;
224
225
126k
        default:
226
126k
                trivial_hash_func(option, state);
227
179k
        }
228
179k
}
229
230
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
231
                ndisc_option_hash_ops,
232
                sd_ndisc_option,
233
                ndisc_option_hash_func,
234
                ndisc_option_compare_func,
235
                ndisc_option_free);
236
237
66.2k
static int ndisc_option_consume(Set **options, sd_ndisc_option *p) {
238
66.2k
        assert(options);
239
66.2k
        assert(p);
240
241
66.2k
        if (set_size(*options) >= MAX_OPTIONS) {
242
1.68k
                ndisc_option_free(p);
243
1.68k
                return -ETOOMANYREFS; /* recognizable error code */
244
1.68k
        }
245
246
64.6k
        return set_ensure_consume(options, &ndisc_option_hash_ops, p);
247
66.2k
}
248
249
0
static int ndisc_option_build_raw(const sd_ndisc_option *option, uint8_t **ret) {
250
0
        assert(option);
251
0
        assert(option->type == 0);
252
0
        assert(ret);
253
254
0
        _cleanup_free_ uint8_t *buf = newdup(uint8_t, option->raw.bytes, option->raw.length);
255
0
        if (!buf)
256
0
                return -ENOMEM;
257
258
0
        *ret = TAKE_PTR(buf);
259
0
        return 0;
260
0
}
261
262
12.7k
int ndisc_option_add_link_layer_address(Set **options, uint8_t type, size_t offset, const struct ether_addr *mac) {
263
12.7k
        assert(options);
264
12.7k
        assert(IN_SET(type, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS));
265
266
12.7k
        if (!mac || ether_addr_is_null(mac)) {
267
0
                ndisc_option_remove_by_type(*options, type);
268
0
                return 0;
269
0
        }
270
271
12.7k
        sd_ndisc_option *p = ndisc_option_get_by_type(*options, type);
272
12.7k
        if (p) {
273
                /* offset == 0 means that we are now building a packet to be sent, and in that case we allow
274
                 * to override the option we previously set.
275
                 * offset != 0 means that we are now parsing a received packet, and we refuse to override
276
                 * conflicting options. */
277
3.53k
                if (offset != 0)
278
3.53k
                        return -EEXIST;
279
280
0
                p->mac = *mac;
281
0
                return 0;
282
3.53k
        }
283
284
9.20k
        p = ndisc_option_new(type, offset);
285
9.20k
        if (!p)
286
0
                return -ENOMEM;
287
288
9.20k
        p->mac = *mac;
289
290
9.20k
        return set_ensure_consume(options, &ndisc_option_hash_ops, p);
291
9.20k
}
292
293
5.94k
static int ndisc_option_parse_link_layer_address(Set **options, size_t offset, size_t len, const uint8_t *opt) {
294
5.94k
        assert(options);
295
5.94k
        assert(opt);
296
297
5.94k
        if (len != sizeof(struct ether_addr) + 2)
298
201
                return -EBADMSG;
299
300
5.74k
        if (!IN_SET(opt[0], SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS))
301
0
                return -EBADMSG;
302
303
5.74k
        struct ether_addr mac;
304
5.74k
        memcpy(&mac, opt + 2, sizeof(struct ether_addr));
305
306
5.74k
        if (ether_addr_is_null(&mac))
307
472
                return -EBADMSG;
308
309
5.27k
        return ndisc_option_add_link_layer_address(options, opt[0], offset, &mac);
310
5.74k
}
311
312
7.16k
static int ndisc_option_build_link_layer_address(const sd_ndisc_option *option, uint8_t **ret) {
313
7.16k
        assert(option);
314
7.16k
        assert(IN_SET(option->type, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS));
315
7.16k
        assert(ret);
316
317
7.16k
        assert_cc(2 + sizeof(struct ether_addr) == 8);
318
319
7.16k
        _cleanup_free_ uint8_t *buf = new(uint8_t, 2 + sizeof(struct ether_addr));
320
7.16k
        if (!buf)
321
0
                return -ENOMEM;
322
323
7.16k
        buf[0] = option->type;
324
7.16k
        buf[1] = 1;
325
7.16k
        memcpy(buf + 2, &option->mac, sizeof(struct ether_addr));
326
327
7.16k
        *ret = TAKE_PTR(buf);
328
7.16k
        return 0;
329
7.16k
}
330
331
int ndisc_option_add_prefix_internal(
332
                Set **options,
333
                size_t offset,
334
                uint8_t flags,
335
                uint8_t prefixlen,
336
                const struct in6_addr *address,
337
                usec_t valid_lifetime,
338
                usec_t preferred_lifetime,
339
                usec_t valid_until,
340
3.26k
                usec_t preferred_until) {
341
342
3.26k
        assert(options);
343
3.26k
        assert(address);
344
345
3.26k
        if (prefixlen > 128)
346
66
                return -EINVAL;
347
348
3.20k
        struct in6_addr addr = *address;
349
3.20k
        in6_addr_mask(&addr, prefixlen);
350
351
        /* RFC 4861 and 4862 only state that link-local prefix should be ignored.
352
         * But here we also ignore null and multicast addresses. */
353
3.20k
        if (in6_addr_is_link_local(&addr) || in6_addr_is_null(&addr) || in6_addr_is_multicast(&addr))
354
220
                return -EINVAL;
355
356
2.98k
        if (preferred_lifetime > valid_lifetime)
357
159
                return -EINVAL;
358
359
2.82k
        if (preferred_until > valid_until)
360
0
                return -EINVAL;
361
362
2.82k
        sd_ndisc_option *p = ndisc_option_get(
363
2.82k
                                *options,
364
2.82k
                                &(const sd_ndisc_option) {
365
2.82k
                                        .type = SD_NDISC_OPTION_PREFIX_INFORMATION,
366
2.82k
                                        .prefix.prefixlen = prefixlen,
367
2.82k
                                        .prefix.address = addr,
368
2.82k
                                });
369
2.82k
        if (p) {
370
167
                if (offset != 0)
371
167
                        return -EEXIST;
372
373
0
                p->prefix.flags = flags;
374
0
                p->prefix.valid_lifetime = valid_lifetime;
375
0
                p->prefix.preferred_lifetime = preferred_lifetime;
376
0
                p->prefix.valid_until = valid_until;
377
0
                p->prefix.preferred_until = preferred_until;
378
0
                return 0;
379
167
        }
380
381
2.65k
        p = ndisc_option_new(SD_NDISC_OPTION_PREFIX_INFORMATION, offset);
382
2.65k
        if (!p)
383
0
                return -ENOMEM;
384
385
2.65k
        p->prefix = (sd_ndisc_prefix) {
386
2.65k
                .flags = flags,
387
2.65k
                .prefixlen = prefixlen,
388
2.65k
                .address = addr,
389
2.65k
                .valid_lifetime = valid_lifetime,
390
2.65k
                .preferred_lifetime = preferred_lifetime,
391
2.65k
                .valid_until = valid_until,
392
2.65k
                .preferred_until = preferred_until,
393
2.65k
        };
394
395
2.65k
        return ndisc_option_consume(options, p);
396
2.65k
}
397
398
3.47k
static int ndisc_option_parse_prefix(Set **options, size_t offset, size_t len, const uint8_t *opt) {
399
3.47k
        const struct nd_opt_prefix_info *pi = (const struct nd_opt_prefix_info*) ASSERT_PTR(opt);
400
401
3.47k
        assert(options);
402
403
3.47k
        if (len != sizeof(struct nd_opt_prefix_info))
404
210
                return -EBADMSG;
405
406
3.26k
        if (pi->nd_opt_pi_type != SD_NDISC_OPTION_PREFIX_INFORMATION)
407
0
                return -EBADMSG;
408
409
3.26k
        usec_t valid = be32_sec_to_usec(pi->nd_opt_pi_valid_time, /* max_as_infinity= */ true);
410
3.26k
        usec_t pref = be32_sec_to_usec(pi->nd_opt_pi_preferred_time, /* max_as_infinity= */ true);
411
412
        /* We only support 64 bits interface identifier for addrconf. */
413
3.26k
        uint8_t flags = pi->nd_opt_pi_flags_reserved;
414
3.26k
        if (FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO) && pi->nd_opt_pi_prefix_len != 64)
415
1.22k
                flags &= ~ND_OPT_PI_FLAG_AUTO;
416
417
3.26k
        return ndisc_option_add_prefix(options, offset, flags,
418
3.26k
                                       pi->nd_opt_pi_prefix_len, &pi->nd_opt_pi_prefix,
419
3.26k
                                       valid, pref);
420
3.26k
}
421
422
843
static int ndisc_option_build_prefix(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
423
843
        assert(option);
424
843
        assert(option->type == SD_NDISC_OPTION_PREFIX_INFORMATION);
425
843
        assert(ret);
426
427
843
        assert_cc(sizeof(struct nd_opt_prefix_info) % 8 == 0);
428
429
843
        _cleanup_free_ struct nd_opt_prefix_info *buf = new(struct nd_opt_prefix_info, 1);
430
843
        if (!buf)
431
0
                return -ENOMEM;
432
433
843
        usec_t valid = MIN(option->prefix.valid_lifetime,
434
843
                           usec_sub_unsigned(option->prefix.valid_until, timestamp));
435
843
        usec_t pref = MIN3(valid,
436
843
                           option->prefix.preferred_lifetime,
437
843
                           usec_sub_unsigned(option->prefix.preferred_until, timestamp));
438
439
843
        *buf = (struct nd_opt_prefix_info) {
440
843
                .nd_opt_pi_type = SD_NDISC_OPTION_PREFIX_INFORMATION,
441
843
                .nd_opt_pi_len = sizeof(struct nd_opt_prefix_info) / 8,
442
843
                .nd_opt_pi_prefix_len = option->prefix.prefixlen,
443
843
                .nd_opt_pi_flags_reserved = option->prefix.flags,
444
843
                .nd_opt_pi_valid_time = usec_to_be32_sec(valid),
445
843
                .nd_opt_pi_preferred_time = usec_to_be32_sec(pref),
446
843
                .nd_opt_pi_prefix = option->prefix.address,
447
843
        };
448
449
843
        *ret = (uint8_t*) TAKE_PTR(buf);
450
843
        return 0;
451
843
}
452
453
295
int ndisc_option_add_redirected_header(Set **options, size_t offset, const struct ip6_hdr *hdr) {
454
295
        assert(options);
455
456
295
        if (!hdr) {
457
0
                ndisc_option_remove_by_type(*options, SD_NDISC_OPTION_REDIRECTED_HEADER);
458
0
                return 0;
459
0
        }
460
461
295
        sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_REDIRECTED_HEADER);
462
295
        if (p) {
463
146
                if (offset != 0)
464
146
                        return -EEXIST;
465
466
0
                memcpy(&p->hdr, hdr, sizeof(struct ip6_hdr));
467
0
                return 0;
468
146
        }
469
470
149
        p = ndisc_option_new(SD_NDISC_OPTION_REDIRECTED_HEADER, offset);
471
149
        if (!p)
472
0
                return -ENOMEM;
473
474
        /* For safety, here we copy only IPv6 header. */
475
149
        memcpy(&p->hdr, hdr, sizeof(struct ip6_hdr));
476
477
149
        return ndisc_option_consume(options, p);
478
149
}
479
480
501
static int ndisc_option_parse_redirected_header(Set **options, size_t offset, size_t len, const uint8_t *opt) {
481
501
        assert(options);
482
501
        assert(opt);
483
484
501
        if (len < sizeof(struct nd_opt_rd_hdr) + sizeof(struct ip6_hdr))
485
206
                return -EBADMSG;
486
487
295
        if (opt[0] != SD_NDISC_OPTION_REDIRECTED_HEADER)
488
0
                return -EBADMSG;
489
490
295
        return ndisc_option_add_redirected_header(options, offset, (const struct ip6_hdr*) (opt + sizeof(struct nd_opt_rd_hdr)));
491
295
}
492
493
26
static int ndisc_option_build_redirected_header(const sd_ndisc_option *option, uint8_t **ret) {
494
26
        assert(option);
495
26
        assert(option->type == SD_NDISC_OPTION_REDIRECTED_HEADER);
496
26
        assert(ret);
497
498
26
        assert_cc((sizeof(struct nd_opt_rd_hdr) + sizeof(struct ip6_hdr)) % 8 == 0);
499
500
26
        size_t len = DIV_ROUND_UP(sizeof(struct nd_opt_rd_hdr) + sizeof(struct ip6_hdr), 8);
501
502
26
        _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
503
26
        if (!buf)
504
0
                return -ENOMEM;
505
506
26
        uint8_t *p;
507
26
        p = mempcpy(buf,
508
26
                    &(const struct nd_opt_rd_hdr) {
509
26
                            .nd_opt_rh_type = SD_NDISC_OPTION_REDIRECTED_HEADER,
510
26
                            .nd_opt_rh_len = len,
511
26
                    },
512
26
                    sizeof(struct nd_opt_rd_hdr));
513
26
        memcpy(p, &option->hdr, sizeof(struct ip6_hdr));
514
515
26
        *ret = TAKE_PTR(buf);
516
26
        return 0;
517
26
}
518
519
1.75k
int ndisc_option_add_mtu(Set **options, size_t offset, uint32_t mtu) {
520
1.75k
        assert(options);
521
522
1.75k
        if (mtu < IPV6_MIN_MTU)
523
220
                return -EINVAL;
524
525
1.53k
        sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_MTU);
526
1.53k
        if (p) {
527
474
                if (offset != 0)
528
474
                        return -EEXIST;
529
530
0
                p->mtu = mtu;
531
0
                return 0;
532
474
        }
533
534
1.05k
        p = ndisc_option_new(SD_NDISC_OPTION_MTU, offset);
535
1.05k
        if (!p)
536
0
                return -ENOMEM;
537
538
1.05k
        p->mtu = mtu;
539
540
1.05k
        return ndisc_option_consume(options, p);
541
1.05k
}
542
543
1.94k
static int ndisc_option_parse_mtu(Set **options, size_t offset, size_t len, const uint8_t *opt) {
544
1.94k
        const struct nd_opt_mtu *pm = (const struct nd_opt_mtu*) ASSERT_PTR(opt);
545
546
1.94k
        assert(options);
547
548
1.94k
        if (len != sizeof(struct nd_opt_mtu))
549
194
                return -EBADMSG;
550
551
1.75k
        if (pm->nd_opt_mtu_type != SD_NDISC_OPTION_MTU)
552
0
                return -EBADMSG;
553
554
1.75k
        return ndisc_option_add_mtu(options, offset, be32toh(pm->nd_opt_mtu_mtu));
555
1.75k
}
556
557
273
static int ndisc_option_build_mtu(const sd_ndisc_option *option, uint8_t **ret) {
558
273
        assert(option);
559
273
        assert(option->type == SD_NDISC_OPTION_MTU);
560
273
        assert(ret);
561
562
273
        assert_cc(sizeof(struct nd_opt_mtu) % 8 == 0);
563
564
273
        _cleanup_free_ struct nd_opt_mtu *buf = new(struct nd_opt_mtu, 1);
565
273
        if (!buf)
566
0
                return -ENOMEM;
567
568
273
        *buf = (struct nd_opt_mtu) {
569
273
                .nd_opt_mtu_type = SD_NDISC_OPTION_MTU,
570
273
                .nd_opt_mtu_len = sizeof(struct nd_opt_mtu) / 8,
571
273
                .nd_opt_mtu_mtu = htobe32(option->mtu),
572
273
        };
573
574
273
        *ret = (uint8_t*) TAKE_PTR(buf);
575
273
        return 0;
576
273
}
577
578
int ndisc_option_add_home_agent_internal(
579
                Set **options,
580
                size_t offset,
581
                uint16_t preference,
582
                usec_t lifetime,
583
2.17k
                usec_t valid_until) {
584
585
2.17k
        assert(options);
586
587
2.17k
        if (lifetime > UINT16_MAX * USEC_PER_SEC)
588
0
                return -EINVAL;
589
590
2.17k
        sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_HOME_AGENT);
591
2.17k
        if (p) {
592
1.05k
                if (offset != 0)
593
1.05k
                        return -EEXIST;
594
595
0
                p->home_agent = (sd_ndisc_home_agent) {
596
0
                        .preference = preference,
597
0
                        .lifetime = lifetime,
598
0
                        .valid_until = valid_until,
599
0
                };
600
0
                return 0;
601
1.05k
        }
602
603
1.12k
        p = ndisc_option_new(SD_NDISC_OPTION_HOME_AGENT, offset);
604
1.12k
        if (!p)
605
0
                return -ENOMEM;
606
607
1.12k
        p->home_agent = (sd_ndisc_home_agent) {
608
1.12k
                .preference = preference,
609
1.12k
                .lifetime = lifetime,
610
1.12k
                .valid_until = valid_until,
611
1.12k
        };
612
613
1.12k
        return ndisc_option_consume(options, p);
614
1.12k
}
615
616
2.37k
static int ndisc_option_parse_home_agent(Set **options, size_t offset, size_t len, const uint8_t *opt) {
617
2.37k
        const struct nd_opt_home_agent_info *p = (const struct nd_opt_home_agent_info*) ASSERT_PTR(opt);
618
619
2.37k
        assert(options);
620
621
2.37k
        if (len != sizeof(struct nd_opt_home_agent_info))
622
198
                return -EBADMSG;
623
624
2.17k
        if (p->nd_opt_home_agent_info_type != SD_NDISC_OPTION_HOME_AGENT)
625
0
                return -EBADMSG;
626
627
2.17k
        return ndisc_option_add_home_agent(
628
2.17k
                        options, offset,
629
2.17k
                        be16toh(p->nd_opt_home_agent_info_preference),
630
2.17k
                        be16_sec_to_usec(p->nd_opt_home_agent_info_lifetime, /* max_as_infinity= */ false));
631
2.17k
}
632
633
304
static int ndisc_option_build_home_agent(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
634
304
        assert(option);
635
304
        assert(option->type == SD_NDISC_OPTION_HOME_AGENT);
636
304
        assert(ret);
637
638
304
        assert_cc(sizeof(struct nd_opt_home_agent_info) % 8 == 0);
639
640
304
        usec_t lifetime = MIN(option->home_agent.lifetime,
641
304
                              usec_sub_unsigned(option->home_agent.valid_until, timestamp));
642
643
304
        _cleanup_free_ struct nd_opt_home_agent_info *buf = new(struct nd_opt_home_agent_info, 1);
644
304
        if (!buf)
645
0
                return -ENOMEM;
646
647
304
        *buf = (struct nd_opt_home_agent_info) {
648
304
                .nd_opt_home_agent_info_type = SD_NDISC_OPTION_HOME_AGENT,
649
304
                .nd_opt_home_agent_info_len = sizeof(struct nd_opt_home_agent_info) / 8,
650
304
                .nd_opt_home_agent_info_preference = htobe16(option->home_agent.preference),
651
304
                .nd_opt_home_agent_info_lifetime = usec_to_be16_sec(lifetime),
652
304
        };
653
654
304
        *ret = (uint8_t*) TAKE_PTR(buf);
655
304
        return 0;
656
304
}
657
658
int ndisc_option_add_route_internal(
659
                Set **options,
660
                size_t offset,
661
                uint8_t preference,
662
                uint8_t prefixlen,
663
                const struct in6_addr *prefix,
664
                usec_t lifetime,
665
4.11k
                usec_t valid_until) {
666
667
4.11k
        assert(options);
668
4.11k
        assert(prefix);
669
670
4.11k
        if (prefixlen > 128)
671
0
                return -EINVAL;
672
673
        /* RFC 4191 section 2.3
674
         * Prf (Route Preference)
675
         * 2-bit signed integer. The Route Preference indicates whether to prefer the router associated with
676
         * this prefix over others, when multiple identical prefixes (for different routers) have been
677
         * received. If the Reserved (10) value is received, the Route Information Option MUST be ignored. */
678
4.11k
        if (!IN_SET(preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH))
679
304
                return -EINVAL;
680
681
3.81k
        struct in6_addr addr = *prefix;
682
3.81k
        in6_addr_mask(&addr, prefixlen);
683
684
3.81k
        sd_ndisc_option *p = ndisc_option_get(
685
3.81k
                                *options,
686
3.81k
                                &(const sd_ndisc_option) {
687
3.81k
                                        .type = SD_NDISC_OPTION_ROUTE_INFORMATION,
688
3.81k
                                        .route.prefixlen = prefixlen,
689
3.81k
                                        .route.address = addr,
690
3.81k
                                });
691
3.81k
        if (p) {
692
863
                if (offset != 0)
693
863
                        return -EEXIST;
694
695
0
                p->route.preference = preference;
696
0
                p->route.lifetime = lifetime;
697
0
                p->route.valid_until = valid_until;
698
0
                return 0;
699
863
        }
700
701
2.95k
        p = ndisc_option_new(SD_NDISC_OPTION_ROUTE_INFORMATION, offset);
702
2.95k
        if (!p)
703
0
                return -ENOMEM;
704
705
2.95k
        p->route = (sd_ndisc_route) {
706
2.95k
                .preference = preference,
707
2.95k
                .prefixlen = prefixlen,
708
2.95k
                .address = addr,
709
2.95k
                .lifetime = lifetime,
710
2.95k
                .valid_until = valid_until,
711
2.95k
        };
712
713
2.95k
        return ndisc_option_consume(options, p);
714
2.95k
}
715
716
4.61k
static int ndisc_option_parse_route(Set **options, size_t offset, size_t len, const uint8_t *opt) {
717
4.61k
        assert(options);
718
4.61k
        assert(opt);
719
720
4.61k
        if (!IN_SET(len, 1*8, 2*8, 3*8))
721
75
                return -EBADMSG;
722
723
4.53k
        if (opt[0] != SD_NDISC_OPTION_ROUTE_INFORMATION)
724
0
                return -EBADMSG;
725
726
4.53k
        uint8_t prefixlen = opt[2];
727
4.53k
        if (prefixlen > 128)
728
200
                return -EBADMSG;
729
730
4.33k
        if (len < (size_t) (DIV_ROUND_UP(prefixlen, 64) + 1) * 8)
731
220
                return -EBADMSG;
732
733
4.11k
        uint8_t preference = (opt[3] >> 3) & 0x03;
734
4.11k
        usec_t lifetime = unaligned_be32_sec_to_usec(opt + 4, /* max_as_infinity= */ true);
735
736
4.11k
        struct in6_addr prefix;
737
4.11k
        memcpy_safe(&prefix, opt + 8, len - 8);
738
4.11k
        in6_addr_mask(&prefix, prefixlen);
739
740
4.11k
        return ndisc_option_add_route(options, offset, preference, prefixlen, &prefix, lifetime);
741
4.33k
}
742
743
905
static int ndisc_option_build_route(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
744
905
        assert(option);
745
905
        assert(option->type == SD_NDISC_OPTION_ROUTE_INFORMATION);
746
905
        assert(option->route.prefixlen <= 128);
747
905
        assert(ret);
748
749
905
        size_t len = 1 + DIV_ROUND_UP(option->route.prefixlen, 64);
750
905
        be32_t lifetime = usec_to_be32_sec(MIN(option->route.lifetime,
751
905
                                               usec_sub_unsigned(option->route.valid_until, timestamp)));
752
753
905
        _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
754
905
        if (!buf)
755
0
                return -ENOMEM;
756
757
905
        buf[0] = SD_NDISC_OPTION_ROUTE_INFORMATION;
758
905
        buf[1] = len;
759
905
        buf[2] = option->route.prefixlen;
760
905
        buf[3] = option->route.preference << 3;
761
905
        memcpy(buf + 4, &lifetime, sizeof(be32_t));
762
905
        memcpy_safe(buf + 8, &option->route.address, (len - 1) * 8);
763
764
905
        *ret = TAKE_PTR(buf);
765
905
        return 0;
766
905
}
767
768
int ndisc_option_add_rdnss_internal(
769
                Set **options,
770
                size_t offset,
771
                size_t n_addresses,
772
                const struct in6_addr *addresses,
773
                usec_t lifetime,
774
1.06k
                usec_t valid_until) {
775
776
1.06k
        assert(options);
777
1.06k
        assert(addresses);
778
779
1.06k
        if (n_addresses == 0)
780
0
                return -EINVAL;
781
782
1.06k
        _cleanup_free_ struct in6_addr *addrs = newdup(struct in6_addr, addresses, n_addresses);
783
1.06k
        if (!addrs)
784
0
                return -ENOMEM;
785
786
1.06k
        sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_RDNSS, offset);
787
1.06k
        if (!p)
788
0
                return -ENOMEM;
789
790
1.06k
        p->rdnss = (sd_ndisc_rdnss) {
791
1.06k
                .n_addresses = n_addresses,
792
1.06k
                .addresses = TAKE_PTR(addrs),
793
1.06k
                .lifetime = lifetime,
794
1.06k
                .valid_until = valid_until,
795
1.06k
        };
796
797
1.06k
        return ndisc_option_consume(options, p);
798
1.06k
}
799
800
1.33k
static int ndisc_option_parse_rdnss(Set **options, size_t offset, size_t len, const uint8_t *opt) {
801
1.33k
        assert(options);
802
1.33k
        assert(opt);
803
804
1.33k
        if (len < 8 + sizeof(struct in6_addr) || (len % sizeof(struct in6_addr)) != 8)
805
276
                return -EBADMSG;
806
807
1.06k
        if (opt[0] != SD_NDISC_OPTION_RDNSS)
808
0
                return -EBADMSG;
809
810
1.06k
        usec_t lifetime = unaligned_be32_sec_to_usec(opt + 4, /* max_as_infinity= */ true);
811
1.06k
        size_t n_addrs = len / sizeof(struct in6_addr);
812
813
1.06k
        return ndisc_option_add_rdnss(options, offset, n_addrs, (const struct in6_addr*) (opt + 8), lifetime);
814
1.06k
}
815
816
331
static int ndisc_option_build_rdnss(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
817
331
        assert(option);
818
331
        assert(option->type == SD_NDISC_OPTION_RDNSS);
819
331
        assert(ret);
820
821
331
        size_t len = option->rdnss.n_addresses * 2 + 1;
822
331
        be32_t lifetime = usec_to_be32_sec(MIN(option->rdnss.lifetime,
823
331
                                               usec_sub_unsigned(option->rdnss.valid_until, timestamp)));
824
825
331
        _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
826
331
        if (!buf)
827
0
                return -ENOMEM;
828
829
331
        buf[0] = SD_NDISC_OPTION_RDNSS;
830
331
        buf[1] = len;
831
331
        buf[2] = 0;
832
331
        buf[3] = 0;
833
331
        memcpy(buf + 4, &lifetime, sizeof(be32_t));
834
331
        memcpy(buf + 8, option->rdnss.addresses, sizeof(struct in6_addr) * option->rdnss.n_addresses);
835
836
331
        *ret = TAKE_PTR(buf);
837
331
        return 0;
838
331
}
839
840
1.57k
int ndisc_option_add_flags_extension(Set **options, size_t offset, uint64_t flags) {
841
1.57k
        assert(options);
842
843
1.57k
        if ((flags & UINT64_C(0x00ffffffffffff00)) != flags)
844
0
                return -EINVAL;
845
846
1.57k
        sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_FLAGS_EXTENSION);
847
1.57k
        if (p) {
848
560
                if (offset != 0)
849
560
                        return -EEXIST;
850
851
0
                p->extended_flags = flags;
852
0
                return 0;
853
560
        }
854
855
1.01k
        p = ndisc_option_new(SD_NDISC_OPTION_FLAGS_EXTENSION, offset);
856
1.01k
        if (!p)
857
0
                return -ENOMEM;
858
859
1.01k
        p->extended_flags = flags;
860
861
1.01k
        return ndisc_option_consume(options, p);
862
1.01k
}
863
864
1.77k
static int ndisc_option_parse_flags_extension(Set **options, size_t offset, size_t len, const uint8_t *opt) {
865
1.77k
        assert(options);
866
1.77k
        assert(opt);
867
868
1.77k
        if (len != 8)
869
206
                return -EBADMSG;
870
871
1.57k
        if (opt[0] != SD_NDISC_OPTION_FLAGS_EXTENSION)
872
0
                return -EBADMSG;
873
874
1.57k
        uint64_t flags = (unaligned_read_be64(opt) & UINT64_C(0xffffffffffff0000)) >> 8;
875
1.57k
        return ndisc_option_add_flags_extension(options, offset, flags);
876
1.57k
}
877
878
225
static int ndisc_option_build_flags_extension(const sd_ndisc_option *option, uint8_t **ret) {
879
225
        assert(option);
880
225
        assert(option->type == SD_NDISC_OPTION_FLAGS_EXTENSION);
881
225
        assert(ret);
882
883
225
        _cleanup_free_ uint8_t *buf = new(uint8_t, 8);
884
225
        if (!buf)
885
0
                return 0;
886
887
225
        unaligned_write_be64(buf, (option->extended_flags & UINT64_C(0x00ffffffffffff00)) << 8);
888
225
        buf[0] = SD_NDISC_OPTION_FLAGS_EXTENSION;
889
225
        buf[1] = 1;
890
891
225
        *ret = TAKE_PTR(buf);
892
225
        return 0;
893
225
}
894
895
int ndisc_option_add_dnssl_internal(
896
                Set **options,
897
                size_t offset, char *
898
                const *domains,
899
                usec_t lifetime,
900
4.78k
                usec_t valid_until) {
901
902
4.78k
        int r;
903
904
4.78k
        assert(options);
905
906
4.78k
        if (strv_isempty(domains))
907
210
                return -EINVAL;
908
909
12.2k
        STRV_FOREACH(s, domains) {
910
12.2k
                r = dns_name_is_valid(*s);
911
12.2k
                if (r < 0)
912
0
                        return r;
913
914
12.2k
                if (is_localhost(*s) || dns_name_is_root(*s))
915
0
                        return -EINVAL;
916
12.2k
        }
917
918
4.57k
        _cleanup_strv_free_ char **copy = strv_copy(domains);
919
4.57k
        if (!copy)
920
0
                return -ENOMEM;
921
922
4.57k
        sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_DNSSL, offset);
923
4.57k
        if (!p)
924
0
                return -ENOMEM;
925
926
4.57k
        p->dnssl = (sd_ndisc_dnssl) {
927
4.57k
                .domains = TAKE_PTR(copy),
928
4.57k
                .lifetime = lifetime,
929
4.57k
                .valid_until = valid_until,
930
4.57k
        };
931
932
4.57k
        return ndisc_option_consume(options, p);
933
4.57k
}
934
935
5.56k
static int ndisc_option_parse_dnssl(Set **options, size_t offset, size_t len, const uint8_t *opt) {
936
5.56k
        int r;
937
938
5.56k
        assert(options);
939
5.56k
        assert(opt);
940
941
5.56k
        if (len < 2*8)
942
78
                return -EBADMSG;
943
944
5.48k
        if (opt[0] != SD_NDISC_OPTION_DNSSL)
945
0
                return -EBADMSG;
946
947
5.48k
        usec_t lifetime = unaligned_be32_sec_to_usec(opt + 4, /* max_as_infinity= */ true);
948
949
5.48k
        _cleanup_strv_free_ char **l = NULL;
950
5.48k
        _cleanup_free_ char *e = NULL;
951
5.48k
        size_t n = 0;
952
113k
        for (size_t c, pos = 8; pos < len; pos += c) {
953
954
108k
                c = opt[pos];
955
108k
                pos++;
956
957
108k
                if (c == 0) {
958
                        /* Found NUL termination */
959
960
87.7k
                        if (n > 0) {
961
13.2k
                                _cleanup_free_ char *normalized = NULL;
962
963
13.2k
                                e[n] = 0;
964
13.2k
                                r = dns_name_normalize(e, 0, &normalized);
965
13.2k
                                if (r < 0)
966
46
                                        return r;
967
968
                                /* Ignore the root domain name or "localhost" and friends */
969
13.2k
                                if (!is_localhost(normalized) && !dns_name_is_root(normalized)) {
970
12.7k
                                        r = strv_consume(&l, TAKE_PTR(normalized));
971
12.7k
                                        if (r < 0)
972
0
                                                return r;
973
12.7k
                                }
974
13.2k
                        }
975
976
87.6k
                        n = 0;
977
87.6k
                        continue;
978
87.7k
                }
979
980
                /* Check for compression (which is not allowed) */
981
21.1k
                if (c > 63)
982
281
                        return -EBADMSG;
983
984
20.8k
                if (pos + c >= len)
985
376
                        return -EBADMSG;
986
987
20.4k
                if (!GREEDY_REALLOC(e, n + (n != 0) + DNS_LABEL_ESCAPED_MAX + 1U))
988
0
                        return -ENOMEM;
989
990
20.4k
                if (n != 0)
991
6.98k
                        e[n++] = '.';
992
993
20.4k
                r = dns_label_escape((const char*) (opt + pos), c, e + n, DNS_LABEL_ESCAPED_MAX);
994
20.4k
                if (r < 0)
995
0
                        return r;
996
997
20.4k
                n += r;
998
20.4k
        }
999
1000
4.78k
        if (n > 0) /* Not properly NUL terminated */
1001
0
                return -EBADMSG;
1002
1003
4.78k
        return ndisc_option_add_dnssl(options, offset, l, lifetime);
1004
4.78k
}
1005
1006
1.48k
 static int ndisc_option_build_dnssl(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
1007
1.48k
        int r;
1008
1009
1.48k
        assert(option);
1010
1.48k
        assert(option->type == SD_NDISC_OPTION_DNSSL);
1011
1.48k
        assert(ret);
1012
1013
1.48k
        size_t len = 8;
1014
1.48k
        STRV_FOREACH(s, option->dnssl.domains)
1015
4.14k
                len += strlen(*s) + 2;
1016
1.48k
        len = DIV_ROUND_UP(len, 8);
1017
1018
1.48k
        be32_t lifetime = usec_to_be32_sec(MIN(option->dnssl.lifetime,
1019
1.48k
                                               usec_sub_unsigned(option->dnssl.valid_until, timestamp)));
1020
1021
1.48k
        _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
1022
1.48k
        if (!buf)
1023
0
                return -ENOMEM;
1024
1025
1.48k
        buf[0] = SD_NDISC_OPTION_DNSSL;
1026
1.48k
        buf[1] = len;
1027
1.48k
        buf[2] = 0;
1028
1.48k
        buf[3] = 0;
1029
1.48k
        memcpy(buf + 4, &lifetime, sizeof(be32_t));
1030
1031
1.48k
        size_t remaining = len * 8 - 8;
1032
1.48k
        uint8_t *p = buf + 8;
1033
1034
4.14k
        STRV_FOREACH(s, option->dnssl.domains) {
1035
4.14k
                r = dns_name_to_wire_format(*s, p, remaining, /* canonical= */ false);
1036
4.14k
                if (r < 0)
1037
0
                        return r;
1038
1039
4.14k
                assert(remaining >= (size_t) r);
1040
4.14k
                p += r;
1041
4.14k
                remaining -= r;
1042
4.14k
        }
1043
1044
1.48k
        memzero(p, remaining);
1045
1046
1.48k
        *ret = TAKE_PTR(buf);
1047
1.48k
        return 0;
1048
1.48k
}
1049
1050
1.60k
int ndisc_option_add_captive_portal(Set **options, size_t offset, const char *portal) {
1051
1.60k
        assert(options);
1052
1053
1.60k
        if (isempty(portal))
1054
0
                return -EINVAL;
1055
1056
1.60k
        if (!in_charset(portal, URI_VALID))
1057
420
                return -EINVAL;
1058
1059
1.18k
        sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_CAPTIVE_PORTAL);
1060
1.18k
        if (p) {
1061
398
                if (offset != 0)
1062
398
                        return -EEXIST;
1063
1064
0
                return free_and_strdup(&p->captive_portal, portal);
1065
398
        }
1066
1067
782
        _cleanup_free_ char *copy = strdup(portal);
1068
782
        if (!copy)
1069
0
                return -ENOMEM;
1070
1071
782
        p = ndisc_option_new(SD_NDISC_OPTION_CAPTIVE_PORTAL, offset);
1072
782
        if (!p)
1073
0
                return -ENOMEM;
1074
1075
782
        p->captive_portal = TAKE_PTR(copy);
1076
1077
782
        return ndisc_option_consume(options, p);
1078
782
}
1079
1080
1.87k
static int ndisc_option_parse_captive_portal(Set **options, size_t offset, size_t len, const uint8_t *opt) {
1081
1.87k
        assert(options);
1082
1.87k
        assert(opt);
1083
1084
1.87k
        if (len < 8)
1085
0
                return -EBADMSG;
1086
1087
1.87k
        if (opt[0] != SD_NDISC_OPTION_CAPTIVE_PORTAL)
1088
0
                return -EBADMSG;
1089
1090
1.87k
        _cleanup_free_ char *portal = memdup_suffix0(opt + 2, len - 2);
1091
1.87k
        if (!portal)
1092
0
                return -ENOMEM;
1093
1094
1.87k
        size_t size = strlen(portal);
1095
1.87k
        if (size == 0)
1096
186
                return -EBADMSG;
1097
1098
        /* Check that the message is not truncated by an embedded NUL.
1099
         * NUL padding to a multiple of 8 is expected. */
1100
1.69k
        if (DIV_ROUND_UP(size + 2, 8) * 8 != len && DIV_ROUND_UP(size + 3, 8) * 8 != len)
1101
92
                return -EBADMSG;
1102
1103
1.60k
        return ndisc_option_add_captive_portal(options, offset, portal);
1104
1.69k
}
1105
1106
193
static int ndisc_option_build_captive_portal(const sd_ndisc_option *option, uint8_t **ret) {
1107
193
        assert(option);
1108
193
        assert(option->type == SD_NDISC_OPTION_CAPTIVE_PORTAL);
1109
193
        assert(ret);
1110
1111
193
        size_t len_portal = strlen(option->captive_portal);
1112
193
        size_t len = DIV_ROUND_UP(len_portal + 1 + 2, 8);
1113
1114
193
        _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
1115
193
        if (!buf)
1116
0
                return -ENOMEM;
1117
1118
193
        buf[0] = SD_NDISC_OPTION_CAPTIVE_PORTAL;
1119
193
        buf[1] = len;
1120
1121
193
        uint8_t *p = mempcpy(buf + 2, option->captive_portal, len_portal);
1122
193
        size_t remaining = len * 8 - 2 - len_portal;
1123
1124
193
        memzero(p, remaining);
1125
1126
193
        *ret = TAKE_PTR(buf);
1127
193
        return 0;
1128
193
}
1129
1130
static const uint8_t prefix_length_code_to_prefix_length[_PREFIX_LENGTH_CODE_MAX] = {
1131
        [PREFIX_LENGTH_CODE_96] = 96,
1132
        [PREFIX_LENGTH_CODE_64] = 64,
1133
        [PREFIX_LENGTH_CODE_56] = 56,
1134
        [PREFIX_LENGTH_CODE_48] = 48,
1135
        [PREFIX_LENGTH_CODE_40] = 40,
1136
        [PREFIX_LENGTH_CODE_32] = 32,
1137
};
1138
1139
3.48k
int pref64_prefix_length_to_plc(uint8_t prefixlen, uint8_t *ret) {
1140
13.4k
        for (size_t i = 0; i < ELEMENTSOF(prefix_length_code_to_prefix_length); i++)
1141
13.4k
                if (prefix_length_code_to_prefix_length[i] == prefixlen) {
1142
3.48k
                        if (ret)
1143
777
                                *ret = i;
1144
3.48k
                        return 0;
1145
3.48k
                }
1146
1147
0
        return -EINVAL;
1148
3.48k
}
1149
1150
2.90k
static int pref64_lifetime_and_plc_parse(uint16_t lifetime_and_plc, uint8_t *ret_prefixlen, usec_t *ret_lifetime) {
1151
2.90k
        uint16_t plc = lifetime_and_plc & PREF64_PLC_MASK;
1152
2.90k
        if (plc >= _PREFIX_LENGTH_CODE_MAX)
1153
196
                return -EINVAL;
1154
1155
2.70k
        if (ret_prefixlen)
1156
2.70k
                *ret_prefixlen = prefix_length_code_to_prefix_length[plc];
1157
2.70k
        if (ret_lifetime)
1158
2.70k
                *ret_lifetime = (lifetime_and_plc & PREF64_SCALED_LIFETIME_MASK) * USEC_PER_SEC;
1159
2.70k
        return 0;
1160
2.90k
}
1161
1162
int ndisc_option_add_prefix64_internal(
1163
                Set **options,
1164
                size_t offset,
1165
                uint8_t prefixlen,
1166
                const struct in6_addr *prefix,
1167
                usec_t lifetime,
1168
2.70k
                usec_t valid_until) {
1169
1170
2.70k
        int r;
1171
1172
2.70k
        assert(options);
1173
2.70k
        assert(prefix);
1174
1175
2.70k
        r = pref64_prefix_length_to_plc(prefixlen, NULL);
1176
2.70k
        if (r < 0)
1177
0
                return r;
1178
1179
2.70k
        if (lifetime > PREF64_MAX_LIFETIME_USEC)
1180
0
                return -EINVAL;
1181
1182
2.70k
        struct in6_addr addr = *prefix;
1183
2.70k
        in6_addr_mask(&addr, prefixlen);
1184
1185
2.70k
        sd_ndisc_option *p = ndisc_option_get(
1186
2.70k
                                *options,
1187
2.70k
                                &(const sd_ndisc_option) {
1188
2.70k
                                        .type = SD_NDISC_OPTION_PREF64,
1189
2.70k
                                        .prefix64.prefixlen = prefixlen,
1190
2.70k
                                        .prefix64.prefix = addr,
1191
2.70k
                                });
1192
2.70k
        if (p) {
1193
276
                if (offset != 0)
1194
276
                        return -EEXIST;
1195
1196
0
                p->prefix64.lifetime = lifetime;
1197
0
                p->prefix64.valid_until = valid_until;
1198
0
                return 0;
1199
276
        }
1200
1201
2.43k
        p = ndisc_option_new(SD_NDISC_OPTION_PREF64, offset);
1202
2.43k
        if (!p)
1203
0
                return -ENOMEM;
1204
1205
2.43k
        p->prefix64 = (sd_ndisc_prefix64) {
1206
2.43k
                .prefixlen = prefixlen,
1207
2.43k
                .prefix = addr,
1208
2.43k
                .lifetime = lifetime,
1209
2.43k
                .valid_until = valid_until,
1210
2.43k
        };
1211
1212
2.43k
        return ndisc_option_consume(options, p);
1213
2.43k
}
1214
1215
3.32k
static int ndisc_option_parse_prefix64(Set **options, size_t offset, size_t len, const uint8_t *opt) {
1216
3.32k
        int r;
1217
1218
3.32k
        assert(options);
1219
3.32k
        assert(opt);
1220
1221
3.32k
        if (len != 2*8)
1222
417
                return -EBADMSG;
1223
1224
2.90k
        if (opt[0] != SD_NDISC_OPTION_PREF64)
1225
0
                return -EBADMSG;
1226
1227
2.90k
        uint8_t prefixlen;
1228
2.90k
        usec_t lifetime;
1229
2.90k
        r = pref64_lifetime_and_plc_parse(unaligned_read_be16(opt + 2), &prefixlen, &lifetime);
1230
2.90k
        if (r < 0)
1231
196
                return r;
1232
1233
2.70k
        struct in6_addr prefix;
1234
2.70k
        memcpy(&prefix, opt + 4, len - 4);
1235
2.70k
        in6_addr_mask(&prefix, prefixlen);
1236
1237
2.70k
        return ndisc_option_add_prefix64(options, offset, prefixlen, &prefix, lifetime);
1238
2.90k
}
1239
1240
777
static int ndisc_option_build_prefix64(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
1241
777
        int r;
1242
1243
777
        assert(option);
1244
777
        assert(option->type == SD_NDISC_OPTION_PREF64);
1245
777
        assert(ret);
1246
1247
777
        uint8_t code;
1248
777
        r = pref64_prefix_length_to_plc(option->prefix64.prefixlen, &code);
1249
777
        if (r < 0)
1250
0
                return r;
1251
1252
777
        uint16_t lifetime = (uint16_t) DIV_ROUND_UP(MIN(option->prefix64.lifetime,
1253
777
                                                        usec_sub_unsigned(option->prefix64.valid_until, timestamp)),
1254
777
                                                    USEC_PER_SEC) & PREF64_SCALED_LIFETIME_MASK;
1255
1256
777
        _cleanup_free_ uint8_t *buf = new(uint8_t, 2 * 8);
1257
777
        if (!buf)
1258
0
                return -ENOMEM;
1259
1260
777
        buf[0] = SD_NDISC_OPTION_PREF64;
1261
777
        buf[1] = 2;
1262
777
        unaligned_write_be16(buf + 2, lifetime | code);
1263
777
        memcpy(buf + 4, &option->prefix64.prefix, 12);
1264
1265
777
        *ret = TAKE_PTR(buf);
1266
777
        return 0;
1267
777
}
1268
1269
int ndisc_option_add_encrypted_dns_internal(
1270
                Set **options,
1271
                size_t offset,
1272
                sd_dns_resolver *res,
1273
                usec_t lifetime,
1274
1.23k
                usec_t valid_until) {
1275
1.23k
        assert(options);
1276
1277
1.23k
        sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_ENCRYPTED_DNS, offset);
1278
1.23k
        if (!p)
1279
0
                return -ENOMEM;
1280
1281
1.23k
        p->encrypted_dns = (sd_ndisc_dnr) {
1282
1.23k
                .resolver = res,
1283
1.23k
                .lifetime = lifetime,
1284
1.23k
                .valid_until = valid_until,
1285
1.23k
        };
1286
1287
1.23k
        return ndisc_option_consume(options, p);
1288
1.23k
}
1289
1290
7.30k
static int ndisc_get_dns_name(const uint8_t *optval, size_t optlen, char **ret) {
1291
7.30k
        _cleanup_free_ char *name = NULL;
1292
7.30k
        int r;
1293
1294
7.30k
        assert(optval || optlen == 0);
1295
7.30k
        assert(ret);
1296
1297
7.30k
        r = dns_name_from_wire_format(&optval, &optlen, &name);
1298
7.30k
        if (r < 0)
1299
461
                return r;
1300
6.84k
        if (r == 0 || optlen != 0)
1301
418
                return -EBADMSG;
1302
1303
6.42k
        *ret = TAKE_PTR(name);
1304
6.42k
        return r;
1305
6.84k
}
1306
1307
7.96k
static int ndisc_option_parse_encrypted_dns(Set **options, size_t offset, size_t len, const uint8_t *opt) {
1308
7.96k
        int r;
1309
1310
7.96k
        assert(options);
1311
7.96k
        assert(opt);
1312
7.96k
        _cleanup_(sd_dns_resolver_done) sd_dns_resolver res = {};
1313
7.96k
        usec_t lifetime;
1314
7.96k
        size_t ilen;
1315
1316
        /* Every field up to and including adn must be present */
1317
7.96k
        if (len < 2*8)
1318
213
                return -EBADMSG;
1319
1320
7.75k
        if (opt[0] != SD_NDISC_OPTION_ENCRYPTED_DNS)
1321
0
                return -EBADMSG;
1322
1323
7.75k
        size_t off = 2;
1324
1325
        /* Priority */
1326
7.75k
        res.priority = unaligned_read_be16(opt + off);
1327
        /* Alias mode is not allowed */
1328
7.75k
        if (res.priority == 0)
1329
194
                return -EBADMSG;
1330
7.55k
        off += sizeof(uint16_t);
1331
1332
        /* Lifetime */
1333
7.55k
        lifetime = unaligned_be32_sec_to_usec(opt + off, /* max_as_infinity= */ true);
1334
7.55k
        off += sizeof(uint32_t);
1335
1336
        /* adn field (length + dns-name) */
1337
7.55k
        ilen = unaligned_read_be16(opt + off);
1338
7.55k
        off += sizeof(uint16_t);
1339
7.55k
        if (off + ilen > len)
1340
255
                return -EBADMSG;
1341
1342
7.30k
        r = ndisc_get_dns_name(opt + off, ilen, &res.auth_name);
1343
7.30k
        if (r < 0)
1344
879
                return r;
1345
6.42k
        r = dns_name_is_valid_ldh(res.auth_name);
1346
6.42k
        if (r < 0)
1347
0
                return r;
1348
6.42k
        if (!r)
1349
915
                return -EBADMSG;
1350
5.50k
        if (dns_name_is_root(res.auth_name))
1351
0
                return -EBADMSG;
1352
5.50k
        off += ilen;
1353
1354
        /* This is the last field in adn-only mode, sans padding */
1355
5.50k
        if (8 * DIV_ROUND_UP(off, 8) == len && memeqzero(opt + off, len - off))
1356
469
                return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Received ADN-only encrypted DNS option, ignoring.");
1357
1358
        /* Fields following the variable (octets) length adn field are no longer certain to be aligned. */
1359
1360
        /* addrs (length + packed struct in6_addr) */
1361
5.03k
        if (off + sizeof(uint16_t) > len)
1362
354
                return -EBADMSG;
1363
4.68k
        ilen = unaligned_read_be16(opt + off);
1364
4.68k
        off += sizeof(uint16_t);
1365
4.68k
        if (off + ilen > len || ilen % (sizeof(struct in6_addr)) != 0)
1366
431
                return -EBADMSG;
1367
1368
4.25k
        size_t n_addrs = ilen / (sizeof(struct in6_addr));
1369
4.25k
        if (n_addrs == 0)
1370
198
                return -EBADMSG;
1371
4.05k
        res.addrs = new(union in_addr_union, n_addrs);
1372
4.05k
        if (!res.addrs)
1373
0
                return -ENOMEM;
1374
1375
9.53k
        for (size_t i = 0; i < n_addrs; i++) {
1376
5.58k
                union in_addr_union addr;
1377
5.58k
                memcpy(&addr.in6, opt + off, sizeof(struct in6_addr));
1378
5.58k
                if (in_addr_is_multicast(AF_INET6, &addr) ||
1379
5.50k
                    in_addr_is_localhost(AF_INET6, &addr))
1380
105
                        return -EBADMSG;
1381
5.47k
                res.addrs[i] = addr;
1382
5.47k
                off += sizeof(struct in6_addr);
1383
5.47k
        }
1384
3.95k
        res.n_addrs = n_addrs;
1385
3.95k
        res.family = AF_INET6;
1386
1387
        /* SvcParam field. (length + SvcParams) */
1388
3.95k
        if (off + sizeof(uint16_t) > len)
1389
86
                return -EBADMSG;
1390
3.86k
        ilen = unaligned_read_be16(opt + off);
1391
3.86k
        off += sizeof(uint16_t);
1392
3.86k
        if (off + ilen > len)
1393
221
                return -EBADMSG;
1394
1395
3.64k
        r = dnr_parse_svc_params(opt + off, ilen, &res);
1396
3.64k
        if (r < 0)
1397
2.04k
                return r;
1398
1.59k
        if (r == 0) /* This indicates a valid message we don't support */
1399
217
                return -EOPNOTSUPP;
1400
1.38k
        off += ilen;
1401
1402
        /* the remaining padding bytes must be zeroed */
1403
1.38k
        if (len - off >= 8 || !memeqzero(opt + off, len - off))
1404
146
                return -EBADMSG;
1405
1406
1.23k
        sd_dns_resolver *new_res = new(sd_dns_resolver, 1);
1407
1.23k
        if (!new_res)
1408
0
                return -ENOMEM;
1409
1410
1.23k
        *new_res = TAKE_STRUCT(res);
1411
1412
1.23k
        return ndisc_option_add_encrypted_dns(options, offset, new_res, lifetime);
1413
1.23k
}
1414
1415
566
static int ndisc_option_build_encrypted_dns(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
1416
566
        int r;
1417
1418
566
        assert(option);
1419
566
        assert(option->type == SD_NDISC_OPTION_ENCRYPTED_DNS);
1420
566
        assert(ret);
1421
1422
566
        size_t off, len, ilen, plen, poff;
1423
1424
        /* Everything up to adn field is required, so we need at least 2*8 bytes */
1425
566
        _cleanup_free_ uint8_t *buf = new(uint8_t, 2 * 8);
1426
566
        if (!buf)
1427
0
                return -ENOMEM;
1428
1429
566
        _cleanup_strv_free_ char **alpns = NULL;
1430
566
        const sd_dns_resolver *res = option->encrypted_dns.resolver;
1431
566
        be32_t lifetime = usec_to_be32_sec(MIN(option->encrypted_dns.lifetime,
1432
566
                                               usec_sub_unsigned(option->encrypted_dns.valid_until, timestamp)));
1433
1434
        /* Type (Length field filled in last) */
1435
566
        buf[0] = option->type;
1436
1437
        /* Priority */
1438
566
        off = 2;
1439
566
        unaligned_write_be16(buf + off, res->priority);
1440
566
        off += sizeof(be16_t);
1441
1442
        /* Lifetime */
1443
566
        memcpy(buf + off, &lifetime, sizeof(be32_t));
1444
566
        off += sizeof(be32_t);
1445
1446
        /* ADN */
1447
        //FIXME can the wire format be longer than this?
1448
566
        ilen = strlen(res->auth_name) + 2;
1449
1450
        /* From now on, there isn't guaranteed to be enough space to put each field */
1451
566
        if (!GREEDY_REALLOC(buf, off + sizeof(uint16_t) + ilen))
1452
0
                return -ENOMEM;
1453
1454
566
        r = dns_name_to_wire_format(res->auth_name, buf + off + sizeof(uint16_t), ilen, /* canonical= */ false);
1455
566
        if (r < 0)
1456
0
                return r;
1457
566
        unaligned_write_be16(buf + off, (uint16_t) r);
1458
566
        off += sizeof(uint16_t) + r;
1459
1460
        /* ADN-only mode */
1461
566
        if (res->n_addrs == 0)
1462
0
                goto padding;
1463
1464
        /* addrs */
1465
566
        if (size_multiply_overflow(sizeof(struct in6_addr), res->n_addrs))
1466
0
                return -ENOMEM;
1467
1468
566
        ilen = res->n_addrs * sizeof(struct in6_addr);
1469
566
        if (!GREEDY_REALLOC(buf, off + sizeof(uint16_t) + ilen))
1470
0
                return -ENOMEM;
1471
1472
566
        unaligned_write_be16(buf + off, ilen);
1473
566
        off += sizeof(uint16_t);
1474
1475
771
        FOREACH_ARRAY(addr, res->addrs, res->n_addrs) {
1476
771
                memcpy(buf + off, &addr->in6, sizeof(struct in6_addr));
1477
771
                off += sizeof(struct in6_addr);
1478
771
        }
1479
1480
        /* SvcParam, MUST appear in order */
1481
566
        poff = off + sizeof(uint16_t);
1482
1483
        /* ALPN */
1484
566
        dns_resolver_transports_to_strv(res->transports, &alpns);
1485
1486
        /* res needs to have at least one valid transport */
1487
566
        if (strv_isempty(alpns))
1488
0
                return -EINVAL;
1489
1490
566
        plen = 0;
1491
566
        STRV_FOREACH(alpn, alpns)
1492
782
                plen += sizeof(uint8_t) + strlen(*alpn);
1493
1494
566
        if (!GREEDY_REALLOC(buf, poff + 2 * sizeof(uint16_t) + plen))
1495
0
                return -ENOMEM;
1496
1497
566
        unaligned_write_be16(buf + poff, (uint16_t) DNS_SVC_PARAM_KEY_ALPN);
1498
566
        poff += sizeof(uint16_t);
1499
566
        unaligned_write_be16(buf + poff, plen);
1500
566
        poff += sizeof(uint16_t);
1501
1502
782
        STRV_FOREACH(alpn, alpns) {
1503
782
                size_t alen = strlen(*alpn);
1504
782
                buf[poff++] = alen;
1505
782
                memcpy(buf + poff, *alpn, alen);
1506
782
                poff += alen;
1507
782
        }
1508
1509
        /* port */
1510
566
        if (res->port > 0) {
1511
35
                plen = 2;
1512
35
                if (!GREEDY_REALLOC(buf, poff + 2 * sizeof(uint16_t) + plen))
1513
0
                        return -ENOMEM;
1514
1515
35
                unaligned_write_be16(buf + poff, (uint16_t) DNS_SVC_PARAM_KEY_PORT);
1516
35
                poff += sizeof(uint16_t);
1517
35
                unaligned_write_be16(buf + poff, plen);
1518
35
                poff += sizeof(uint16_t);
1519
35
                unaligned_write_be16(buf + poff, res->port);
1520
35
                poff += sizeof(uint16_t);
1521
35
        }
1522
1523
        /* dohpath */
1524
566
        if (res->dohpath) {
1525
94
                plen = strlen(res->dohpath);
1526
94
                if (!GREEDY_REALLOC(buf, poff + 2 * sizeof(uint16_t) + plen))
1527
0
                        return -ENOMEM;
1528
1529
94
                unaligned_write_be16(buf + poff, (uint16_t) DNS_SVC_PARAM_KEY_DOHPATH);
1530
94
                poff += sizeof(uint16_t);
1531
94
                unaligned_write_be16(buf + poff, plen);
1532
94
                poff += sizeof(uint16_t);
1533
94
                memcpy(buf + poff, res->dohpath, plen);
1534
94
                poff += plen;
1535
94
        }
1536
1537
566
        unaligned_write_be16(buf + off, LESS_BY(poff, off));
1538
566
        off = poff;
1539
1540
566
padding:
1541
566
        len = DIV_ROUND_UP(off, 8);
1542
566
        if (!GREEDY_REALLOC(buf, 8*len))
1543
0
                return -ENOMEM;
1544
566
        memzero(buf + off, 8*len - off);
1545
1546
566
        buf[1] = len;
1547
566
        *ret = TAKE_PTR(buf);
1548
566
        return 0;
1549
566
}
1550
1551
47.2k
static int ndisc_option_parse_default(Set **options, size_t offset, size_t len, const uint8_t *opt) {
1552
47.2k
        assert(options);
1553
47.2k
        assert(opt);
1554
47.2k
        assert(len > 0);
1555
1556
47.2k
        sd_ndisc_option *p = ndisc_option_new(opt[0], offset);
1557
47.2k
        if (!p)
1558
0
                return -ENOMEM;
1559
1560
47.2k
        return ndisc_option_consume(options, p);
1561
47.2k
}
1562
1563
20.1k
static int ndisc_header_size(uint8_t icmp6_type) {
1564
20.1k
        switch (icmp6_type) {
1565
11.8k
        case ND_ROUTER_SOLICIT:
1566
11.8k
                return sizeof(struct nd_router_solicit);
1567
7.11k
        case ND_ROUTER_ADVERT:
1568
7.11k
                return sizeof(struct nd_router_advert);
1569
508
        case ND_NEIGHBOR_SOLICIT:
1570
508
                return sizeof(struct nd_neighbor_solicit);
1571
386
        case ND_NEIGHBOR_ADVERT:
1572
386
                return sizeof(struct nd_neighbor_advert);
1573
272
        case ND_REDIRECT:
1574
272
                return sizeof(struct nd_redirect);
1575
18
        default:
1576
18
                return -EINVAL;
1577
20.1k
        }
1578
20.1k
}
1579
1580
10.1k
int ndisc_parse_options(ICMP6Packet *packet, Set **ret_options) {
1581
10.1k
        _cleanup_set_free_ Set *options = NULL;
1582
10.1k
        int r;
1583
1584
10.1k
        assert(packet);
1585
10.1k
        assert(ret_options);
1586
1587
10.1k
        r = icmp6_packet_get_type(packet);
1588
10.1k
        if (r < 0)
1589
0
                return r;
1590
1591
10.1k
        r = ndisc_header_size(r);
1592
10.1k
        if (r < 0)
1593
18
                return -EBADMSG;
1594
10.1k
        size_t header_size = r;
1595
1596
10.1k
        if (packet->raw_size < header_size)
1597
32
                return -EBADMSG;
1598
1599
98.5k
        for (size_t length, offset = header_size; offset < packet->raw_size; offset += length) {
1600
88.8k
                uint8_t type;
1601
88.8k
                const uint8_t *opt;
1602
1603
88.8k
                r = ndisc_option_parse(packet, offset, &type, &length, &opt);
1604
88.8k
                if (r < 0)
1605
373
                        return log_debug_errno(r, "Failed to parse NDisc option header: %m");
1606
1607
88.5k
                switch (type) {
1608
542
                case 0:
1609
542
                        r = -EBADMSG;
1610
542
                        break;
1611
1612
4.45k
                case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
1613
5.94k
                case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
1614
5.94k
                        r = ndisc_option_parse_link_layer_address(&options, offset, length, opt);
1615
5.94k
                        break;
1616
1617
3.47k
                case SD_NDISC_OPTION_PREFIX_INFORMATION:
1618
3.47k
                        r = ndisc_option_parse_prefix(&options, offset, length, opt);
1619
3.47k
                        break;
1620
1621
501
                case SD_NDISC_OPTION_REDIRECTED_HEADER:
1622
501
                        r = ndisc_option_parse_redirected_header(&options, offset, length, opt);
1623
501
                        break;
1624
1625
1.94k
                case SD_NDISC_OPTION_MTU:
1626
1.94k
                        r = ndisc_option_parse_mtu(&options, offset, length, opt);
1627
1.94k
                        break;
1628
1629
2.37k
                case SD_NDISC_OPTION_HOME_AGENT:
1630
2.37k
                        r = ndisc_option_parse_home_agent(&options, offset, length, opt);
1631
2.37k
                        break;
1632
1633
4.61k
                case SD_NDISC_OPTION_ROUTE_INFORMATION:
1634
4.61k
                        r = ndisc_option_parse_route(&options, offset, length, opt);
1635
4.61k
                        break;
1636
1637
1.33k
                case SD_NDISC_OPTION_RDNSS:
1638
1.33k
                        r = ndisc_option_parse_rdnss(&options, offset, length, opt);
1639
1.33k
                        break;
1640
1641
1.77k
                case SD_NDISC_OPTION_FLAGS_EXTENSION:
1642
1.77k
                        r = ndisc_option_parse_flags_extension(&options, offset, length, opt);
1643
1.77k
                        break;
1644
1645
5.56k
                case SD_NDISC_OPTION_DNSSL:
1646
5.56k
                        r = ndisc_option_parse_dnssl(&options, offset, length, opt);
1647
5.56k
                        break;
1648
1649
1.87k
                case SD_NDISC_OPTION_CAPTIVE_PORTAL:
1650
1.87k
                        r = ndisc_option_parse_captive_portal(&options, offset, length, opt);
1651
1.87k
                        break;
1652
1653
3.32k
                case SD_NDISC_OPTION_PREF64:
1654
3.32k
                        r = ndisc_option_parse_prefix64(&options, offset, length, opt);
1655
3.32k
                        break;
1656
1657
7.96k
                case SD_NDISC_OPTION_ENCRYPTED_DNS:
1658
7.96k
                        r = ndisc_option_parse_encrypted_dns(&options, offset, length, opt);
1659
7.96k
                        break;
1660
1661
47.2k
                default:
1662
47.2k
                        r = ndisc_option_parse_default(&options, offset, length, opt);
1663
88.5k
                }
1664
88.5k
                if (r == -ENOMEM)
1665
0
                        return log_oom_debug();
1666
88.5k
                if (r < 0)
1667
88.5k
                        log_debug_errno(r, "Failed to parse NDisc option %u, ignoring: %m", type);
1668
88.5k
        }
1669
1670
9.71k
        *ret_options = TAKE_PTR(options);
1671
9.71k
        return 0;
1672
10.0k
}
1673
1674
35.4k
sd_ndisc_option* ndisc_option_get(Set *options, const sd_ndisc_option *p) {
1675
35.4k
        return set_get(options, ASSERT_PTR(p));
1676
35.4k
}
1677
1678
26.1k
sd_ndisc_option* ndisc_option_get_by_type(Set *options, uint8_t type) {
1679
26.1k
        return ndisc_option_get(options, &(const sd_ndisc_option) { .type = type });
1680
26.1k
}
1681
1682
6.61k
int ndisc_option_get_mac(Set *options, uint8_t type, struct ether_addr *ret) {
1683
6.61k
        assert(IN_SET(type, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS));
1684
1685
6.61k
        sd_ndisc_option *p = ndisc_option_get_by_type(options, type);
1686
6.61k
        if (!p)
1687
2.57k
                return -ENODATA;
1688
1689
4.03k
        if (ret)
1690
3.73k
                *ret = p->mac;
1691
4.03k
        return 0;
1692
6.61k
}
1693
1694
0
void ndisc_option_remove(Set *options, const sd_ndisc_option *p) {
1695
0
        ndisc_option_free(set_remove(options, ASSERT_PTR(p)));
1696
0
}
1697
1698
9.96k
int ndisc_send(int fd, const struct in6_addr *dst, const struct icmp6_hdr *hdr, Set *options, usec_t timestamp) {
1699
9.96k
        int r;
1700
1701
9.96k
        assert(fd >= 0);
1702
9.96k
        assert(dst);
1703
9.96k
        assert(hdr);
1704
1705
9.96k
        size_t n;
1706
9.96k
        _cleanup_free_ sd_ndisc_option **list = NULL;
1707
9.96k
        r = set_dump_sorted(options, (void***) &list, &n);
1708
9.96k
        if (r < 0)
1709
0
                return r;
1710
1711
9.96k
        struct iovec *iov = NULL;
1712
9.96k
        size_t n_iov = 0;
1713
9.96k
        CLEANUP_ARRAY(iov, n_iov, iovec_array_free);
1714
1715
9.96k
        iov = new(struct iovec, 1 + n);
1716
9.96k
        if (!iov)
1717
0
                return -ENOMEM;
1718
1719
9.96k
        r = ndisc_header_size(hdr->icmp6_type);
1720
9.96k
        if (r < 0)
1721
0
                return r;
1722
9.96k
        size_t hdr_size = r;
1723
1724
9.96k
        _cleanup_free_ uint8_t *copy = newdup(uint8_t, hdr, hdr_size);
1725
9.96k
        if (!copy)
1726
0
                return -ENOMEM;
1727
1728
9.96k
        iov[n_iov++] = IOVEC_MAKE(TAKE_PTR(copy), hdr_size);
1729
1730
35.4k
        FOREACH_ARRAY(p, list, n) {
1731
35.4k
                _cleanup_free_ uint8_t *buf = NULL;
1732
35.4k
                sd_ndisc_option *option = *p;
1733
1734
35.4k
                switch (option->type) {
1735
0
                case 0:
1736
0
                        r = ndisc_option_build_raw(option, &buf);
1737
0
                        break;
1738
1739
6.94k
                case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
1740
7.16k
                case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
1741
7.16k
                        r = ndisc_option_build_link_layer_address(option, &buf);
1742
7.16k
                        break;
1743
1744
843
                case SD_NDISC_OPTION_PREFIX_INFORMATION:
1745
843
                        r = ndisc_option_build_prefix(option, timestamp, &buf);
1746
843
                        break;
1747
1748
26
                case SD_NDISC_OPTION_REDIRECTED_HEADER:
1749
26
                        r = ndisc_option_build_redirected_header(option, &buf);
1750
26
                        break;
1751
1752
273
                case SD_NDISC_OPTION_MTU:
1753
273
                        r = ndisc_option_build_mtu(option, &buf);
1754
273
                        break;
1755
1756
304
                case SD_NDISC_OPTION_HOME_AGENT:
1757
304
                        r = ndisc_option_build_home_agent(option, timestamp, &buf);
1758
304
                        break;
1759
1760
905
                case SD_NDISC_OPTION_ROUTE_INFORMATION:
1761
905
                        r = ndisc_option_build_route(option, timestamp, &buf);
1762
905
                        break;
1763
1764
331
                case SD_NDISC_OPTION_RDNSS:
1765
331
                        r = ndisc_option_build_rdnss(option, timestamp, &buf);
1766
331
                        break;
1767
1768
225
                case SD_NDISC_OPTION_FLAGS_EXTENSION:
1769
225
                        r = ndisc_option_build_flags_extension(option, &buf);
1770
225
                        break;
1771
1772
1.48k
                case SD_NDISC_OPTION_DNSSL:
1773
1.48k
                        r = ndisc_option_build_dnssl(option, timestamp, &buf);
1774
1.48k
                        break;
1775
1776
193
                case SD_NDISC_OPTION_CAPTIVE_PORTAL:
1777
193
                        r = ndisc_option_build_captive_portal(option, &buf);
1778
193
                        break;
1779
1780
777
                case SD_NDISC_OPTION_PREF64:
1781
777
                        r = ndisc_option_build_prefix64(option, timestamp, &buf);
1782
777
                        break;
1783
1784
566
                case SD_NDISC_OPTION_ENCRYPTED_DNS:
1785
566
                        r = ndisc_option_build_encrypted_dns(option, timestamp, &buf);
1786
566
                        break;
1787
1788
22.3k
                default:
1789
22.3k
                        continue;
1790
35.4k
                }
1791
13.0k
                if (r == -ENOMEM)
1792
0
                        return log_oom_debug();
1793
13.0k
                if (r < 0)
1794
13.0k
                        log_debug_errno(r, "Failed to build NDisc option %u, ignoring: %m", option->type);
1795
1796
13.0k
                iov[n_iov++] = IOVEC_MAKE(buf, buf[1] * 8);
1797
13.0k
                TAKE_PTR(buf);
1798
13.0k
        }
1799
1800
9.96k
        return icmp6_send(fd, dst, iov, n_iov);
1801
9.96k
}