Coverage Report

Created: 2026-09-14 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/systemd/src/network/networkd-dhcp-common.c
Line
Count
Source
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3
#include <linux/if_arp.h>
4
#include <linux/rtnetlink.h>
5
#include <netinet/in.h>
6
7
#include "sd-bus.h"
8
#include "sd-dhcp6-lease.h"
9
#include "sd-dhcp6-option.h"
10
11
#include "alloc-util.h"
12
#include "bus-error.h"
13
#include "bus-locator.h"
14
#include "dhcp6-option.h"
15
#include "escape.h"
16
#include "extract-word.h"
17
#include "hexdecoct.h"
18
#include "in-addr-prefix-util.h"
19
#include "iovec-util.h"
20
#include "networkd-dhcp-common.h"
21
#include "networkd-dhcp-prefix-delegation.h"
22
#include "networkd-ipv6ll.h"
23
#include "networkd-link.h"
24
#include "networkd-manager.h"
25
#include "networkd-network.h"
26
#include "networkd-route-util.h"
27
#include "networkd-util.h"
28
#include "parse-util.h"
29
#include "set.h"
30
#include "socket-util.h"
31
#include "string-table.h"
32
#include "string-util.h"
33
#include "strv.h"
34
#include "vrf.h"
35
36
0
static uint32_t link_get_vrf_table(Link *link) {
37
0
        assert(link);
38
0
        assert(link->network);
39
40
0
        return link->network->vrf ? VRF(link->network->vrf)->table : RT_TABLE_MAIN;
41
0
}
42
43
0
uint32_t link_get_dhcp4_route_table(Link *link) {
44
0
        assert(link);
45
0
        assert(link->network);
46
47
        /* When the interface is part of an VRF use the VRFs routing table, unless
48
         * another table is explicitly specified. */
49
50
0
        if (link->network->dhcp_route_table_set)
51
0
                return link->network->dhcp_route_table;
52
0
        return link_get_vrf_table(link);
53
0
}
54
55
0
uint32_t link_get_ndisc_route_table(Link *link) {
56
0
        assert(link);
57
0
        assert(link->network);
58
59
0
        if (link->network->ndisc_route_table_set)
60
0
                return link->network->ndisc_route_table;
61
0
        return link_get_vrf_table(link);
62
0
}
63
64
0
uint32_t link_get_dhcp6_route_table(Link *link) {
65
0
        assert(link);
66
0
        assert(link->network);
67
68
0
        if (link->network->dhcp6_route_table_set)
69
0
                return link->network->dhcp6_route_table;
70
0
        return link_get_vrf_table(link);
71
0
}
72
73
0
bool link_dhcp_enabled(Link *link, int family) {
74
0
        assert(link);
75
0
        assert(IN_SET(family, AF_INET, AF_INET6));
76
77
        /* Currently, sd-dhcp-client supports only ethernet and infiniband.
78
         * (ARMHRD_RAWIP and ARMHRD_NONE are typically wwan modems and will be
79
         * treated as ethernet devices.) */
80
0
        if (family == AF_INET && !IN_SET(link->iftype, ARPHRD_ETHER, ARPHRD_INFINIBAND, ARPHRD_RAWIP, ARPHRD_NONE))
81
0
                return false;
82
83
0
        if (family == AF_INET6 && !socket_ipv6_is_supported())
84
0
                return false;
85
86
0
        if (link->flags & IFF_LOOPBACK)
87
0
                return false;
88
89
0
        if (link->iftype == ARPHRD_CAN)
90
0
                return false;
91
92
0
        if (!link->network)
93
0
                return false;
94
95
0
        return link->network->dhcp & AF_TO_ADDRESS_FAMILY(family);
96
0
}
97
98
3.53k
void network_adjust_dhcp(Network *network) {
99
3.53k
        assert(network);
100
3.53k
        assert(network->dhcp >= 0);
101
102
3.53k
        if (network->dhcp == ADDRESS_FAMILY_NO)
103
3.49k
                return;
104
105
        /* Bonding slave does not support addressing. */
106
38
        if (network->bond) {
107
0
                log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.",
108
0
                            network->filename);
109
0
                network->dhcp = ADDRESS_FAMILY_NO;
110
0
                return;
111
0
        }
112
113
38
        if (!FLAGS_SET(network->link_local, ADDRESS_FAMILY_IPV6) &&
114
23
            !network_has_static_ipv6ll_address(network) &&
115
22
            FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV6)) {
116
20
                log_warning("%s: DHCPv6 client is enabled but IPv6 link-local addressing is disabled. "
117
20
                            "Disabling DHCPv6 client.", network->filename);
118
20
                SET_FLAG(network->dhcp, ADDRESS_FAMILY_IPV6, false);
119
20
        }
120
121
38
        network_adjust_dhcp4(network);
122
38
        network_adjust_dhcp_prefix_delegation(network);
123
38
}
124
125
0
static bool duid_needs_product_uuid(const DUID *duid) {
126
0
        assert(duid);
127
128
0
        return duid->type == DUID_TYPE_UUID && duid->raw_data_len == 0;
129
0
}
130
131
static const struct DUID fallback_duid = { .type = DUID_TYPE_EN };
132
133
0
const DUID *link_get_duid(Link *link, int family) {
134
0
        const DUID *duid;
135
136
0
        assert(link);
137
0
        assert(IN_SET(family, AF_INET, AF_INET6));
138
139
0
        if (link->network) {
140
0
                duid = family == AF_INET ? &link->network->dhcp_duid : &link->network->dhcp6_duid;
141
0
                if (duid->type != _DUID_TYPE_INVALID) {
142
0
                        if (duid_needs_product_uuid(duid))
143
0
                                return &link->manager->duid_product_uuid;
144
0
                        else
145
0
                                return duid;
146
0
                }
147
0
        }
148
149
0
        duid = family == AF_INET ? &link->manager->dhcp_duid : &link->manager->dhcp6_duid;
150
0
        if (link->hw_addr.length == 0 && IN_SET(duid->type, DUID_TYPE_LLT, DUID_TYPE_LL))
151
                /* Fallback to DUID that works without MAC address.
152
                 * This is useful for tunnel devices without MAC address. */
153
0
                return &fallback_duid;
154
155
0
        return duid;
156
0
}
157
158
0
static int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
159
0
        Manager *manager = ASSERT_PTR(userdata);
160
0
        const sd_bus_error *e;
161
0
        const void *a;
162
0
        size_t sz;
163
0
        int r;
164
165
0
        assert(m);
166
167
        /* To avoid calling GetProductUUID() bus method so frequently, set the flag below
168
         * even if the method fails. */
169
0
        manager->has_product_uuid = true;
170
171
0
        e = sd_bus_message_get_error(m);
172
0
        if (e) {
173
0
                r = sd_bus_error_get_errno(e);
174
0
                log_warning_errno(r, "Could not get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %s",
175
0
                                  bus_error_message(e, r));
176
0
                return 0;
177
0
        }
178
179
0
        r = sd_bus_message_read_array(m, 'y', &a, &sz);
180
0
        if (r < 0) {
181
0
                log_warning_errno(r, "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m");
182
0
                return 0;
183
0
        }
184
185
0
        if (sz != sizeof(sd_id128_t)) {
186
0
                log_warning("Invalid product UUID. Falling back to use machine-app-specific ID as DUID-UUID.");
187
0
                return 0;
188
0
        }
189
190
0
        log_debug("Successfully obtained product UUID");
