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