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