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