]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-dhcp-common.c
network/link: shorten code a bit
[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
e47bcb7d
YW
43uint32_t link_get_ipv6_accept_ra_route_table(Link *link) {
44 assert(link);
45 assert(link->network);
46
47 if (link->network->ipv6_accept_ra_route_table_set)
48 return link->network->ipv6_accept_ra_route_table;
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
64de00c4
YW
284 if (link->network->ipv6_accept_ra_use_captive_portal) {
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;
6f812d28
YW
412 if (!network->ipv6_accept_ra_route_metric_set) {
413 network->ipv6_accept_ra_route_metric_high = metric;
414 network->ipv6_accept_ra_route_metric_medium = metric;
415 network->ipv6_accept_ra_route_metric_low = metric;
416 }
967e6a64
YW
417 break;
418 default:
419 assert_not_reached();
bdad94d0
YW
420 }
421
422 return 0;
423}
424
6f812d28
YW
425int config_parse_ipv6_accept_ra_route_metric(
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
450 r = extract_many_words(&p, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &high, &medium, &low, NULL);
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
475 network->ipv6_accept_ra_route_metric_high = metric_high;
476 network->ipv6_accept_ra_route_metric_medium = metric_medium;
477 network->ipv6_accept_ra_route_metric_low = metric_low;
478 network->ipv6_accept_ra_route_metric_set = true;
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
4f7331a8
YW
635int config_parse_dhcp_use_ntp(
636 const char* unit,
637 const char *filename,
638 unsigned line,
639 const char *section,
640 unsigned section_line,
641 const char *lvalue,
642 int ltype,
643 const char *rvalue,
644 void *data,
645 void *userdata) {
646
5b7f0aaf 647 Network *network = userdata;
4f7331a8
YW
648 int r;
649
650 assert(filename);
651 assert(lvalue);
967e6a64 652 assert(IN_SET(ltype, AF_UNSPEC, AF_INET, AF_INET6));
4f7331a8
YW
653 assert(rvalue);
654 assert(data);
655
656 r = parse_boolean(rvalue);
657 if (r < 0) {
d96edb2c 658 log_syntax(unit, LOG_WARNING, filename, line, r,
4f7331a8
YW
659 "Failed to parse UseNTP=%s, ignoring assignment: %m", rvalue);
660 return 0;
661 }
662
89346ac6 663 switch (ltype) {
967e6a64 664 case AF_INET:
bdad94d0
YW
665 network->dhcp_use_ntp = r;
666 network->dhcp_use_ntp_set = true;
967e6a64
YW
667 break;
668 case AF_INET6:
bdad94d0
YW
669 network->dhcp6_use_ntp = r;
670 network->dhcp6_use_ntp_set = true;
967e6a64
YW
671 break;
672 case AF_UNSPEC:
673 /* For backward compatibility. */
bdad94d0
YW
674 if (!network->dhcp_use_ntp_set)
675 network->dhcp_use_ntp = r;
676 if (!network->dhcp6_use_ntp_set)
677 network->dhcp6_use_ntp = r;
967e6a64
YW
678 break;
679 default:
680 assert_not_reached();
bdad94d0 681 }
4f7331a8
YW
682
683 return 0;
684}
685
e47bcb7d 686int config_parse_dhcp_or_ra_route_table(
ca5ad760
YW
687 const char *unit,
688 const char *filename,
689 unsigned line,
690 const char *section,
691 unsigned section_line,
692 const char *lvalue,
693 int ltype,
694 const char *rvalue,
695 void *data,
696 void *userdata) {
697
99534007 698 Network *network = ASSERT_PTR(userdata);
ca5ad760
YW
699 uint32_t rt;
700 int r;
701
702 assert(filename);
703 assert(lvalue);
2d00239c 704 assert(IN_SET(ltype, AF_INET, AF_INET6));
ca5ad760 705 assert(rvalue);
ca5ad760 706
7db98bc9 707 r = manager_get_route_table_from_string(network->manager, rvalue, &rt);
ca5ad760 708 if (r < 0) {
d96edb2c 709 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
710 "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue);
711 return 0;
712 }
713
89346ac6 714 switch (ltype) {
2d00239c 715 case AF_INET:
ca5ad760
YW
716 network->dhcp_route_table = rt;
717 network->dhcp_route_table_set = true;
e47bcb7d 718 break;
2d00239c 719 case AF_INET6:
ca5ad760
YW
720 network->ipv6_accept_ra_route_table = rt;
721 network->ipv6_accept_ra_route_table_set = true;
e47bcb7d
YW
722 break;
723 default:
724 assert_not_reached();
ca5ad760
YW
725 }
726
727 return 0;
728}
729
63481576
YW
730int config_parse_iaid(
731 const char *unit,
732 const char *filename,
733 unsigned line,
734 const char *section,
735 unsigned section_line,
736 const char *lvalue,
737 int ltype,
738 const char *rvalue,
739 void *data,
740 void *userdata) {
4e26a5ba 741
99534007 742 Network *network = ASSERT_PTR(userdata);
ca5ad760
YW
743 uint32_t iaid;
744 int r;
745
746 assert(filename);
747 assert(lvalue);
748 assert(rvalue);
4e26a5ba 749 assert(IN_SET(ltype, AF_INET, AF_INET6));
ca5ad760
YW
750
751 r = safe_atou32(rvalue, &iaid);
752 if (r < 0) {
d96edb2c 753 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
754 "Unable to read IAID, ignoring assignment: %s", rvalue);
755 return 0;
756 }
757
4e26a5ba
YW
758 if (ltype == AF_INET) {
759 network->dhcp_iaid = iaid;
760 network->dhcp_iaid_set = true;
761 if (!network->dhcp6_iaid_set_explicitly) {
87160186
YW
762 /* Backward compatibility. Previously, IAID is shared by DHCPv4 and DHCPv6.
763 * If DHCPv6 IAID is not specified explicitly, then use DHCPv4 IAID for DHCPv6. */
4e26a5ba
YW
764 network->dhcp6_iaid = iaid;
765 network->dhcp6_iaid_set = true;
766 }
767 } else {
768 assert(ltype == AF_INET6);
769 network->dhcp6_iaid = iaid;
770 network->dhcp6_iaid_set = true;
771 network->dhcp6_iaid_set_explicitly = true;
772 }
ca5ad760
YW
773
774 return 0;
775}
776
6fda02e1 777int config_parse_dhcp_user_or_vendor_class(
3175a8c2
SS
778 const char *unit,
779 const char *filename,
780 unsigned line,
781 const char *section,
782 unsigned section_line,
783 const char *lvalue,
784 int ltype,
785 const char *rvalue,
786 void *data,
787 void *userdata) {
788
99534007 789 char ***l = ASSERT_PTR(data);
f37f2a6b
SS
790 int r;
791
f37f2a6b
SS
792 assert(lvalue);
793 assert(rvalue);
6fda02e1 794 assert(IN_SET(ltype, AF_INET, AF_INET6));
f37f2a6b
SS
795
796 if (isempty(rvalue)) {
797 *l = strv_free(*l);
798 return 0;
799 }
800
d96edb2c 801 for (const char *p = rvalue;;) {
f37f2a6b 802 _cleanup_free_ char *w = NULL;
b79951fa 803 size_t len;
f37f2a6b 804
d96edb2c 805 r = extract_first_word(&p, &w, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
f37f2a6b
SS
806 if (r == -ENOMEM)
807 return log_oom();
808 if (r < 0) {
d96edb2c 809 log_syntax(unit, LOG_WARNING, filename, line, r,
f37f2a6b 810 "Failed to split user classes option, ignoring: %s", rvalue);
d96edb2c 811 return 0;
f37f2a6b
SS
812 }
813 if (r == 0)
d96edb2c 814 return 0;
f37f2a6b 815
b79951fa 816 len = strlen(w);
f37f2a6b 817 if (ltype == AF_INET) {
b79951fa 818 if (len > UINT8_MAX || len == 0) {
d96edb2c 819 log_syntax(unit, LOG_WARNING, filename, line, 0,
1d3a473b 820 "%s length is not in the range 1…255, ignoring.", w);
f37f2a6b
SS
821 continue;
822 }
823 } else {
b79951fa 824 if (len > UINT16_MAX || len == 0) {
d96edb2c 825 log_syntax(unit, LOG_WARNING, filename, line, 0,
1d3a473b 826 "%s length is not in the range 1…65535, ignoring.", w);
f37f2a6b
SS
827 continue;
828 }
829 }
830
b79951fa 831 r = strv_consume(l, TAKE_PTR(w));
f37f2a6b
SS
832 if (r < 0)
833 return log_oom();
f37f2a6b 834 }
f37f2a6b
SS
835}
836
0e96961d
YW
837int config_parse_dhcp_send_option(
838 const char *unit,
839 const char *filename,
840 unsigned line,
841 const char *section,
842 unsigned section_line,
843 const char *lvalue,
844 int ltype,
845 const char *rvalue,
846 void *data,
847 void *userdata) {
848
d7ac0952
FS
849 _cleanup_(sd_dhcp_option_unrefp) sd_dhcp_option *opt4 = NULL;
850 _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *opt6 = NULL;
851 _unused_ _cleanup_(sd_dhcp_option_unrefp) sd_dhcp_option *old4 = NULL;
852 _unused_ _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *old6 = NULL;
b4ccc5de 853 uint32_t uint32_data, enterprise_identifier = 0;
0e96961d 854 _cleanup_free_ char *word = NULL, *q = NULL;
99534007 855 OrderedHashmap **options = ASSERT_PTR(data);
b4ccc5de 856 uint16_t u16, uint16_data;
0e96961d
YW
857 union in_addr_union addr;
858 DHCPOptionDataType type;
e7d5fe17 859 uint8_t u8, uint8_data;
0e96961d
YW
860 const void *udata;
861 const char *p;
862 ssize_t sz;
863 int r;
864
865 assert(filename);
866 assert(lvalue);
867 assert(rvalue);
0e96961d
YW
868
869 if (isempty(rvalue)) {
870 *options = ordered_hashmap_free(*options);
871 return 0;
872 }
873
874 p = rvalue;
b4ccc5de
SS
875 if (ltype == AF_INET6 && streq(lvalue, "SendVendorOption")) {
876 r = extract_first_word(&p, &word, ":", 0);
877 if (r == -ENOMEM)
878 return log_oom();
879 if (r <= 0 || isempty(p)) {
d96edb2c 880 log_syntax(unit, LOG_WARNING, filename, line, r,
b4ccc5de
SS
881 "Invalid DHCP option, ignoring assignment: %s", rvalue);
882 return 0;
883 }
884
885 r = safe_atou32(word, &enterprise_identifier);
886 if (r < 0) {
d96edb2c 887 log_syntax(unit, LOG_WARNING, filename, line, r,
b4ccc5de
SS
888 "Failed to parse DHCPv6 enterprise identifier data, ignoring assignment: %s", p);
889 return 0;
890 }
891 word = mfree(word);
892 }
893
0e96961d
YW
894 r = extract_first_word(&p, &word, ":", 0);
895 if (r == -ENOMEM)
896 return log_oom();
b4ccc5de 897 if (r <= 0 || isempty(p)) {
d96edb2c 898 log_syntax(unit, LOG_WARNING, filename, line, r,
0e96961d
YW
899 "Invalid DHCP option, ignoring assignment: %s", rvalue);
900 return 0;
901 }
902
e7d5fe17
AD
903 if (ltype == AF_INET6) {
904 r = safe_atou16(word, &u16);
905 if (r < 0) {
d96edb2c 906 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17
AD
907 "Invalid DHCP option, ignoring assignment: %s", rvalue);
908 return 0;
909 }
f37f2a6b 910 if (u16 < 1 || u16 >= UINT16_MAX) {
d96edb2c 911 log_syntax(unit, LOG_WARNING, filename, line, 0,
e7d5fe17
AD
912 "Invalid DHCP option, valid range is 1-65535, ignoring assignment: %s", rvalue);
913 return 0;
914 }
915 } else {
916 r = safe_atou8(word, &u8);
917 if (r < 0) {
d96edb2c 918 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17
AD
919 "Invalid DHCP option, ignoring assignment: %s", rvalue);
920 return 0;
921 }
ffed0205 922 if (u8 < 1 || u8 >= UINT8_MAX) {
d96edb2c 923 log_syntax(unit, LOG_WARNING, filename, line, 0,
e7d5fe17
AD
924 "Invalid DHCP option, valid range is 1-254, ignoring assignment: %s", rvalue);
925 return 0;
926 }
0e96961d
YW
927 }
928
3db7d5d2 929 word = mfree(word);
0e96961d
YW
930 r = extract_first_word(&p, &word, ":", 0);
931 if (r == -ENOMEM)
932 return log_oom();
1eb73422 933 if (r <= 0 || isempty(p)) {
d96edb2c 934 log_syntax(unit, LOG_WARNING, filename, line, r,
0e96961d
YW
935 "Invalid DHCP option, ignoring assignment: %s", rvalue);
936 return 0;
937 }
938
939 type = dhcp_option_data_type_from_string(word);
940 if (type < 0) {
b98680b2 941 log_syntax(unit, LOG_WARNING, filename, line, type,
0e96961d
YW
942 "Invalid DHCP option data type, ignoring assignment: %s", p);
943 return 0;
944 }
945
89346ac6 946 switch (type) {
0e96961d
YW
947 case DHCP_OPTION_DATA_UINT8:{
948 r = safe_atou8(p, &uint8_data);
949 if (r < 0) {
d96edb2c 950 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17 951 "Failed to parse DHCP uint8 data, ignoring assignment: %s", p);
0e96961d
YW
952 return 0;
953 }
954
955 udata = &uint8_data;
956 sz = sizeof(uint8_t);
957 break;
958 }
959 case DHCP_OPTION_DATA_UINT16:{
176321cb
SS
960 uint16_t k;
961
962 r = safe_atou16(p, &k);
0e96961d 963 if (r < 0) {
d96edb2c 964 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17 965 "Failed to parse DHCP uint16 data, ignoring assignment: %s", p);
0e96961d
YW
966 return 0;
967 }
968
176321cb 969 uint16_data = htobe16(k);
0e96961d
YW
970 udata = &uint16_data;
971 sz = sizeof(uint16_t);
972 break;
973 }
974 case DHCP_OPTION_DATA_UINT32: {
176321cb
SS
975 uint32_t k;
976
977 r = safe_atou32(p, &k);
0e96961d 978 if (r < 0) {
d96edb2c 979 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17 980 "Failed to parse DHCP uint32 data, ignoring assignment: %s", p);
0e96961d
YW
981 return 0;
982 }
983
176321cb 984 uint32_data = htobe32(k);
0e96961d
YW
985 udata = &uint32_data;
986 sz = sizeof(uint32_t);
987
988 break;
989 }
990 case DHCP_OPTION_DATA_IPV4ADDRESS: {
991 r = in_addr_from_string(AF_INET, p, &addr);
992 if (r < 0) {
d96edb2c 993 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17 994 "Failed to parse DHCP ipv4address data, ignoring assignment: %s", p);
0e96961d
YW
995 return 0;
996 }
997
998 udata = &addr.in;
999 sz = sizeof(addr.in.s_addr);
1000 break;
1001 }
e7d5fe17
AD
1002 case DHCP_OPTION_DATA_IPV6ADDRESS: {
1003 r = in_addr_from_string(AF_INET6, p, &addr);
1004 if (r < 0) {
d96edb2c 1005 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17
AD
1006 "Failed to parse DHCP ipv6address data, ignoring assignment: %s", p);
1007 return 0;
1008 }
1009
1010 udata = &addr.in6;
1011 sz = sizeof(addr.in6.s6_addr);
1012 break;
1013 }
0e96961d 1014 case DHCP_OPTION_DATA_STRING:
732e3a61 1015 sz = cunescape(p, UNESCAPE_ACCEPT_NUL, &q);
fbf946ce 1016 if (sz < 0) {
d96edb2c 1017 log_syntax(unit, LOG_WARNING, filename, line, sz,
e7d5fe17 1018 "Failed to decode DHCP option data, ignoring assignment: %s", p);
fbf946ce
YW
1019 return 0;
1020 }
0e96961d
YW
1021
1022 udata = q;
1023 break;
1024 default:
1025 return -EINVAL;
1026 }
1027
e7d5fe17 1028 if (ltype == AF_INET6) {
b4ccc5de 1029 r = sd_dhcp6_option_new(u16, udata, sz, enterprise_identifier, &opt6);
e7d5fe17 1030 if (r < 0) {
d96edb2c 1031 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17
AD
1032 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
1033 return 0;
1034 }
0e96961d 1035
e7d5fe17
AD
1036 r = ordered_hashmap_ensure_allocated(options, &dhcp6_option_hash_ops);
1037 if (r < 0)
1038 return log_oom();
0e96961d 1039
e7d5fe17
AD
1040 /* Overwrite existing option */
1041 old6 = ordered_hashmap_get(*options, UINT_TO_PTR(u16));
1042 r = ordered_hashmap_replace(*options, UINT_TO_PTR(u16), opt6);
1043 if (r < 0) {
d96edb2c 1044 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17
AD
1045 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
1046 return 0;
1047 }
1048 TAKE_PTR(opt6);
1049 } else {
1050 r = sd_dhcp_option_new(u8, udata, sz, &opt4);
1051 if (r < 0) {
d96edb2c 1052 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17
AD
1053 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
1054 return 0;
1055 }
0e96961d 1056
e7d5fe17
AD
1057 r = ordered_hashmap_ensure_allocated(options, &dhcp_option_hash_ops);
1058 if (r < 0)
1059 return log_oom();
1060
1061 /* Overwrite existing option */
1062 old4 = ordered_hashmap_get(*options, UINT_TO_PTR(u8));
1063 r = ordered_hashmap_replace(*options, UINT_TO_PTR(u8), opt4);
1064 if (r < 0) {
d96edb2c 1065 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17
AD
1066 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
1067 return 0;
1068 }
1069 TAKE_PTR(opt4);
1070 }
0e96961d
YW
1071 return 0;
1072}
1073
35f6a5cb
SS
1074int config_parse_dhcp_request_options(
1075 const char *unit,
1076 const char *filename,
1077 unsigned line,
1078 const char *section,
1079 unsigned section_line,
1080 const char *lvalue,
1081 int ltype,
1082 const char *rvalue,
1083 void *data,
1084 void *userdata) {
1085
5b7f0aaf 1086 Network *network = userdata;
35f6a5cb
SS
1087 int r;
1088
1089 assert(filename);
1090 assert(lvalue);
1091 assert(rvalue);
1092 assert(data);
1093
1094 if (isempty(rvalue)) {
1095 if (ltype == AF_INET)
1096 network->dhcp_request_options = set_free(network->dhcp_request_options);
1097 else
1098 network->dhcp6_request_options = set_free(network->dhcp6_request_options);
1099
1100 return 0;
1101 }
1102
cda7fc8d 1103 for (const char *p = rvalue;;) {
35f6a5cb
SS
1104 _cleanup_free_ char *n = NULL;
1105 uint32_t i;
1106
1107 r = extract_first_word(&p, &n, NULL, 0);
d96edb2c
YW
1108 if (r == -ENOMEM)
1109 return log_oom();
35f6a5cb 1110 if (r < 0) {
d96edb2c 1111 log_syntax(unit, LOG_WARNING, filename, line, r,
35f6a5cb
SS
1112 "Failed to parse DHCP request option, ignoring assignment: %s",
1113 rvalue);
1114 return 0;
1115 }
1116 if (r == 0)
1117 return 0;
1118
1119 r = safe_atou32(n, &i);
1120 if (r < 0) {
d96edb2c 1121 log_syntax(unit, LOG_WARNING, filename, line, r,
35f6a5cb
SS
1122 "DHCP request option is invalid, ignoring assignment: %s", n);
1123 continue;
1124 }
1125
ffed0205 1126 if (i < 1 || i >= UINT8_MAX) {
d96edb2c 1127 log_syntax(unit, LOG_WARNING, filename, line, 0,
35f6a5cb
SS
1128 "DHCP request option is invalid, valid range is 1-254, ignoring assignment: %s", n);
1129 continue;
1130 }
1131
de7fef4b
ZJS
1132 r = set_ensure_put(ltype == AF_INET ? &network->dhcp_request_options : &network->dhcp6_request_options,
1133 NULL, UINT32_TO_PTR(i));
35f6a5cb 1134 if (r < 0)
d96edb2c 1135 log_syntax(unit, LOG_WARNING, filename, line, r,
35f6a5cb
SS
1136 "Failed to store DHCP request option '%s', ignoring assignment: %m", n);
1137 }
35f6a5cb
SS
1138}
1139
ca5ad760
YW
1140static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = {
1141 [DHCP_USE_DOMAINS_NO] = "no",
1142 [DHCP_USE_DOMAINS_ROUTE] = "route",
1143 [DHCP_USE_DOMAINS_YES] = "yes",
1144};
1145
1146DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES);
2e5580a8
YW
1147
1148static const char * const dhcp_option_data_type_table[_DHCP_OPTION_DATA_MAX] = {
1149 [DHCP_OPTION_DATA_UINT8] = "uint8",
1150 [DHCP_OPTION_DATA_UINT16] = "uint16",
1151 [DHCP_OPTION_DATA_UINT32] = "uint32",
1152 [DHCP_OPTION_DATA_STRING] = "string",
1153 [DHCP_OPTION_DATA_IPV4ADDRESS] = "ipv4address",
e7d5fe17 1154 [DHCP_OPTION_DATA_IPV6ADDRESS] = "ipv6address",
2e5580a8
YW
1155};
1156
1157DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type, DHCPOptionDataType);
cde74a65
YW
1158
1159static const char* const duid_type_table[_DUID_TYPE_MAX] = {
6ed69be9
YW
1160 [DUID_TYPE_LLT] = "link-layer-time",
1161 [DUID_TYPE_EN] = "vendor",
1162 [DUID_TYPE_LL] = "link-layer",
1163 [DUID_TYPE_UUID] = "uuid",
cde74a65
YW
1164};
1165DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(duid_type, DUIDType);
1166
1167int config_parse_duid_type(
1168 const char *unit,
1169 const char *filename,
1170 unsigned line,
1171 const char *section,
1172 unsigned section_line,
1173 const char *lvalue,
1174 int ltype,
1175 const char *rvalue,
1176 void *data,
1177 void *userdata) {
1178
1179 _cleanup_free_ char *type_string = NULL;
99534007 1180 const char *p = ASSERT_PTR(rvalue);
4e26a5ba 1181 bool force = ltype;
99534007 1182 DUID *duid = ASSERT_PTR(data);
cde74a65
YW
1183 DUIDType type;
1184 int r;
1185
1186 assert(filename);
1187 assert(lvalue);
cde74a65 1188
4e26a5ba
YW
1189 if (!force && duid->set)
1190 return 0;
1191
cde74a65
YW
1192 r = extract_first_word(&p, &type_string, ":", 0);
1193 if (r == -ENOMEM)
1194 return log_oom();
1195 if (r < 0) {
1196 log_syntax(unit, LOG_WARNING, filename, line, r,
1197 "Invalid syntax, ignoring: %s", rvalue);
1198 return 0;
1199 }
1200 if (r == 0) {
1201 log_syntax(unit, LOG_WARNING, filename, line, 0,
1202 "Failed to extract DUID type from '%s', ignoring.", rvalue);
1203 return 0;
1204 }
1205
1206 type = duid_type_from_string(type_string);
1207 if (type < 0) {
80500bb5
YW
1208 uint16_t t;
1209
1210 r = safe_atou16(type_string, &t);
1211 if (r < 0) {
1212 log_syntax(unit, LOG_WARNING, filename, line, r,
1213 "Failed to parse DUID type '%s', ignoring.", type_string);
1214 return 0;
1215 }
1216
1217 type = t;
1218 assert(type == t); /* Check if type can store uint16_t. */
cde74a65
YW
1219 }
1220
1221 if (!isempty(p)) {
1222 usec_t u;
1223
1224 if (type != DUID_TYPE_LLT) {
1225 log_syntax(unit, LOG_WARNING, filename, line, r,
1226 "Invalid syntax, ignoring: %s", rvalue);
1227 return 0;
1228 }
1229
1230 r = parse_timestamp(p, &u);
1231 if (r < 0) {
1232 log_syntax(unit, LOG_WARNING, filename, line, r,
1233 "Failed to parse timestamp, ignoring: %s", p);
1234 return 0;
1235 }
1236
1237 duid->llt_time = u;
1238 }
1239
1240 duid->type = type;
4e26a5ba 1241 duid->set = force;
cde74a65
YW
1242
1243 return 0;
1244}
1245
4e26a5ba
YW
1246int config_parse_manager_duid_type(
1247 const char *unit,
1248 const char *filename,
1249 unsigned line,
1250 const char *section,
1251 unsigned section_line,
1252 const char *lvalue,
1253 int ltype,
1254 const char *rvalue,
1255 void *data,
1256 void *userdata) {
1257
99534007 1258 Manager *manager = ASSERT_PTR(userdata);
4e26a5ba
YW
1259 int r;
1260
87160186 1261 /* For backward compatibility. Setting both DHCPv4 and DHCPv6 DUID if they are not specified explicitly. */
4e26a5ba
YW
1262
1263 r = config_parse_duid_type(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp_duid, manager);
1264 if (r < 0)
1265 return r;
1266
1267 return config_parse_duid_type(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp6_duid, manager);
1268}
1269
1270int config_parse_network_duid_type(
1271 const char *unit,
1272 const char *filename,
1273 unsigned line,
1274 const char *section,
1275 unsigned section_line,
1276 const char *lvalue,
1277 int ltype,
1278 const char *rvalue,
1279 void *data,
1280 void *userdata) {
1281
99534007 1282 Network *network = ASSERT_PTR(userdata);
4e26a5ba
YW
1283 int r;
1284
4e26a5ba
YW
1285 r = config_parse_duid_type(unit, filename, line, section, section_line, lvalue, true, rvalue, &network->dhcp_duid, network);
1286 if (r < 0)
1287 return r;
1288
87160186 1289 /* For backward compatibility, also set DHCPv6 DUID if not specified explicitly. */
4e26a5ba
YW
1290 return config_parse_duid_type(unit, filename, line, section, section_line, lvalue, false, rvalue, &network->dhcp6_duid, network);
1291}
1292
cde74a65
YW
1293int config_parse_duid_rawdata(
1294 const char *unit,
1295 const char *filename,
1296 unsigned line,
1297 const char *section,
1298 unsigned section_line,
1299 const char *lvalue,
1300 int ltype,
1301 const char *rvalue,
1302 void *data,
1303 void *userdata) {
1304
92914960 1305 uint8_t raw_data[MAX_DUID_DATA_LEN];
cde74a65 1306 unsigned count = 0;
4e26a5ba 1307 bool force = ltype;
99534007 1308 DUID *duid = ASSERT_PTR(data);
cde74a65
YW
1309
1310 assert(filename);
1311 assert(lvalue);
1312 assert(rvalue);
4e26a5ba
YW
1313
1314 if (!force && duid->set)
1315 return 0;
cde74a65
YW
1316
1317 /* RawData contains DUID in format "NN:NN:NN..." */
1318 for (const char *p = rvalue;;) {
1319 int n1, n2, len, r;
1320 uint32_t byte;
1321 _cleanup_free_ char *cbyte = NULL;
1322
1323 r = extract_first_word(&p, &cbyte, ":", 0);
1324 if (r == -ENOMEM)
1325 return log_oom();
1326 if (r < 0) {
1327 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to read DUID, ignoring assignment: %s.", rvalue);
1328 return 0;
1329 }
1330 if (r == 0)
1331 break;
1332
92914960 1333 if (count >= MAX_DUID_DATA_LEN) {
cde74a65
YW
1334 log_syntax(unit, LOG_WARNING, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue);
1335 return 0;
1336 }
1337
1338 len = strlen(cbyte);
1339 if (!IN_SET(len, 1, 2)) {
1340 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue);
1341 return 0;
1342 }
1343 n1 = unhexchar(cbyte[0]);
1344 if (len == 2)
1345 n2 = unhexchar(cbyte[1]);
1346 else
1347 n2 = 0;
1348
1349 if (n1 < 0 || n2 < 0) {
1350 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue);
1351 return 0;
1352 }
1353
1354 byte = ((uint8_t) n1 << (4 * (len-1))) | (uint8_t) n2;
1355 raw_data[count++] = byte;
1356 }
1357
4e26a5ba
YW
1358 assert_cc(sizeof(raw_data) == sizeof(duid->raw_data));
1359 memcpy(duid->raw_data, raw_data, count);
1360 duid->raw_data_len = count;
1361 duid->set = force;
1362
cde74a65
YW
1363 return 0;
1364}
4e26a5ba
YW
1365
1366int config_parse_manager_duid_rawdata(
1367 const char *unit,
1368 const char *filename,
1369 unsigned line,
1370 const char *section,
1371 unsigned section_line,
1372 const char *lvalue,
1373 int ltype,
1374 const char *rvalue,
1375 void *data,
1376 void *userdata) {
1377
99534007 1378 Manager *manager = ASSERT_PTR(userdata);
4e26a5ba
YW
1379 int r;
1380
87160186 1381 /* For backward compatibility. Setting both DHCPv4 and DHCPv6 DUID if they are not specified explicitly. */
4e26a5ba
YW
1382
1383 r = config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp_duid, manager);
1384 if (r < 0)
1385 return r;
1386
1387 return config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp6_duid, manager);
1388}
1389
1390int config_parse_network_duid_rawdata(
1391 const char *unit,
1392 const char *filename,
1393 unsigned line,
1394 const char *section,
1395 unsigned section_line,
1396 const char *lvalue,
1397 int ltype,
1398 const char *rvalue,
1399 void *data,
1400 void *userdata) {
1401
99534007 1402 Network *network = ASSERT_PTR(userdata);
4e26a5ba
YW
1403 int r;
1404
4e26a5ba
YW
1405 r = config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, true, rvalue, &network->dhcp_duid, network);
1406 if (r < 0)
1407 return r;
1408
87160186 1409 /* For backward compatibility, also set DHCPv6 DUID if not specified explicitly. */
4e26a5ba
YW
1410 return config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, false, rvalue, &network->dhcp6_duid, network);
1411}
2cba2146
YW
1412
1413int config_parse_uplink(
1414 const char *unit,
1415 const char *filename,
1416 unsigned line,
1417 const char *section,
1418 unsigned section_line,
1419 const char *lvalue,
1420 int ltype,
1421 const char *rvalue,
1422 void *data,
1423 void *userdata) {
1424
99534007 1425 Network *network = ASSERT_PTR(userdata);
dc5cae6c 1426 bool accept_none = true;
2cba2146
YW
1427 int *index, r;
1428 char **name;
1429
1430 assert(filename);
1431 assert(section);
1432 assert(lvalue);
1433 assert(rvalue);
1434
1435 if (streq(section, "DHCPServer")) {
1436 index = &network->dhcp_server_uplink_index;
1437 name = &network->dhcp_server_uplink_name;
1438 } else if (streq(section, "IPv6SendRA")) {
1439 index = &network->router_uplink_index;
1440 name = &network->router_uplink_name;
a27588d4
YW
1441 } else if (STR_IN_SET(section, "DHCPv6PrefixDelegation", "DHCPPrefixDelegation")) {
1442 index = &network->dhcp_pd_uplink_index;
1443 name = &network->dhcp_pd_uplink_name;
dc5cae6c 1444 accept_none = false;
2cba2146
YW
1445 } else
1446 assert_not_reached();
1447
1448 if (isempty(rvalue) || streq(rvalue, ":auto")) {
1449 *index = UPLINK_INDEX_AUTO;
1450 *name = mfree(*name);
1451 return 0;
1452 }
1453
dc5cae6c 1454 if (accept_none && streq(rvalue, ":none")) {
2cba2146
YW
1455 *index = UPLINK_INDEX_NONE;
1456 *name = mfree(*name);
1457 return 0;
1458 }
1459
dc5cae6c
YW
1460 if (!accept_none && streq(rvalue, ":self")) {
1461 *index = UPLINK_INDEX_SELF;
1462 *name = mfree(*name);
1463 return 0;
1464 }
1465
2cba2146
YW
1466 r = parse_ifindex(rvalue);
1467 if (r > 0) {
1468 *index = r;
1469 *name = mfree(*name);
1470 return 0;
1471 }
1472
1473 if (!ifname_valid_full(rvalue, IFNAME_VALID_ALTERNATIVE)) {
1474 log_syntax(unit, LOG_WARNING, filename, line, 0,
1475 "Invalid interface name in %s=, ignoring assignment: %s", lvalue, rvalue);
1476 return 0;
1477 }
1478
1479 /* The interface name will be resolved later. */
1480 r = free_and_strdup_warn(name, rvalue);
1481 if (r < 0)
1482 return r;
1483
1484 /* Note, if uplink_name is set, then uplink_index will be ignored. So, the below does not mean
1485 * an uplink interface will be selected automatically. */
1486 *index = UPLINK_INDEX_AUTO;
1487 return 0;
1488}