]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-dhcp-common.c
Merge pull request #32684 from YHNdnzj/pr-followups
[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}
f225a338 533
e47bcb7d 534int config_parse_dhcp_or_ra_route_table(
ca5ad760
YW
535 const char *unit,
536 const char *filename,
537 unsigned line,
538 const char *section,
539 unsigned section_line,
540 const char *lvalue,
541 int ltype,
542 const char *rvalue,
543 void *data,
544 void *userdata) {
545
99534007 546 Network *network = ASSERT_PTR(userdata);
ca5ad760
YW
547 uint32_t rt;
548 int r;
549
550 assert(filename);
551 assert(lvalue);
2d00239c 552 assert(IN_SET(ltype, AF_INET, AF_INET6));
ca5ad760 553 assert(rvalue);
ca5ad760 554
7db98bc9 555 r = manager_get_route_table_from_string(network->manager, rvalue, &rt);
ca5ad760 556 if (r < 0) {
d96edb2c 557 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
558 "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue);
559 return 0;
560 }
561
89346ac6 562 switch (ltype) {
2d00239c 563 case AF_INET:
ca5ad760
YW
564 network->dhcp_route_table = rt;
565 network->dhcp_route_table_set = true;
e47bcb7d 566 break;
2d00239c 567 case AF_INET6:
52672db3
YW
568 network->ndisc_route_table = rt;
569 network->ndisc_route_table_set = true;
e47bcb7d
YW
570 break;
571 default:
572 assert_not_reached();
ca5ad760
YW
573 }
574
575 return 0;
576}
577
63481576
YW
578int config_parse_iaid(
579 const char *unit,
580 const char *filename,
581 unsigned line,
582 const char *section,
583 unsigned section_line,
584 const char *lvalue,
585 int ltype,
586 const char *rvalue,
587 void *data,
588 void *userdata) {
4e26a5ba 589
99534007 590 Network *network = ASSERT_PTR(userdata);
ca5ad760
YW
591 uint32_t iaid;
592 int r;
593
594 assert(filename);
595 assert(lvalue);
596 assert(rvalue);
4e26a5ba 597 assert(IN_SET(ltype, AF_INET, AF_INET6));
ca5ad760
YW
598
599 r = safe_atou32(rvalue, &iaid);
600 if (r < 0) {
d96edb2c 601 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
602 "Unable to read IAID, ignoring assignment: %s", rvalue);
603 return 0;
604 }
605
4e26a5ba
YW
606 if (ltype == AF_INET) {
607 network->dhcp_iaid = iaid;
608 network->dhcp_iaid_set = true;
609 if (!network->dhcp6_iaid_set_explicitly) {
87160186
YW
610 /* Backward compatibility. Previously, IAID is shared by DHCPv4 and DHCPv6.
611 * If DHCPv6 IAID is not specified explicitly, then use DHCPv4 IAID for DHCPv6. */
4e26a5ba
YW
612 network->dhcp6_iaid = iaid;
613 network->dhcp6_iaid_set = true;
614 }
615 } else {
616 assert(ltype == AF_INET6);
617 network->dhcp6_iaid = iaid;
618 network->dhcp6_iaid_set = true;
619 network->dhcp6_iaid_set_explicitly = true;
620 }
ca5ad760
YW
621
622 return 0;
623}
624
6fda02e1 625int config_parse_dhcp_user_or_vendor_class(
3175a8c2
SS
626 const char *unit,
627 const char *filename,
628 unsigned line,
629 const char *section,
630 unsigned section_line,
631 const char *lvalue,
632 int ltype,
633 const char *rvalue,
634 void *data,
635 void *userdata) {
636
99534007 637 char ***l = ASSERT_PTR(data);
f37f2a6b
SS
638 int r;
639
f37f2a6b
SS
640 assert(lvalue);
641 assert(rvalue);
6fda02e1 642 assert(IN_SET(ltype, AF_INET, AF_INET6));
f37f2a6b
SS
643
644 if (isempty(rvalue)) {
645 *l = strv_free(*l);
646 return 0;
647 }
648
d96edb2c 649 for (const char *p = rvalue;;) {
f37f2a6b 650 _cleanup_free_ char *w = NULL;
b79951fa 651 size_t len;
f37f2a6b 652
d96edb2c 653 r = extract_first_word(&p, &w, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
f37f2a6b
SS
654 if (r == -ENOMEM)
655 return log_oom();
656 if (r < 0) {
d96edb2c 657 log_syntax(unit, LOG_WARNING, filename, line, r,
f37f2a6b 658 "Failed to split user classes option, ignoring: %s", rvalue);
d96edb2c 659 return 0;
f37f2a6b
SS
660 }
661 if (r == 0)
d96edb2c 662 return 0;
f37f2a6b 663
b79951fa 664 len = strlen(w);
f37f2a6b 665 if (ltype == AF_INET) {
b79951fa 666 if (len > UINT8_MAX || len == 0) {
d96edb2c 667 log_syntax(unit, LOG_WARNING, filename, line, 0,
1d3a473b 668 "%s length is not in the range 1…255, ignoring.", w);
f37f2a6b
SS
669 continue;
670 }
671 } else {
b79951fa 672 if (len > UINT16_MAX || len == 0) {
d96edb2c 673 log_syntax(unit, LOG_WARNING, filename, line, 0,
1d3a473b 674 "%s length is not in the range 1…65535, ignoring.", w);
f37f2a6b
SS
675 continue;
676 }
677 }
678
b79951fa 679 r = strv_consume(l, TAKE_PTR(w));
f37f2a6b
SS
680 if (r < 0)
681 return log_oom();
f37f2a6b 682 }
f37f2a6b
SS
683}
684
0e96961d
YW
685int config_parse_dhcp_send_option(
686 const char *unit,
687 const char *filename,
688 unsigned line,
689 const char *section,
690 unsigned section_line,
691 const char *lvalue,
692 int ltype,
693 const char *rvalue,
694 void *data,
695 void *userdata) {
696
d7ac0952
FS
697 _cleanup_(sd_dhcp_option_unrefp) sd_dhcp_option *opt4 = NULL;
698 _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *opt6 = NULL;
699 _unused_ _cleanup_(sd_dhcp_option_unrefp) sd_dhcp_option *old4 = NULL;
700 _unused_ _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *old6 = NULL;
b4ccc5de 701 uint32_t uint32_data, enterprise_identifier = 0;
0e96961d 702 _cleanup_free_ char *word = NULL, *q = NULL;
99534007 703 OrderedHashmap **options = ASSERT_PTR(data);
b4ccc5de 704 uint16_t u16, uint16_data;
0e96961d
YW
705 union in_addr_union addr;
706 DHCPOptionDataType type;
e7d5fe17 707 uint8_t u8, uint8_data;
0e96961d
YW
708 const void *udata;
709 const char *p;
710 ssize_t sz;
711 int r;
712
713 assert(filename);
714 assert(lvalue);
715 assert(rvalue);
0e96961d
YW
716
717 if (isempty(rvalue)) {
718 *options = ordered_hashmap_free(*options);
719 return 0;
720 }
721
722 p = rvalue;
b4ccc5de
SS
723 if (ltype == AF_INET6 && streq(lvalue, "SendVendorOption")) {
724 r = extract_first_word(&p, &word, ":", 0);
725 if (r == -ENOMEM)
726 return log_oom();
727 if (r <= 0 || isempty(p)) {
d96edb2c 728 log_syntax(unit, LOG_WARNING, filename, line, r,
b4ccc5de
SS
729 "Invalid DHCP option, ignoring assignment: %s", rvalue);
730 return 0;
731 }
732
733 r = safe_atou32(word, &enterprise_identifier);
734 if (r < 0) {
d96edb2c 735 log_syntax(unit, LOG_WARNING, filename, line, r,
b4ccc5de
SS
736 "Failed to parse DHCPv6 enterprise identifier data, ignoring assignment: %s", p);
737 return 0;
738 }
739 word = mfree(word);
740 }
741
0e96961d
YW
742 r = extract_first_word(&p, &word, ":", 0);
743 if (r == -ENOMEM)
744 return log_oom();
b4ccc5de 745 if (r <= 0 || isempty(p)) {
d96edb2c 746 log_syntax(unit, LOG_WARNING, filename, line, r,
0e96961d
YW
747 "Invalid DHCP option, ignoring assignment: %s", rvalue);
748 return 0;
749 }
750
e7d5fe17
AD
751 if (ltype == AF_INET6) {
752 r = safe_atou16(word, &u16);
753 if (r < 0) {
d96edb2c 754 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17
AD
755 "Invalid DHCP option, ignoring assignment: %s", rvalue);
756 return 0;
757 }
f37f2a6b 758 if (u16 < 1 || u16 >= UINT16_MAX) {
d96edb2c 759 log_syntax(unit, LOG_WARNING, filename, line, 0,
e7d5fe17
AD
760 "Invalid DHCP option, valid range is 1-65535, ignoring assignment: %s", rvalue);
761 return 0;
762 }
763 } else {
764 r = safe_atou8(word, &u8);
765 if (r < 0) {
d96edb2c 766 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17
AD
767 "Invalid DHCP option, ignoring assignment: %s", rvalue);
768 return 0;
769 }
ffed0205 770 if (u8 < 1 || u8 >= UINT8_MAX) {
d96edb2c 771 log_syntax(unit, LOG_WARNING, filename, line, 0,
e7d5fe17
AD
772 "Invalid DHCP option, valid range is 1-254, ignoring assignment: %s", rvalue);
773 return 0;
774 }
0e96961d
YW
775 }
776
3db7d5d2 777 word = mfree(word);
0e96961d
YW
778 r = extract_first_word(&p, &word, ":", 0);
779 if (r == -ENOMEM)
780 return log_oom();
1eb73422 781 if (r <= 0 || isempty(p)) {
d96edb2c 782 log_syntax(unit, LOG_WARNING, filename, line, r,
0e96961d
YW
783 "Invalid DHCP option, ignoring assignment: %s", rvalue);
784 return 0;
785 }
786
787 type = dhcp_option_data_type_from_string(word);
788 if (type < 0) {
b98680b2 789 log_syntax(unit, LOG_WARNING, filename, line, type,
0e96961d
YW
790 "Invalid DHCP option data type, ignoring assignment: %s", p);
791 return 0;
792 }
793
89346ac6 794 switch (type) {
0e96961d
YW
795 case DHCP_OPTION_DATA_UINT8:{
796 r = safe_atou8(p, &uint8_data);
797 if (r < 0) {
d96edb2c 798 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17 799 "Failed to parse DHCP uint8 data, ignoring assignment: %s", p);
0e96961d
YW
800 return 0;
801 }
802
803 udata = &uint8_data;
804 sz = sizeof(uint8_t);
805 break;
806 }
807 case DHCP_OPTION_DATA_UINT16:{
176321cb
SS
808 uint16_t k;
809
810 r = safe_atou16(p, &k);
0e96961d 811 if (r < 0) {
d96edb2c 812 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17 813 "Failed to parse DHCP uint16 data, ignoring assignment: %s", p);
0e96961d
YW
814 return 0;
815 }
816
176321cb 817 uint16_data = htobe16(k);
0e96961d
YW
818 udata = &uint16_data;
819 sz = sizeof(uint16_t);
820 break;
821 }
822 case DHCP_OPTION_DATA_UINT32: {
176321cb
SS
823 uint32_t k;
824
825 r = safe_atou32(p, &k);
0e96961d 826 if (r < 0) {
d96edb2c 827 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17 828 "Failed to parse DHCP uint32 data, ignoring assignment: %s", p);
0e96961d
YW
829 return 0;
830 }
831
176321cb 832 uint32_data = htobe32(k);
0e96961d
YW
833 udata = &uint32_data;
834 sz = sizeof(uint32_t);
835
836 break;
837 }
838 case DHCP_OPTION_DATA_IPV4ADDRESS: {
839 r = in_addr_from_string(AF_INET, p, &addr);
840 if (r < 0) {
d96edb2c 841 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17 842 "Failed to parse DHCP ipv4address data, ignoring assignment: %s", p);
0e96961d
YW
843 return 0;
844 }
845
846 udata = &addr.in;
847 sz = sizeof(addr.in.s_addr);
848 break;
849 }
e7d5fe17
AD
850 case DHCP_OPTION_DATA_IPV6ADDRESS: {
851 r = in_addr_from_string(AF_INET6, p, &addr);
852 if (r < 0) {
d96edb2c 853 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17
AD
854 "Failed to parse DHCP ipv6address data, ignoring assignment: %s", p);
855 return 0;
856 }
857
858 udata = &addr.in6;
859 sz = sizeof(addr.in6.s6_addr);
860 break;
861 }
0e96961d 862 case DHCP_OPTION_DATA_STRING:
732e3a61 863 sz = cunescape(p, UNESCAPE_ACCEPT_NUL, &q);
fbf946ce 864 if (sz < 0) {
d96edb2c 865 log_syntax(unit, LOG_WARNING, filename, line, sz,
e7d5fe17 866 "Failed to decode DHCP option data, ignoring assignment: %s", p);
fbf946ce
YW
867 return 0;
868 }
0e96961d
YW
869
870 udata = q;
871 break;
872 default:
873 return -EINVAL;
874 }
875
e7d5fe17 876 if (ltype == AF_INET6) {
b4ccc5de 877 r = sd_dhcp6_option_new(u16, udata, sz, enterprise_identifier, &opt6);
e7d5fe17 878 if (r < 0) {
d96edb2c 879 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17
AD
880 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
881 return 0;
882 }
0e96961d 883
e7d5fe17
AD
884 r = ordered_hashmap_ensure_allocated(options, &dhcp6_option_hash_ops);
885 if (r < 0)
886 return log_oom();
0e96961d 887
e7d5fe17
AD
888 /* Overwrite existing option */
889 old6 = ordered_hashmap_get(*options, UINT_TO_PTR(u16));
890 r = ordered_hashmap_replace(*options, UINT_TO_PTR(u16), opt6);
891 if (r < 0) {
d96edb2c 892 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17
AD
893 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
894 return 0;
895 }
896 TAKE_PTR(opt6);
897 } else {
898 r = sd_dhcp_option_new(u8, udata, sz, &opt4);
899 if (r < 0) {
d96edb2c 900 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17
AD
901 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
902 return 0;
903 }
0e96961d 904
e7d5fe17
AD
905 r = ordered_hashmap_ensure_allocated(options, &dhcp_option_hash_ops);
906 if (r < 0)
907 return log_oom();
908
909 /* Overwrite existing option */
910 old4 = ordered_hashmap_get(*options, UINT_TO_PTR(u8));
911 r = ordered_hashmap_replace(*options, UINT_TO_PTR(u8), opt4);
912 if (r < 0) {
d96edb2c 913 log_syntax(unit, LOG_WARNING, filename, line, r,
e7d5fe17
AD
914 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
915 return 0;
916 }
917 TAKE_PTR(opt4);
918 }
0e96961d
YW
919 return 0;
920}
921
35f6a5cb
SS
922int config_parse_dhcp_request_options(
923 const char *unit,
924 const char *filename,
925 unsigned line,
926 const char *section,
927 unsigned section_line,
928 const char *lvalue,
929 int ltype,
930 const char *rvalue,
931 void *data,
932 void *userdata) {
933
5b7f0aaf 934 Network *network = userdata;
35f6a5cb
SS
935 int r;
936
937 assert(filename);
938 assert(lvalue);
939 assert(rvalue);
940 assert(data);
941
942 if (isempty(rvalue)) {
943 if (ltype == AF_INET)
944 network->dhcp_request_options = set_free(network->dhcp_request_options);
945 else
946 network->dhcp6_request_options = set_free(network->dhcp6_request_options);
947
948 return 0;
949 }
950
cda7fc8d 951 for (const char *p = rvalue;;) {
35f6a5cb
SS
952 _cleanup_free_ char *n = NULL;
953 uint32_t i;
954
955 r = extract_first_word(&p, &n, NULL, 0);
d96edb2c
YW
956 if (r == -ENOMEM)
957 return log_oom();
35f6a5cb 958 if (r < 0) {
d96edb2c 959 log_syntax(unit, LOG_WARNING, filename, line, r,
35f6a5cb
SS
960 "Failed to parse DHCP request option, ignoring assignment: %s",
961 rvalue);
962 return 0;
963 }
964 if (r == 0)
965 return 0;
966
967 r = safe_atou32(n, &i);
968 if (r < 0) {
d96edb2c 969 log_syntax(unit, LOG_WARNING, filename, line, r,
35f6a5cb
SS
970 "DHCP request option is invalid, ignoring assignment: %s", n);
971 continue;
972 }
973
ffed0205 974 if (i < 1 || i >= UINT8_MAX) {
d96edb2c 975 log_syntax(unit, LOG_WARNING, filename, line, 0,
35f6a5cb
SS
976 "DHCP request option is invalid, valid range is 1-254, ignoring assignment: %s", n);
977 continue;
978 }
979
de7fef4b
ZJS
980 r = set_ensure_put(ltype == AF_INET ? &network->dhcp_request_options : &network->dhcp6_request_options,
981 NULL, UINT32_TO_PTR(i));
35f6a5cb 982 if (r < 0)
d96edb2c 983 log_syntax(unit, LOG_WARNING, filename, line, r,
35f6a5cb
SS
984 "Failed to store DHCP request option '%s', ignoring assignment: %m", n);
985 }
35f6a5cb
SS
986}
987
2e5580a8
YW
988static const char * const dhcp_option_data_type_table[_DHCP_OPTION_DATA_MAX] = {
989 [DHCP_OPTION_DATA_UINT8] = "uint8",
990 [DHCP_OPTION_DATA_UINT16] = "uint16",
991 [DHCP_OPTION_DATA_UINT32] = "uint32",
992 [DHCP_OPTION_DATA_STRING] = "string",
993 [DHCP_OPTION_DATA_IPV4ADDRESS] = "ipv4address",
e7d5fe17 994 [DHCP_OPTION_DATA_IPV6ADDRESS] = "ipv6address",
2e5580a8
YW
995};
996
997DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type, DHCPOptionDataType);
cde74a65
YW
998
999static const char* const duid_type_table[_DUID_TYPE_MAX] = {
6ed69be9
YW
1000 [DUID_TYPE_LLT] = "link-layer-time",
1001 [DUID_TYPE_EN] = "vendor",
1002 [DUID_TYPE_LL] = "link-layer",
1003 [DUID_TYPE_UUID] = "uuid",
cde74a65
YW
1004};
1005DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(duid_type, DUIDType);
1006
1007int config_parse_duid_type(
1008 const char *unit,
1009 const char *filename,
1010 unsigned line,
1011 const char *section,
1012 unsigned section_line,
1013 const char *lvalue,
1014 int ltype,
1015 const char *rvalue,
1016 void *data,
1017 void *userdata) {
1018
1019 _cleanup_free_ char *type_string = NULL;
99534007 1020 const char *p = ASSERT_PTR(rvalue);
4e26a5ba 1021 bool force = ltype;
99534007 1022 DUID *duid = ASSERT_PTR(data);
cde74a65
YW
1023 DUIDType type;
1024 int r;
1025
1026 assert(filename);
1027 assert(lvalue);
cde74a65 1028
4e26a5ba
YW
1029 if (!force && duid->set)
1030 return 0;
1031
cde74a65
YW
1032 r = extract_first_word(&p, &type_string, ":", 0);
1033 if (r == -ENOMEM)
1034 return log_oom();
1035 if (r < 0) {
1036 log_syntax(unit, LOG_WARNING, filename, line, r,
1037 "Invalid syntax, ignoring: %s", rvalue);
1038 return 0;
1039 }
1040 if (r == 0) {
1041 log_syntax(unit, LOG_WARNING, filename, line, 0,
1042 "Failed to extract DUID type from '%s', ignoring.", rvalue);
1043 return 0;
1044 }
1045
1046 type = duid_type_from_string(type_string);
1047 if (type < 0) {
80500bb5
YW
1048 uint16_t t;
1049
1050 r = safe_atou16(type_string, &t);
1051 if (r < 0) {
1052 log_syntax(unit, LOG_WARNING, filename, line, r,
1053 "Failed to parse DUID type '%s', ignoring.", type_string);
1054 return 0;
1055 }
1056
1057 type = t;
1058 assert(type == t); /* Check if type can store uint16_t. */
cde74a65
YW
1059 }
1060
1061 if (!isempty(p)) {
1062 usec_t u;
1063
1064 if (type != DUID_TYPE_LLT) {
1065 log_syntax(unit, LOG_WARNING, filename, line, r,
1066 "Invalid syntax, ignoring: %s", rvalue);
1067 return 0;
1068 }
1069
1070 r = parse_timestamp(p, &u);
1071 if (r < 0) {
1072 log_syntax(unit, LOG_WARNING, filename, line, r,
1073 "Failed to parse timestamp, ignoring: %s", p);
1074 return 0;
1075 }
1076
1077 duid->llt_time = u;
1078 }
1079
1080 duid->type = type;
4e26a5ba 1081 duid->set = force;
cde74a65
YW
1082
1083 return 0;
1084}
1085
4e26a5ba
YW
1086int config_parse_manager_duid_type(
1087 const char *unit,
1088 const char *filename,
1089 unsigned line,
1090 const char *section,
1091 unsigned section_line,
1092 const char *lvalue,
1093 int ltype,
1094 const char *rvalue,
1095 void *data,
1096 void *userdata) {
1097
99534007 1098 Manager *manager = ASSERT_PTR(userdata);
4e26a5ba
YW
1099 int r;
1100
87160186 1101 /* For backward compatibility. Setting both DHCPv4 and DHCPv6 DUID if they are not specified explicitly. */
4e26a5ba
YW
1102
1103 r = config_parse_duid_type(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp_duid, manager);
1104 if (r < 0)
1105 return r;
1106
1107 return config_parse_duid_type(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp6_duid, manager);
1108}
1109
1110int config_parse_network_duid_type(
1111 const char *unit,
1112 const char *filename,
1113 unsigned line,
1114 const char *section,
1115 unsigned section_line,
1116 const char *lvalue,
1117 int ltype,
1118 const char *rvalue,
1119 void *data,
1120 void *userdata) {
1121
99534007 1122 Network *network = ASSERT_PTR(userdata);
4e26a5ba
YW
1123 int r;
1124
4e26a5ba
YW
1125 r = config_parse_duid_type(unit, filename, line, section, section_line, lvalue, true, rvalue, &network->dhcp_duid, network);
1126 if (r < 0)
1127 return r;
1128
87160186 1129 /* For backward compatibility, also set DHCPv6 DUID if not specified explicitly. */
4e26a5ba
YW
1130 return config_parse_duid_type(unit, filename, line, section, section_line, lvalue, false, rvalue, &network->dhcp6_duid, network);
1131}
1132
cde74a65
YW
1133int config_parse_duid_rawdata(
1134 const char *unit,
1135 const char *filename,
1136 unsigned line,
1137 const char *section,
1138 unsigned section_line,
1139 const char *lvalue,
1140 int ltype,
1141 const char *rvalue,
1142 void *data,
1143 void *userdata) {
1144
92914960 1145 uint8_t raw_data[MAX_DUID_DATA_LEN];
cde74a65 1146 unsigned count = 0;
4e26a5ba 1147 bool force = ltype;
99534007 1148 DUID *duid = ASSERT_PTR(data);
cde74a65
YW
1149
1150 assert(filename);
1151 assert(lvalue);
1152 assert(rvalue);
4e26a5ba
YW
1153
1154 if (!force && duid->set)
1155 return 0;
cde74a65
YW
1156
1157 /* RawData contains DUID in format "NN:NN:NN..." */
1158 for (const char *p = rvalue;;) {
1159 int n1, n2, len, r;
1160 uint32_t byte;
1161 _cleanup_free_ char *cbyte = NULL;
1162
1163 r = extract_first_word(&p, &cbyte, ":", 0);
1164 if (r == -ENOMEM)
1165 return log_oom();
1166 if (r < 0) {
1167 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to read DUID, ignoring assignment: %s.", rvalue);
1168 return 0;
1169 }
1170 if (r == 0)
1171 break;
1172
92914960 1173 if (count >= MAX_DUID_DATA_LEN) {
cde74a65
YW
1174 log_syntax(unit, LOG_WARNING, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue);
1175 return 0;
1176 }
1177
1178 len = strlen(cbyte);
1179 if (!IN_SET(len, 1, 2)) {
1180 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue);
1181 return 0;
1182 }
1183 n1 = unhexchar(cbyte[0]);
1184 if (len == 2)
1185 n2 = unhexchar(cbyte[1]);
1186 else
1187 n2 = 0;
1188
1189 if (n1 < 0 || n2 < 0) {
1190 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue);
1191 return 0;
1192 }
1193
1194 byte = ((uint8_t) n1 << (4 * (len-1))) | (uint8_t) n2;
1195 raw_data[count++] = byte;
1196 }
1197
4e26a5ba
YW
1198 assert_cc(sizeof(raw_data) == sizeof(duid->raw_data));
1199 memcpy(duid->raw_data, raw_data, count);
1200 duid->raw_data_len = count;
1201 duid->set = force;
1202
cde74a65
YW
1203 return 0;
1204}
4e26a5ba
YW
1205
1206int config_parse_manager_duid_rawdata(
1207 const char *unit,
1208 const char *filename,
1209 unsigned line,
1210 const char *section,
1211 unsigned section_line,
1212 const char *lvalue,
1213 int ltype,
1214 const char *rvalue,
1215 void *data,
1216 void *userdata) {
1217
99534007 1218 Manager *manager = ASSERT_PTR(userdata);
4e26a5ba
YW
1219 int r;
1220
87160186 1221 /* For backward compatibility. Setting both DHCPv4 and DHCPv6 DUID if they are not specified explicitly. */
4e26a5ba
YW
1222
1223 r = config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp_duid, manager);
1224 if (r < 0)
1225 return r;
1226
1227 return config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp6_duid, manager);
1228}
1229
1230int config_parse_network_duid_rawdata(
1231 const char *unit,
1232 const char *filename,
1233 unsigned line,
1234 const char *section,
1235 unsigned section_line,
1236 const char *lvalue,
1237 int ltype,
1238 const char *rvalue,
1239 void *data,
1240 void *userdata) {
1241
99534007 1242 Network *network = ASSERT_PTR(userdata);
4e26a5ba
YW
1243 int r;
1244
4e26a5ba
YW
1245 r = config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, true, rvalue, &network->dhcp_duid, network);
1246 if (r < 0)
1247 return r;
1248
87160186 1249 /* For backward compatibility, also set DHCPv6 DUID if not specified explicitly. */
4e26a5ba
YW
1250 return config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, false, rvalue, &network->dhcp6_duid, network);
1251}
2cba2146
YW
1252
1253int config_parse_uplink(
1254 const char *unit,
1255 const char *filename,
1256 unsigned line,
1257 const char *section,
1258 unsigned section_line,
1259 const char *lvalue,
1260 int ltype,
1261 const char *rvalue,
1262 void *data,
1263 void *userdata) {
1264
99534007 1265 Network *network = ASSERT_PTR(userdata);
dc5cae6c 1266 bool accept_none = true;
2cba2146
YW
1267 int *index, r;
1268 char **name;
1269
1270 assert(filename);
1271 assert(section);
1272 assert(lvalue);
1273 assert(rvalue);
1274
1275 if (streq(section, "DHCPServer")) {
1276 index = &network->dhcp_server_uplink_index;
1277 name = &network->dhcp_server_uplink_name;
1278 } else if (streq(section, "IPv6SendRA")) {
1279 index = &network->router_uplink_index;
1280 name = &network->router_uplink_name;
a27588d4
YW
1281 } else if (STR_IN_SET(section, "DHCPv6PrefixDelegation", "DHCPPrefixDelegation")) {
1282 index = &network->dhcp_pd_uplink_index;
1283 name = &network->dhcp_pd_uplink_name;
dc5cae6c 1284 accept_none = false;
2cba2146
YW
1285 } else
1286 assert_not_reached();
1287
1288 if (isempty(rvalue) || streq(rvalue, ":auto")) {
1289 *index = UPLINK_INDEX_AUTO;
1290 *name = mfree(*name);
1291 return 0;
1292 }
1293
dc5cae6c 1294 if (accept_none && streq(rvalue, ":none")) {
2cba2146
YW
1295 *index = UPLINK_INDEX_NONE;
1296 *name = mfree(*name);
1297 return 0;
1298 }
1299
dc5cae6c
YW
1300 if (!accept_none && streq(rvalue, ":self")) {
1301 *index = UPLINK_INDEX_SELF;
1302 *name = mfree(*name);
1303 return 0;
1304 }
1305
2cba2146
YW
1306 r = parse_ifindex(rvalue);
1307 if (r > 0) {
1308 *index = r;
1309 *name = mfree(*name);
1310 return 0;
1311 }
1312
1313 if (!ifname_valid_full(rvalue, IFNAME_VALID_ALTERNATIVE)) {
1314 log_syntax(unit, LOG_WARNING, filename, line, 0,
1315 "Invalid interface name in %s=, ignoring assignment: %s", lvalue, rvalue);
1316 return 0;
1317 }
1318
1319 /* The interface name will be resolved later. */
1320 r = free_and_strdup_warn(name, rvalue);
1321 if (r < 0)
1322 return r;
1323
1324 /* Note, if uplink_name is set, then uplink_index will be ignored. So, the below does not mean
1325 * an uplink interface will be selected automatically. */
1326 *index = UPLINK_INDEX_AUTO;
1327 return 0;
1328}