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