191
192
0
        memcpy(&manager->duid_product_uuid.raw_data, a, sz);
193
0
        manager->duid_product_uuid.raw_data_len = sz;
194
195
0
        return 0;
196
0
}
197
198
0
int manager_request_product_uuid(Manager *m) {
199
0
        static bool bus_method_is_called = false;
200
0
        int r;
201
202
0
        assert(m);
203
204
0
        if (bus_method_is_called)
205
0
                return 0;
206
207
0
        if (sd_bus_is_ready(m->bus) <= 0 && !m->product_uuid_requested) {
208
0
                log_debug("Not connected to system bus, requesting product UUID later.");
209
0
                m->product_uuid_requested = true;
210
0
                return 0;
211
0
        }
212
213
0
        m->product_uuid_requested = false;
214
215
0
        r = bus_call_method_async(
216
0
                        m->bus,
217
0
                        NULL,
218
0
                        bus_hostname,
219
0
                        "GetProductUUID",
220
0
                        get_product_uuid_handler,
221
0
                        m,
222
0
                        "b",
223
0
                        false);
224
0
        if (r < 0)
225
0
                return log_warning_errno(r, "Failed to get product UUID: %m");
226
227
0
        log_debug("Requesting product UUID.");
228
229
0
        bus_method_is_called = true;
230
231
0
        return 0;
232
0
}
233
234
0
int dhcp_configure_duid(Link *link, const DUID *duid) {
235
0
        Manager *m;
236
0
        int r;
237
238
0
        assert(link);
239
0
        assert(link->manager);
240
0
        assert(duid);
241
242
0
        m = link->manager;
243
244
0
        if (!duid_needs_product_uuid(duid))
245
0
                return 1;
246
247
0
        if (m->has_product_uuid)
248
0
                return 1;
249
250
0
        r = manager_request_product_uuid(m);
251
0
        if (r < 0) {
252
0
                log_link_warning_errno(link, r,
253
0
                                       "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m");
254
255
0
                m->has_product_uuid = true; /* Do not request UUID again on failure. */
256
0
                return 1;
257
0
        }
258
259
0
        return 0;
260
0
}
261
262
0
bool address_is_filtered(int family, const union in_addr_union *address, uint8_t prefixlen, Set *allow_list, Set *deny_list) {
263
0
        struct in_addr_prefix *p;
264
265
0
        assert(IN_SET(family, AF_INET, AF_INET6));
266
0
        assert(address);
267
268
0
        if (allow_list) {
269
0
                SET_FOREACH(p, allow_list)
270
0
                        if (p->family == family &&
271
0
                            p->prefixlen <= prefixlen &&
272
0
                            in_addr_prefix_covers(family, &p->address, p->prefixlen, address) > 0)
273
0
                                return false;
274
275
0
                return true;
276
0
        }
277
278
0
        SET_FOREACH(p, deny_list)
279
0
                if (p->family == family &&
280
0
                    in_addr_prefix_intersect(family, &p->address, p->prefixlen, address, prefixlen) > 0)
281
0
                        return true;
282
283
0
        return false;
284
0
}
285
286
0
int link_get_captive_portal(Link *link, const char **ret) {
287
0
        const char *dhcp4_cp = NULL, *dhcp6_cp = NULL, *ndisc_cp = NULL;
288
0
        int r;
289
290
0
        assert(link);
291
0
        assert(ret);
292
293
0
        if (!link->network) {
294
0
                *ret = NULL;
295
0
                return 0;
296
0
        }
297
298
0
        if (link->network->dhcp_use_captive_portal && link->dhcp_lease) {
299
0
                r = sd_dhcp_lease_get_captive_portal(link->dhcp_lease, &dhcp4_cp);
300
0
                if (r < 0 && r != -ENODATA)
301
0
                        return r;
302
0
        }
303
304
0
        if (link->network->dhcp6_use_captive_portal && link->dhcp6_lease) {
305
0
                r = sd_dhcp6_lease_get_captive_portal(link->dhcp6_lease, &dhcp6_cp);
306
0
                if (r < 0 && r != -ENODATA)
307
0
                        return r;
308
0
        }
309
310
0
        if (link->network->ndisc_use_captive_portal) {
311
0
                NDiscCaptivePortal *cp;
312
0
                usec_t usec = 0;
313
314
                /* Use the captive portal with the longest lifetime. */
315
316
0
                SET_FOREACH(cp, link->ndisc_captive_portals) {
317
0
                        if (cp->lifetime_usec < usec)
318
0
                                continue;
319
320
0
                        ndisc_cp = cp->captive_portal;
321
0
                        usec = cp->lifetime_usec;
322
0
                }
323
324
0
                if (set_size(link->ndisc_captive_portals) > 1)
325
0
                        log_link_debug(link, "Multiple captive portals obtained by IPv6RA, using \"%s\" and ignoring others.",
326
0
                                       ndisc_cp);
327
0
        }
328
329
0
        if (dhcp4_cp) {
330
0
                if (dhcp6_cp && !streq(dhcp4_cp, dhcp6_cp))
331
0
                        log_link_debug(link, "DHCPv6 captive portal (%s) does not match DHCPv4 (%s), ignoring DHCPv6 captive portal.",
332
0
                                       dhcp6_cp, dhcp4_cp);
333
334
0
                if (ndisc_cp && !streq(dhcp4_cp, ndisc_cp))
335
0
                        log_link_debug(link, "IPv6RA captive portal (%s) does not match DHCPv4 (%s), ignoring IPv6RA captive portal.",
336
0
                                       ndisc_cp, dhcp4_cp);
337
338
0
                *ret = dhcp4_cp;
339
0
                return 1;
340
0
        }
341
342
0
        if (dhcp6_cp) {
343
0
                if (ndisc_cp && !streq(dhcp6_cp, ndisc_cp))
344
0
                        log_link_debug(link, "IPv6RA captive portal (%s) does not match DHCPv6 (%s), ignoring IPv6RA captive portal.",
345
0
                                       ndisc_cp, dhcp6_cp);
346
347
0
                *ret = dhcp6_cp;
348
0
                return 1;
349
0
        }
350
351
0
        *ret = ndisc_cp;
352
0
        return !!ndisc_cp;
353
0
}
354
355
int config_parse_dhcp(
356
                const char *unit,
357
                const char *filename,
358
                unsigned line,
359
                const char *section,
360
                unsigned section_line,
361
                const char *lvalue,
362
                int ltype,
363
                const char *rvalue,
364
                void *data,
365
961
                void *userdata) {
366
367
961
        AddressFamily *dhcp = data, s;
368
369
961
        assert(filename);
370
961
        assert(lvalue);
371
961
        assert(rvalue);
372
961
        assert(data);
373
374
        /* Note that this is mostly like
375
         * config_parse_address_family(), except that it
376
         * understands some old names for the enum values */
377
378
961
        s = address_family_from_string(rvalue);
379
961
        if (s < 0) {
380
381
                /* Previously, we had a slightly different enum here,
382
                 * support its values for compatibility. */
383
384
531
                s = dhcp_deprecated_address_family_from_string(rvalue);
385
531
                if (s < 0) {
386
301
                        log_syntax(unit, LOG_WARNING, filename, line, s,
387
301
                                   "Failed to parse DHCP option, ignoring: %s", rvalue);
388
301
                        return 0;
389
301
                }
390
391
531
                log_syntax(unit, LOG_WARNING, filename, line, 0,
392
230
                           "DHCP=%s is deprecated, please use DHCP=%s instead.",
393
230
                           rvalue, address_family_to_string(s));
394
230
        }
395
396
660
        *dhcp = s;
397
660
        return 0;
398
961
}
399
400
int config_parse_dhcp_route_metric(
401
                const char *unit,
402
                const char *filename,
403
                unsigned line,
404
                const char *section,
405
                unsigned section_line,
406
                const char *lvalue,
407
                int ltype,
408
                const char *rvalue,
409
                void *data,
410
1.00k
                void *userdata) {
411
412
1.00k
        Network *network = userdata;
413
1.00k
        uint32_t metric;
414
1.00k
        int r;
415
416
1.00k
        assert(filename);
417
1.00k
        assert(lvalue);
418
1.00k
        assert(IN_SET(ltype, AF_UNSPEC, AF_INET));
419
1.00k
        assert(rvalue);
420
1.00k
        assert(data);
421
422
1.00k
        r = safe_atou32(rvalue, &metric);
423
1.00k
        if (r < 0) {
424
196
                log_syntax(unit, LOG_WARNING, filename, line, r,
425
196
                           "Failed to parse RouteMetric=%s, ignoring assignment: %m", rvalue);
426
196
                return 0;
427
196
        }
428
429
806
        switch (ltype) {
430
216
        case AF_INET:
431
216
                network->dhcp_route_metric = metric;
432
216
                network->dhcp_route_metric_set = true;
433
216
                break;
434
590
        case AF_UNSPEC:
435
                /* For backward compatibility. */
436
590
                if (!network->dhcp_route_metric_set)
437
396
                        network->dhcp_route_metric = metric;
438
590
                if (!network->ndisc_route_metric_set) {
439
396
                        network->ndisc_route_metric_high = metric;
440
396
                        network->ndisc_route_metric_medium = metric;
441
396
                        network->ndisc_route_metric_low = metric;
442
396
                }
443
590
                break;
444
0
        default:
445
0
                assert_not_reached();
446
806
        }
447
448
806
        return 0;
449
806
}
450
451
int config_parse_ndisc_route_metric(
452
                const char *unit,
453
                const char *filename,
454
                unsigned line,
455
                const char *section,
456
                unsigned section_line,
457
                const char *lvalue,
458
                int ltype,
459
                const char *rvalue,
460
                void *data,
461
2.19k
                void *userdata) {
462
463
2.19k
        Network *network = ASSERT_PTR(userdata);
464
2.19k
        uint32_t metric_high, metric_medium, metric_low;
465
2.19k
        int r, s, t;
466
467
2.19k
        assert(filename);
468
2.19k
        assert(rvalue);
469
470
2.19k
        if (safe_atou32(rvalue, &metric_low) >= 0)
471
204
                metric_high = metric_medium = metric_low;
472
1.99k
        else {
473
1.99k
                _cleanup_free_ char *high = NULL, *medium = NULL, *low = NULL;
474
1.99k
                const char *p = rvalue;
475
476
1.99k
                r = extract_many_words(&p, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &high, &medium, &low);
477
1.99k
                if (r == -ENOMEM)
478
0
                        return log_oom();
479
1.99k
                if (r != 3 || !isempty(p)) {
480
646
                        log_syntax(unit, LOG_WARNING, filename, line, r < 0 ? r : 0,
481
646
                                   "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue);
482
646
                        return 0;
483
646
                }
484
485
1.34k
                r = safe_atou32(high, &metric_high);
486
1.34k
                s = safe_atou32(medium, &metric_medium);
487
1.34k
                t = safe_atou32(low, &metric_low);
488
1.34k
                if (r < 0 || s < 0 || t < 0) {
489
680
                        log_syntax(unit, LOG_WARNING, filename, line, r < 0 ? r : s < 0 ? s : t,
490
680
                                   "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue);
491
680
                        return 0;
492
680
                }
493
494
664
                if (metric_high >= metric_medium || metric_medium >= metric_low) {
495
466
                        log_syntax(unit, LOG_WARNING, filename, line, 0,
496
466
                                   "Invalid RouteTable=%s, ignoring assignment: %m", rvalue);
497
466
                        return 0;
498
466
                }
499
664
        }
500
501
402
        network->ndisc_route_metric_high = metric_high;
502
402
        network->ndisc_route_metric_medium = metric_medium;
503
402
        network->ndisc_route_metric_low = metric_low;
504
402
        network->ndisc_route_metric_set = true;
505
506
402
        return 0;
507
2.19k
}
508
509
int config_parse_dhcp_send_hostname(
510
                const char *unit,
511
                const char *filename,
512
                unsigned line,
513
                const char *section,
514
                unsigned section_line,
515
                const char *lvalue,
516
                int ltype,
517
                const char *rvalue,
518
                void *data,
519
1.31k
                void *userdata) {
520
521
1.31k
        Network *network = userdata;
522
1.31k
        int r;
523
524
1.31k
        assert(filename);
525
1.31k
        assert(lvalue);
526
1.31k
        assert(IN_SET(ltype, AF_UNSPEC, AF_INET, AF_INET6));
527
1.31k
        assert(rvalue);
528
1.31k
        assert(data);
529
530
1.31k
        r = parse_boolean(rvalue);
531
1.31k
        if (r < 0) {
532
203
                log_syntax(unit, LOG_WARNING, filename, line, r,
533
203
                           "Failed to parse SendHostname=%s, ignoring assignment: %m", rvalue);
534
203
                return 0;
535
203
        }
536
537
1.10k
        switch (ltype) {
538
203
        case AF_INET:
539
203
                network->dhcp_send_hostname = r;
540
203
                network->dhcp_send_hostname_set = true;
541
203
                break;
542
202
        case AF_INET6:
543
202
                network->dhcp6_send_hostname = r;
544
202
                network->dhcp6_send_hostname_set = true;
545
202
                break;
546
704
        case AF_UNSPEC:
547
                /* For backward compatibility. */
548
704
                if (!network->dhcp_send_hostname_set)
549
510
                        network->dhcp_send_hostname = r;
550
704
                if (!network->dhcp6_send_hostname_set)
551
510
                        network->dhcp6_send_hostname = r;
552
704
                break;
553
0
        default:
554
0
                assert_not_reached();
555
1.10k
        }
556
557
1.10k
        return 0;
558
1.10k
}
559
560
int config_parse_dhcp_or_ra_route_table(
561
                const char *unit,
562
                const char *filename,
563
                unsigned line,
564
                const char *section,
565
                unsigned section_line,
566
                const char *lvalue,
567
                int ltype,
568
                const char *rvalue,
569
                void *data,
570
779
                void *userdata) {
571
572
779
        Network *network = ASSERT_PTR(userdata);
573
779
        uint32_t rt;
574
779
        int r;
575
576
779
        assert(filename);
577
779
        assert(lvalue);
578
779
        assert(IN_SET(ltype, NETWORK_CONFIG_SOURCE_DHCP4, NETWORK_CONFIG_SOURCE_DHCP6, NETWORK_CONFIG_SOURCE_NDISC));
579
779
        assert(rvalue);
580
581
779
        r = manager_get_route_table_from_string(network->manager, rvalue, &rt);
582
779
        if (r < 0) {
583
197
                log_syntax(unit, LOG_WARNING, filename, line, r,
584
197
                           "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue);
585
197
                return 0;
586
197
        }
587
588
582
        switch (ltype) {
589
194
        case NETWORK_CONFIG_SOURCE_DHCP4:
590
194
                network->dhcp_route_table = rt;
591
194
                network->dhcp_route_table_set = true;
592
194
                break;
593
194
        case NETWORK_CONFIG_SOURCE_DHCP6:
594
194
                network->dhcp6_route_table = rt;
595
194
                network->dhcp6_route_table_set = true;
596
194
                break;
597
194
        case NETWORK_CONFIG_SOURCE_NDISC:
598
194
                network->ndisc_route_table = rt;
599
194
                network->ndisc_route_table_set = true;
600
194
                break;
601
0
        default:
602
0
                assert_not_reached();
603
582
        }
604
605
582
        return 0;
606
582
}
607
608
int config_parse_iaid(
609
                const char *unit,
610
                const char *filename,
611
                unsigned line,
612
                const char *section,
613
                unsigned section_line,
614
                const char *lvalue,
615
                int ltype,
616
                const char *rvalue,
617
                void *data,
618
808
                void *userdata) {
619
620
808
        Network *network = ASSERT_PTR(userdata);
621
808
        uint32_t iaid;
622
808
        int r;
623
624
808
        assert(filename);
625
808
        assert(lvalue);
626
808
        assert(rvalue);
627
808
        assert(IN_SET(ltype, AF_INET, AF_INET6));
628
629
808
        r = safe_atou32(rvalue, &iaid);
630
808
        if (r < 0) {
631
216
                log_syntax(unit, LOG_WARNING, filename, line, r,
632
216
                           "Unable to read IAID, ignoring assignment: %s", rvalue);
633
216
                return 0;
634
216
        }
635
636
592
        if (ltype == AF_INET) {
637
390
                network->dhcp_iaid = iaid;
638
390
                network->dhcp_iaid_set = true;
639
390
                if (!network->dhcp6_iaid_set_explicitly) {
640
                        /* Backward compatibility. Previously, IAID is shared by DHCPv4 and DHCPv6.
641
                         * If DHCPv6 IAID is not specified explicitly, then use DHCPv4 IAID for DHCPv6. */
642
196
                        network->dhcp6_iaid = iaid;
643
196
                        network->dhcp6_iaid_set = true;
644
196
                }
645
390
        } else {
646
202
                assert(ltype == AF_INET6);
647
202
                network->dhcp6_iaid = iaid;
648
202
                network->dhcp6_iaid_set = true;
649
202
                network->dhcp6_iaid_set_explicitly = true;
650
202
        }
651
652
592
        return 0;
653
808
}
654
655
int config_parse_dhcp4_user_class(
656
                const char *unit,
657
                const char *filename,
658
                unsigned line,
659
                const char *section,
660
                unsigned section_line,
661
                const char *lvalue,
662
                int ltype,
663
                const char *rvalue,
664
                void *data,
665
1.18k
                void *userdata) {
666
667
1.18k
        struct iovec_wrapper *iovw = ASSERT_PTR(data);
668
1.18k
        int r;
669
670
1.18k
        assert(lvalue);
671
1.18k
        assert(rvalue);
672
673
1.18k
        if (isempty(rvalue)) {
674
482
                iovw_done_free(iovw);
675
482
                return 0;
676
482
        }
677
678
5.74k
        for (const char *p = rvalue;;) {
679
5.74k
                _cleanup_free_ char *w = NULL;
680
681
5.74k
                r = extract_first_word(&p, &w, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
682
5.74k
                if (r < 0)
683
195
                        return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
684
5.55k
                if (r == 0)
685
506
                        return 0;
686
687
5.04k
                size_t len = strlen(w);
688
5.04k
                if (len > UINT8_MAX || len == 0) {
689
460
                        log_syntax(unit, LOG_WARNING, filename, line, 0,
690
460
                                   "The length of the %s entry '%s' is not in the range 1…255, ignoring.", lvalue, w);
691
460
                        continue;
692
460
                }
693
694
4.58k
                r = iovw_consume(iovw, TAKE_PTR(w), len);
695
4.58k
                if (r < 0)
696
1
                        return log_oom();
697
4.58k
        }
698
702
}
699
700
int config_parse_dhcp6_user_or_vendor_class(
701
                const char *unit,
702
                const char *filename,
703
                unsigned line,
704
                const char *section,
705
                unsigned section_line,
706
                const char *lvalue,
707
                int ltype,
708
                const char *rvalue,
709
                void *data,
710
775
                void *userdata) {
711
712
775
        char ***l = ASSERT_PTR(data);
713
775
        int r;
714
715
775
        assert(lvalue);
716
775
        assert(rvalue);
717
718
775
        if (isempty(rvalue)) {
719
349
                *l = strv_free(*l);
720
349
                return 0;
721
349
        }
722
723
4.14k
        for (const char *p = rvalue;;) {
724
4.14k
                _cleanup_free_ char *w = NULL;
725
726
4.14k
                r = extract_first_word(&p, &w, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
727
4.14k
                if (r < 0)
728
194
                        return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
729
3.95k
                if (r == 0)
730
232
                        return 0;
731
732
3.72k
                size_t len = strlen(w);
733
3.72k
                if (len > UINT16_MAX || len == 0) {
734
275
                        log_syntax(unit, LOG_WARNING, filename, line, 0,
735
275
                                   "The length of the %s entry '%s' is not in the range 1…65535, ignoring.", lvalue, w);
736
275
                        continue;
737
275
                }
738
739
3.44k
                r = strv_consume(l, TAKE_PTR(w));
740
3.44k
                if (r < 0)
741
0
                        return log_oom();
742
3.44k
        }
743
426
}
744
745
int config_parse_dhcp6_send_option(
746
                const char *unit,
747
                const char *filename,
748
                unsigned line,
749
                const char *section,
750
                unsigned section_line,
751
                const char *lvalue,
752
                int ltype,
753
                const char *rvalue,
754
                void *data,
755
5.94k
                void *userdata) {
756
757
5.94k
        _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *opt6 = NULL;
758
5.94k
        _unused_ _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *old6 = NULL;
759
5.94k
        uint32_t uint32_data, enterprise_identifier = 0;
760
5.94k
        _cleanup_free_ char *word = NULL, *q = NULL;
761
5.94k
        OrderedHashmap **dhcp6_options = ASSERT_PTR(data);
762
5.94k
        uint16_t u16, uint16_data;
763
5.94k
        union in_addr_union addr;
764
5.94k
        DHCPOptionDataType type;
765
5.94k
        uint8_t uint8_data;
766
5.94k
        const void *udata;
767
5.94k
        const char *p;
768
5.94k
        ssize_t sz;
769
5.94k
        int r;
770
771
5.94k
        assert(filename);
772
5.94k
        assert(lvalue);
773
5.94k
        assert(rvalue);
774
775
5.94k
        if (isempty(rvalue)) {
776
483
                *dhcp6_options = ordered_hashmap_free(*dhcp6_options);
777
483
                return 0;
778
483
        }
779
780
5.46k
        p = rvalue;
781
5.46k
        if (streq(lvalue, "SendVendorOption")) {
782
785
                r = extract_first_word(&p, &word, ":", 0);
783
785
                if (r == -ENOMEM)
784
0
                        return log_oom();
785
785
                if (r <= 0 || isempty(p)) {
786
389
                        log_syntax(unit, LOG_WARNING, filename, line, r,
787
389
                                   "Invalid DHCP option, ignoring assignment: %s", rvalue);
788
389
                        return 0;
789
389
                }
790
791
396
                r = safe_atou32(word, &enterprise_identifier);
792
396
                if (r < 0) {
793
194
                        log_syntax(unit, LOG_WARNING, filename, line, r,
794
194
                                   "Failed to parse DHCPv6 enterprise identifier data, ignoring assignment: %s", p);
795
194
                        return 0;
796
194
                }
797
202
                word = mfree(word);
798
202
        }
799
800
4.87k
        r = extract_first_word(&p, &word, ":", 0);
801
4.87k
        if (r == -ENOMEM)
802
0
                return log_oom();
803
4.87k
        if (r <= 0 || isempty(p)) {
804
592
                log_syntax(unit, LOG_WARNING, filename, line, r,
805
592
                           "Invalid DHCP option, ignoring assignment: %s", rvalue);
806
592
                return 0;
807
592
        }
808
809
4.28k
        r = safe_atou16(word, &u16);
810
4.28k
        if (r < 0) {
811
216
                log_syntax(unit, LOG_WARNING, filename, line, r,
812
216
                           "Invalid DHCP option, ignoring assignment: %s", rvalue);
813
216
                return 0;
814
216
        }
815
4.07k
        if (u16 < 1 || u16 >= UINT16_MAX) {
816
389
                log_syntax(unit, LOG_WARNING, filename, line, 0,
817
389
                           "Invalid DHCP option, valid range is 1-65535, ignoring assignment: %s", rvalue);
818
389
                return 0;
819
389
        }
820
821
3.68k
        word = mfree(word);
822
3.68k
        r = extract_first_word(&p, &word, ":", 0);
823
3.68k
        if (r == -ENOMEM)
824
0
                return log_oom();
825
3.68k
        if (r <= 0 || isempty(p)) {
826
433
                log_syntax(unit, LOG_WARNING, filename, line, r,
827
433
                           "Invalid DHCP option, ignoring assignment: %s", rvalue);
828
433
                return 0;
829
433
        }
830
831
3.24k
        type = dhcp_option_data_type_from_string(word);
832
3.24k
        if (type < 0) {
833
0
                log_syntax(unit, LOG_WARNING, filename, line, type,
834
0
                           "Invalid DHCP option data type, ignoring assignment: %s", p);
835
0
                return 0;
836
0
        }
837
838
3.24k
        switch (type) {
839
1.02k
        case DHCP_OPTION_DATA_UINT8:{
840
1.02k
                r = safe_atou8(p, &uint8_data);
841
1.02k
                if (r < 0) {
842
199
                        log_syntax(unit, LOG_WARNING, filename, line, r,
843
199
                                   "Failed to parse DHCP uint8 data, ignoring assignment: %s", p);
844
199
                        return 0;
845
199
                }
846
847
825
                udata = &uint8_data;
848
825
                sz = sizeof(uint8_t);
849
825
                break;
850
1.02k
        }
851
391
        case DHCP_OPTION_DATA_UINT16:{
852
391
                uint16_t k;
853
854
391
                r = safe_atou16(p, &k);
855
391
                if (r < 0) {
856
195
                        log_syntax(unit, LOG_WARNING, filename, line, r,
857
195
                                   "Failed to parse DHCP uint16 data, ignoring assignment: %s", p);
858
195
                        return 0;
859
195
                }
860
861
196
                uint16_data = htobe16(k);
862
196
                udata = &uint16_data;
863
196
                sz = sizeof(uint16_t);
864
196
                break;
865
391
        }
866
448
        case DHCP_OPTION_DATA_UINT32: {
867
448
                uint32_t k;
868
869
448
                r = safe_atou32(p, &k);
870
448
                if (r < 0) {
871
194
                        log_syntax(unit, LOG_WARNING, filename, line, r,
872
194
                                   "Failed to parse DHCP uint32 data, ignoring assignment: %s", p);
873
194
                        return 0;
874
194
                }
875
876
254
                uint32_data = htobe32(k);
877
254
                udata = &uint32_data;
878
254
                sz = sizeof(uint32_t);
879
880
254
                break;
881
448
        }
882
399
        case DHCP_OPTION_DATA_IPV4ADDRESS: {
883
399
                r = in_addr_from_string(AF_INET, p, &addr);
884
399
                if (r < 0) {
885
205
                        log_syntax(unit, LOG_WARNING, filename, line, r,
886
205
                                   "Failed to parse DHCP ipv4address data, ignoring assignment: %s", p);
887
205
                        return 0;
888
205
                }
889
890
194
                udata = &addr.in;
891
194
                sz = sizeof(addr.in.s_addr);
892
194
                break;
893
399
        }
894
391
        case DHCP_OPTION_DATA_IPV6ADDRESS: {
895
391
                r = in_addr_from_string(AF_INET6, p, &addr);
896
391
                if (r < 0) {
897
197
                        log_syntax(unit, LOG_WARNING, filename, line, r,
898
197
                                   "Failed to parse DHCP ipv6address data, ignoring assignment: %s", p);
899
197
                        return 0;
900
197
                }
901
902
194
                udata = &addr.in6;
903
194
                sz = sizeof(addr.in6.s6_addr);
904
194
                break;
905
391
        }
906
589
        case DHCP_OPTION_DATA_STRING:
907
589
                sz = cunescape(p, UNESCAPE_ACCEPT_NUL, &q);
908
589
                if (sz < 0) {
909
195
                        log_syntax(unit, LOG_WARNING, filename, line, sz,
910
195
                                   "Failed to decode DHCP option data, ignoring assignment: %s", p);
911
195
                        return 0;
912
195
                }
913
914
394
                udata = q;
915
394
                break;
916
6
        default:
917
6
                return -EINVAL;
918
3.24k
        }
919
920
2.05k
        r = sd_dhcp6_option_new(u16, udata, sz, enterprise_identifier, &opt6);
921
2.05k
        if (r < 0) {
922
0
                log_syntax(unit, LOG_WARNING, filename, line, r,
923
0
                           "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
924
0
                return 0;
925
0
        }
926
927
2.05k
        r = ordered_hashmap_ensure_allocated(dhcp6_options, &dhcp6_option_hash_ops);
928
2.05k
        if (r < 0)
929
0
                return log_oom();
930
931
        /* Overwrite existing option */
932
2.05k
        old6 = ordered_hashmap_get(*dhcp6_options, UINT_TO_PTR(u16));
933
2.05k
        r = ordered_hashmap_replace(*dhcp6_options, UINT_TO_PTR(u16), opt6);
934
2.05k
        if (r < 0) {
935
0
                log_syntax(unit, LOG_WARNING, filename, line, r,
936
0
                           "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
937
0
                return 0;
938
0
        }
939
2.05k
        TAKE_PTR(opt6);
940
941
2.05k
        return 0;
942
2.05k
}
943
944
int config_parse_dhcp_option(
945
                const char *unit,
946
                const char *filename,
947
                unsigned line,
948
                const char *section,
949
                unsigned section_line,
950
                const char *lvalue,
951
                int ltype,
952
                const char *rvalue,
953
                void *data,
954
29.8k
                void *userdata) {
955
956
29.8k
        struct iovec *iov = ASSERT_PTR(data);
957
29.8k
        bool check_length = ltype;
958
29.8k
        int r;
959
960
29.8k
        if (isempty(rvalue)) {
961
194
                iovec_done(iov);
962
194
                return 0;
963
194
        }
964
965
29.6k
        _cleanup_free_ char *word = NULL;
966
29.6k
        const char *p = rvalue;
967
29.6k
        r = extract_first_word(&p, &word, ":", 0);
968
29.6k
        if (r == -ENOMEM)
969
0
                return log_oom();
970
29.6k
        if (r <= 0 || isempty(p))
971
607
                return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
972
973
29.0k
        DHCPOptionDataType type = dhcp_option_data_type_from_string(word);
974
29.0k
        if (type < 0)
975
0
                return log_syntax_parse_error(unit, filename, line, type, lvalue, rvalue);
976
977
29.0k
        switch (type) {
978
24.8k
        case DHCP_OPTION_DATA_UINT8:{
979
24.8k
                uint8_t u;
980
981
24.8k
                r = safe_atou8(p, &u);
982
24.8k
                if (r < 0)
983
288
                        return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
984
985
24.6k
                r = iovec_done_and_memdup(iov, &IOVEC_MAKE(&u, sizeof(u)));
986
24.6k
                if (r < 0)
987
0
                        return log_oom();
988
989
24.6k
                return 1;
990
24.6k
        }
991
473
        case DHCP_OPTION_DATA_UINT16:{
992
473
                uint16_t u;
993
994
473
                r = safe_atou16(p, &u);
995
473
                if (r < 0)
996
194
                        return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
997
998
279
                u = htobe16(u);
999
279
                r = iovec_done_and_memdup(iov, &IOVEC_MAKE(&u, sizeof(u)));
1000
279
                if (r < 0)
1001
0
                        return log_oom();
1002
1003
279
                return 1;
1004
279
        }
1005
1.48k
        case DHCP_OPTION_DATA_UINT32: {
1006
1.48k
                uint32_t u;
1007
1008
1.48k
                r = safe_atou32(p, &u);
1009
1.48k
                if (r < 0)
1010
196
                        return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
1011
1012
1.29k
                u = htobe32(u);
1013
1.29k
                r = iovec_done_and_memdup(iov, &IOVEC_MAKE(&u, sizeof(u)));
1014
1.29k
                if (r < 0)
1015
0
                        return log_oom();
1016
1017
1.29k
                return 1;
1018
1.29k
        }
1019
393
        case DHCP_OPTION_DATA_IPV4ADDRESS: {
1020
393
                union in_addr_union a;
1021
1022
393
                r = in_addr_from_string(AF_INET, p, &a);
1023
393
                if (r < 0)
1024
199
                        return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
1025
1026
194
                r = iovec_done_and_memdup(iov, &IOVEC_MAKE(&a.in, sizeof(a.in)));
1027
194
                if (r < 0)
1028
0
                        return log_oom();
1029
1030
194
                return 1;
1031
194
        }
1032
388
        case DHCP_OPTION_DATA_IPV6ADDRESS: {
1033
388
                union in_addr_union a;
1034
1035
388
                r = in_addr_from_string(AF_INET6, p, &a);
1036
388
                if (r < 0)
1037
194
                        return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
1038
1039
194
                r = iovec_done_and_memdup(iov, &IOVEC_MAKE(&a.in6, sizeof(a.in6)));
1040
194
                if (r < 0)
1041
0
                        return log_oom();
1042
1043
194
                return 1;
1044
194
        }
1045
1.44k
        case DHCP_OPTION_DATA_STRING: {
1046
1.44k
                _cleanup_free_ char *s = NULL;
1047
1.44k
                ssize_t sz = cunescape(p, UNESCAPE_ACCEPT_NUL, &s);
1048
1.44k
                if (sz < 0)
1049
200
                        return log_syntax_parse_error(unit, filename, line, sz, lvalue, rvalue);
1050
1.24k
                if (check_length && sz > UINT8_MAX)
1051
195
                        return log_syntax_parse_error(unit, filename, line, 0, lvalue, rvalue);
1052
1053
1.04k
                iovec_done(iov);
1054
1.04k
                *iov = IOVEC_MAKE(TAKE_PTR(s), sz);
1055
1.04k
                return 1;
1056
1.24k
        }
1057
8
        default:
1058
8
                return -EINVAL;
1059
29.0k
        }
1060
29.0k
}
1061
1062
int config_parse_dhcp_option_tlv(
1063
                const char *unit,
1064
                const char *filename,
1065
                unsigned line,
1066
                const char *section,
1067
                unsigned section_line,
1068
                const char *lvalue,
1069
                int ltype,
1070
                const char *rvalue,
1071
                void *data,
1072
31.6k
                void *userdata) {
1073
1074
31.6k
        TLV *options = ASSERT_PTR(data);
1075
31.6k
        int r;
1076
1077
31.6k
        if (isempty(rvalue)) {
1078
1.25k
                tlv_done(options);
1079
1.25k
                return 0;
1080
1.25k
        }
1081
1082
30.4k
        _cleanup_free_ char *word = NULL;
1083
30.4k
        const char *p = rvalue;
1084
30.4k
        r = extract_first_word(&p, &word, ":", 0);
1085
30.4k
        if (r == -ENOMEM)
1086
0
                return log_oom();
1087
30.4k
        if (r <= 0 || isempty(p))
1088
424
                return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
1089
1090
30.0k
        uint8_t code;
1091
30.0k
        r = safe_atou8(word, &code);
1092
30.0k
        if (r < 0)
1093
629
                return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
1094
29.3k
        if (code < 1 || code >= UINT8_MAX)
1095
805
                return log_syntax_parse_error(unit, filename, line, 0, lvalue, rvalue);
1096
1097
28.5k
        _cleanup_(iovec_done) struct iovec iov = {};
1098
28.5k
        r = config_parse_dhcp_option(unit, filename, line, section, section_line, lvalue, ltype, p, &iov, userdata);
1099
28.5k
        if (r <= 0)
1100
1.87k
                return r;
1101
1102
26.7k
        r = tlv_append_iov(options, code, &iov);
1103
26.7k
        if (r < 0)
1104
592
                log_syntax(unit, LOG_WARNING, filename, line, r,
1105
26.7k
                           "Failed to store '%s=%s', ignoring assignment: %m", lvalue, rvalue);
1106
1107
26.7k
        return 0;
1108
28.5k
}
1109
1110
int config_parse_dhcp_request_options(
1111
                const char *unit,
1112
                const char *filename,
1113
                unsigned line,
1114
                const char *section,
1115
                unsigned section_line,
1116
                const char *lvalue,
1117
                int ltype,
1118
                const char *rvalue,
1119
                void *data,
1120
1.27k
                void *userdata) {
1121
1122
1.27k
        Network *network = userdata;
1123
1.27k
        int r;
1124
1125
1.27k
        assert(filename);
1126
1.27k
        assert(lvalue);
1127
1.27k
        assert(rvalue);
1128
1.27k
        assert(data);
1129
1130
1.27k
        if (isempty(rvalue)) {
1131
578
                if (ltype == AF_INET)
1132
359
                        network->dhcp_request_options = set_free(network->dhcp_request_options);
1133
219
                else
1134
219
                        network->dhcp6_request_options = set_free(network->dhcp6_request_options);
1135
1136
578
                return 0;
1137
578
        }
1138
1139
3.01k
        for (const char *p = rvalue;;) {
1140
3.01k
                _cleanup_free_ char *n = NULL;
1141
3.01k
                uint32_t i;
1142
1143
3.01k
                r = extract_first_word(&p, &n, NULL, 0);
1144
3.01k
                if (r == -ENOMEM)
1145
0
                        return log_oom();
1146
3.01k
                if (r < 0) {
1147
194
                        log_syntax(unit, LOG_WARNING, filename, line, r,
1148
194
                                   "Failed to parse DHCP request option, ignoring assignment: %s",
1149
194
                                   rvalue);
1150
194
                        return 0;
1151
194
                }
1152
2.82k
                if (r == 0)
1153
501
                        return 0;
1154
1155
2.32k
                r = safe_atou32(n, &i);
1156
2.32k
                if (r < 0) {
1157
430
                        log_syntax(unit, LOG_WARNING, filename, line, r,
1158
430
                                   "DHCP request option is invalid, ignoring assignment: %s", n);
1159
430
                        continue;
1160
430
                }
1161
1162
1.89k
                if (i < 1 || i >= UINT8_MAX) {
1163
685
                        log_syntax(unit, LOG_WARNING, filename, line, 0,
1164
685
                                   "DHCP request option is invalid, valid range is 1-254, ignoring assignment: %s", n);
1165
685
                        continue;
1166
685
                }
1167
1168
1.20k
                r = set_ensure_put(ltype == AF_INET ? &network->dhcp_request_options : &network->dhcp6_request_options,
1169
1.20k
                                   NULL, UINT32_TO_PTR(i));
1170
1.20k
                if (r < 0)
1171
0
                        log_syntax(unit, LOG_WARNING, filename, line, r,
1172
1.20k
                                   "Failed to store DHCP request option '%s', ignoring assignment: %m", n);
1173
1.20k
        }
1174
695
}
1175
1176
static const char * const dhcp_option_data_type_table[_DHCP_OPTION_DATA_MAX] = {
1177
        [DHCP_OPTION_DATA_UINT8]       = "uint8",
1178
        [DHCP_OPTION_DATA_UINT16]      = "uint16",
1179
        [DHCP_OPTION_DATA_UINT32]      = "uint32",
1180
        [DHCP_OPTION_DATA_STRING]      = "string",
1181
        [DHCP_OPTION_DATA_IPV4ADDRESS] = "ipv4address",
1182
        [DHCP_OPTION_DATA_IPV6ADDRESS] = "ipv6address",
1183
};
1184
1185
DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type, DHCPOptionDataType);
1186
1187
static const char* const duid_type_table[_DUID_TYPE_MAX] = {
1188
        [DUID_TYPE_LLT]  = "link-layer-time",
1189
        [DUID_TYPE_EN]   = "vendor",
1190
        [DUID_TYPE_LL]   = "link-layer",
1191
        [DUID_TYPE_UUID] = "uuid",
1192
};
1193
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(duid_type, DUIDType);
1194
1195
int config_parse_duid_type(
1196
                const char *unit,
1197
                const char *filename,
1198
                unsigned line,
1199
                const char *section,
1200
                unsigned section_line,
1201
                const char *lvalue,
1202
                int ltype,
1203
                const char *rvalue,
1204
                void *data,
1205
19.1k
                void *userdata) {
1206
1207
19.1k
        _cleanup_free_ char *type_string = NULL;
1208
19.1k
        const char *p = ASSERT_PTR(rvalue);
1209
19.1k
        bool force = ltype;
1210
19.1k
        DUID *duid = ASSERT_PTR(data);
1211
19.1k
        DUIDType type;
1212
19.1k
        int r;
1213
1214
19.1k
        assert(filename);
1215
19.1k
        assert(lvalue);
1216
1217
19.1k
        if (!force && duid->set)
1218
0
                return 0;
1219
1220
19.1k
        r = extract_first_word(&p, &type_string, ":", 0);
1221
19.1k
        if (r == -ENOMEM)
1222
0
                return log_oom();
1223
19.1k
        if (r < 0) {
1224
194
                log_syntax(unit, LOG_WARNING, filename, line, r,
1225
194
                           "Invalid syntax, ignoring: %s", rvalue);
1226
194
                return 0;
1227
194
        }
1228
18.9k
        if (r == 0) {
1229
848
                log_syntax(unit, LOG_WARNING, filename, line, 0,
1230
848
                           "Failed to extract DUID type from '%s', ignoring.", rvalue);
1231
848
                return 0;
1232
848
        }
1233
1234
18.0k
        type = duid_type_from_string(type_string);
1235
18.0k
        if (type < 0) {
1236
17.4k
                uint16_t t;
1237
1238
17.4k
                r = safe_atou16(type_string, &t);
1239
17.4k
                if (r < 0) {
1240
711
                        log_syntax(unit, LOG_WARNING, filename, line, r,
1241
711
                                   "Failed to parse DUID type '%s', ignoring.", type_string);
1242
711
                        return 0;
1243
711
                }
1244
1245
16.7k
                type = t;
1246
16.7k
                assert(type == t); /* Check if type can store uint16_t. */
1247
16.7k
        }
1248
1249
17.3k
        if (!isempty(p)) {
1250
16.7k
                usec_t u;
1251
1252
16.7k
                if (type != DUID_TYPE_LLT) {
1253
308
                        log_syntax(unit, LOG_WARNING, filename, line, r,
1254
308
                                   "Invalid syntax, ignoring: %s", rvalue);
1255
308
                        return 0;
1256
308
                }
1257
1258
16.4k
                r = parse_timestamp(p, &u);
1259
16.4k
                if (r < 0) {
1260
12.5k
                        log_syntax(unit, LOG_WARNING, filename, line, r,
1261
12.5k
                                   "Failed to parse timestamp, ignoring: %s", p);
1262
12.5k
                        return 0;
1263
12.5k
                }
1264
1265
3.94k
                duid->llt_time = u;
1266
3.94k
        }
1267
1268
4.53k
        duid->type = type;
1269
4.53k
        duid->set = force;
1270
1271
4.53k
        return 0;
1272
17.3k
}
1273
1274
int config_parse_manager_duid_type(
1275
                const char *unit,
1276
                const char *filename,
1277
                unsigned line,
1278
                const char *section,
1279
                unsigned section_line,
1280
                const char *lvalue,
1281
                int ltype,
1282
                const char *rvalue,
1283
                void *data,
1284
0
                void *userdata) {
1285
1286
0
        Manager *manager = ASSERT_PTR(userdata);
1287
0
        int r;
1288
1289
        /* For backward compatibility. Setting both DHCPv4 and DHCPv6 DUID if they are not specified explicitly. */
1290
1291
0
        r = config_parse_duid_type(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp_duid, manager);
1292
0
        if (r < 0)
1293
0
                return r;
1294
1295
0
        return config_parse_duid_type(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp6_duid, manager);
1296
0
}
1297
1298
int config_parse_network_duid_type(
1299
                const char *unit,
1300
                const char *filename,
1301
                unsigned line,
1302
                const char *section,
1303
                unsigned section_line,
1304
                const char *lvalue,
1305
                int ltype,
1306
                const char *rvalue,
1307
                void *data,
1308
9.43k
                void *userdata) {
1309
1310
9.43k
        Network *network = ASSERT_PTR(userdata);
1311
9.43k
        int r;
1312
1313
9.43k
        r = config_parse_duid_type(unit, filename, line, section, section_line, lvalue, true, rvalue, &network->dhcp_duid, network);
1314
9.43k
        if (r < 0)
1315
0
                return r;
1316
1317
        /* For backward compatibility, also set DHCPv6 DUID if not specified explicitly. */
1318
9.43k
        return config_parse_duid_type(unit, filename, line, section, section_line, lvalue, false, rvalue, &network->dhcp6_duid, network);
1319
9.43k
}
1320
1321
int config_parse_duid_rawdata(
1322
                const char *unit,
1323
                const char *filename,
1324
                unsigned line,
1325
                const char *section,
1326
                unsigned section_line,
1327
                const char *lvalue,
1328
                int ltype,
1329
                const char *rvalue,
1330
                void *data,
1331
1.87k
                void *userdata) {
1332
1333
1.87k
        uint8_t raw_data[MAX_DUID_DATA_LEN];
1334
1.87k
        unsigned count = 0;
1335
1.87k
        bool force = ltype;
1336
1.87k
        DUID *duid = ASSERT_PTR(data);
1337
1338
1.87k
        assert(filename);
1339
1.87k
        assert(lvalue);
1340
1.87k
        assert(rvalue);
1341
1342
1.87k
        if (!force && duid->set)
1343
0
                return 0;
1344
1345
        /* RawData contains DUID in format "NN:NN:NN..." */
1346
28.1k
        for (const char *p = rvalue;;) {
1347
28.1k
                int n1, n2, len, r;
1348
28.1k
                uint32_t byte;
1349
28.1k
                _cleanup_free_ char *cbyte = NULL;
1350
1351
28.1k
                r = extract_first_word(&p, &cbyte, ":", 0);
1352
28.1k
                if (r == -ENOMEM)
1353
0
                        return log_oom();
1354
28.1k
                if (r < 0) {
1355
194
                        log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to read DUID, ignoring assignment: %s.", rvalue);
1356
194
                        return 0;
1357
194
                }
1358
27.9k
                if (r == 0)
1359
444
                        break;
1360
1361
27.5k
                if (count >= MAX_DUID_DATA_LEN) {
1362
194
                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue);
1363
194
                        return 0;
1364
194
                }
1365
1366
27.3k
                len = strlen(cbyte);
1367
27.3k
                if (!IN_SET(len, 1, 2)) {
1368
206
                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue);
1369
206
                        return 0;
1370
206
                }
1371
27.1k
                n1 = unhexchar(cbyte[0]);
1372
27.1k
                if (len == 2)
1373
1.36k
                        n2 = unhexchar(cbyte[1]);
1374
25.7k
                else
1375
25.7k
                        n2 = 0;
1376
1377
27.1k
                if (n1 < 0 || n2 < 0) {
1378
840
                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue);
1379
840
                        return 0;
1380
840
                }
1381
1382
26.3k
                byte = ((uint8_t) n1 << (4 * (len-1))) | (uint8_t) n2;
1383
26.3k
                raw_data[count++] = byte;
1384
26.3k
        }
1385
1386
444
        assert_cc(sizeof(raw_data) == sizeof(duid->raw_data));
1387
444
        memcpy(duid->raw_data, raw_data, count);
1388
444
        duid->raw_data_len = count;
1389
444
        duid->set = force;
1390
1391
444
        return 0;
1392
1.87k
}
1393
1394
int config_parse_manager_duid_rawdata(
1395
                const char *unit,
1396
                const char *filename,
1397
                unsigned line,
1398
                const char *section,
1399
                unsigned section_line,
1400
                const char *lvalue,
1401
                int ltype,
1402
                const char *rvalue,
1403
                void *data,
1404
0
                void *userdata) {
1405
1406
0
        Manager *manager = ASSERT_PTR(userdata);
1407
0
        int r;
1408
1409
        /* For backward compatibility. Setting both DHCPv4 and DHCPv6 DUID if they are not specified explicitly. */
1410
1411
0
        r = config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp_duid, manager);
1412
0
        if (r < 0)
1413
0
                return r;
1414
1415
0
        return config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp6_duid, manager);
1416
0
}
1417
1418
int config_parse_network_duid_rawdata(
1419
                const char *unit,
1420
                const char *filename,
1421
                unsigned line,
1422
                const char *section,
1423
                unsigned section_line,
1424
                const char *lvalue,
1425
                int ltype,
1426
                const char *rvalue,
1427
                void *data,
1428
864
                void *userdata) {
1429
1430
864
        Network *network = ASSERT_PTR(userdata);
1431
864
        int r;
1432
1433
864
        r = config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, true, rvalue, &network->dhcp_duid, network);
1434
864
        if (r < 0)
1435
0
                return r;
1436
1437
        /* For backward compatibility, also set DHCPv6 DUID if not specified explicitly. */
1438
864
        return config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, false, rvalue, &network->dhcp6_duid, network);
1439
864
}
1440
1441
int config_parse_uplink(
1442
                const char *unit,
1443
                const char *filename,
1444
                unsigned line,
1445
                const char *section,
1446
                unsigned section_line,
1447
                const char *lvalue,
1448
                int ltype,
1449
                const char *rvalue,
1450
                void *data,
1451
1.63k
                void *userdata) {
1452
1453
1.63k
        Network *network = ASSERT_PTR(userdata);
1454
1.63k
        bool accept_none = true;
1455
1.63k
        int *index, r;
1456
1.63k
        char **name;
1457
1458
1.63k
        assert(filename);
1459
1.63k
        assert(section);
1460
1.63k
        assert(lvalue);
1461
1.63k
        assert(rvalue);
1462
1463
1.63k
        if (streq(section, "DHCPServer")) {
1464
984
                index = &network->dhcp_server_uplink_index;
1465
984
                name = &network->dhcp_server_uplink_name;
1466
984
        } else if (streq(section, "IPv6SendRA")) {
1467
254
                index = &network->router_uplink_index;
1468
254
                name = &network->router_uplink_name;
1469
401
        } else if (STR_IN_SET(section, "DHCPv6PrefixDelegation", "DHCPPrefixDelegation")) {
1470
401
                index = &network->dhcp_pd_uplink_index;
1471
401
                name = &network->dhcp_pd_uplink_name;
1472
401
                accept_none = false;
1473
401
        } else
1474
0
                assert_not_reached();
1475
1476
1.63k
        if (isempty(rvalue) || streq(rvalue, ":auto")) {
1477
570
                *index = UPLINK_INDEX_AUTO;
1478
570
                *name = mfree(*name);
1479
570
                return 0;
1480
570
        }
1481
1482
1.06k
        if (accept_none && streq(rvalue, ":none")) {
1483
194
                *index = UPLINK_INDEX_NONE;
1484
194
                *name = mfree(*name);
1485
194
                return 0;
1486
194
        }
1487
1488
875
        if (!accept_none && streq(rvalue, ":self")) {
1489
194
                *index = UPLINK_INDEX_SELF;
1490
194
                *name = mfree(*name);
1491
194
                return 0;
1492
194
        }
1493
1494
681
        r = parse_ifindex(rvalue);
1495
681
        if (r > 0) {
1496
210
                *index = r;
1497
210
                *name = mfree(*name);
1498
210
                return 0;
1499
210
        }
1500
1501
471
        if (!ifname_valid_full(rvalue, IFNAME_VALID_ALTERNATIVE)) {
1502
241
                log_syntax(unit, LOG_WARNING, filename, line, 0,
1503
241
                           "Invalid interface name in %s=, ignoring assignment: %s", lvalue, rvalue);
1504
241
                return 0;
1505
241
        }
1506
1507
        /* The interface name will be resolved later. */
1508
230
        r = free_and_strdup_warn(name, rvalue);
1509
230
        if (r < 0)
1510
0
                return r;
1511
1512
        /* Note, if uplink_name is set, then uplink_index will be ignored. So, the below does not mean
1513
         * an uplink interface will be selected automatically. */
1514
230
        *index = UPLINK_INDEX_AUTO;
1515
230
        return 0;
1516
230
}