]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp-common.c
253756126554628789e8b9cfbb8eba5a94d962bb
[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 "dhcp-internal.h"
7 #include "dhcp6-internal.h"
8 #include "escape.h"
9 #include "in-addr-util.h"
10 #include "networkd-dhcp-common.h"
11 #include "networkd-link.h"
12 #include "networkd-manager.h"
13 #include "networkd-network.h"
14 #include "parse-util.h"
15 #include "socket-util.h"
16 #include "string-table.h"
17 #include "strv.h"
18
19 bool link_dhcp_enabled(Link *link, int family) {
20 assert(link);
21 assert(IN_SET(family, AF_INET, AF_INET6));
22
23 if (family == AF_INET6 && !socket_ipv6_is_supported())
24 return false;
25
26 if (link->flags & IFF_LOOPBACK)
27 return false;
28
29 if (link->iftype == ARPHRD_CAN)
30 return false;
31
32 if (!link->network)
33 return false;
34
35 return link->network->dhcp & (family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6);
36 }
37
38 void network_adjust_dhcp(Network *network) {
39 assert(network);
40 assert(network->dhcp >= 0);
41
42 if (network->dhcp == ADDRESS_FAMILY_NO)
43 return;
44
45 /* Bonding slave does not support addressing. */
46 if (network->bond) {
47 log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.",
48 network->filename);
49 network->dhcp = ADDRESS_FAMILY_NO;
50 return;
51 }
52
53 if (!FLAGS_SET(network->link_local, ADDRESS_FAMILY_IPV6) &&
54 FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV6)) {
55 log_warning("%s: DHCPv6 client is enabled but IPv6 link local addressing is disabled. "
56 "Disabling DHCPv6 client.", network->filename);
57 SET_FLAG(network->dhcp, ADDRESS_FAMILY_IPV6, false);
58 }
59 }
60
61 DUID* link_get_duid(Link *link) {
62 if (link->network->duid.type != _DUID_TYPE_INVALID)
63 return &link->network->duid;
64 else
65 return &link->manager->duid;
66 }
67
68 static int duid_set_uuid(DUID *duid, sd_id128_t uuid) {
69 assert(duid);
70
71 if (duid->raw_data_len > 0)
72 return 0;
73
74 if (duid->type != DUID_TYPE_UUID)
75 return -EINVAL;
76
77 memcpy(&duid->raw_data, &uuid, sizeof(sd_id128_t));
78 duid->raw_data_len = sizeof(sd_id128_t);
79
80 return 1;
81 }
82
83 static int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
84 Manager *manager = userdata;
85 const sd_bus_error *e;
86 const void *a;
87 size_t sz;
88 DUID *duid;
89 Link *link;
90 int r;
91
92 assert(m);
93 assert(manager);
94
95 e = sd_bus_message_get_error(m);
96 if (e) {
97 log_error_errno(sd_bus_error_get_errno(e),
98 "Could not get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %s",
99 e->message);
100 goto configure;
101 }
102
103 r = sd_bus_message_read_array(m, 'y', &a, &sz);
104 if (r < 0)
105 goto configure;
106
107 if (sz != sizeof(sd_id128_t)) {
108 log_error("Invalid product UUID. Falling back to use machine-app-specific ID as DUID-UUID.");
109 goto configure;
110 }
111
112 memcpy(&manager->product_uuid, a, sz);
113 while ((duid = set_steal_first(manager->duids_requesting_uuid)))
114 (void) duid_set_uuid(duid, manager->product_uuid);
115
116 manager->duids_requesting_uuid = set_free(manager->duids_requesting_uuid);
117
118 configure:
119 while ((link = set_steal_first(manager->links_requesting_uuid))) {
120 link_unref(link);
121
122 r = link_configure(link);
123 if (r < 0)
124 link_enter_failed(link);
125 }
126
127 manager->links_requesting_uuid = set_free(manager->links_requesting_uuid);
128
129 /* To avoid calling GetProductUUID() bus method so frequently, set the flag below
130 * even if the method fails. */
131 manager->has_product_uuid = true;
132
133 return 1;
134 }
135
136 int manager_request_product_uuid(Manager *m, Link *link) {
137 int r;
138
139 assert(m);
140
141 if (m->has_product_uuid)
142 return 0;
143
144 log_debug("Requesting product UUID");
145
146 if (link) {
147 DUID *duid;
148
149 assert_se(duid = link_get_duid(link));
150
151 r = set_ensure_put(&m->links_requesting_uuid, NULL, link);
152 if (r < 0)
153 return log_oom();
154 if (r > 0)
155 link_ref(link);
156
157 r = set_ensure_put(&m->duids_requesting_uuid, NULL, duid);
158 if (r < 0)
159 return log_oom();
160 }
161
162 if (!m->bus || sd_bus_is_ready(m->bus) <= 0) {
163 log_debug("Not connected to system bus, requesting product UUID later.");
164 return 0;
165 }
166
167 r = sd_bus_call_method_async(
168 m->bus,
169 NULL,
170 "org.freedesktop.hostname1",
171 "/org/freedesktop/hostname1",
172 "org.freedesktop.hostname1",
173 "GetProductUUID",
174 get_product_uuid_handler,
175 m,
176 "b",
177 false);
178 if (r < 0)
179 return log_warning_errno(r, "Failed to get product UUID: %m");
180
181 return 0;
182 }
183
184 static bool link_requires_uuid(Link *link) {
185 const DUID *duid;
186
187 assert(link);
188 assert(link->manager);
189 assert(link->network);
190
191 duid = link_get_duid(link);
192 if (duid->type != DUID_TYPE_UUID || duid->raw_data_len != 0)
193 return false;
194
195 if (link_dhcp4_enabled(link) && IN_SET(link->network->dhcp_client_identifier, DHCP_CLIENT_ID_DUID, DHCP_CLIENT_ID_DUID_ONLY))
196 return true;
197
198 if (link_dhcp6_enabled(link) || link_ipv6_accept_ra_enabled(link))
199 return true;
200
201 return false;
202 }
203
204 int link_configure_duid(Link *link) {
205 Manager *m;
206 DUID *duid;
207 int r;
208
209 assert(link);
210 assert(link->manager);
211 assert(link->network);
212
213 m = link->manager;
214 duid = link_get_duid(link);
215
216 if (!link_requires_uuid(link))
217 return 1;
218
219 if (m->has_product_uuid) {
220 (void) duid_set_uuid(duid, m->product_uuid);
221 return 1;
222 }
223
224 if (!m->links_requesting_uuid) {
225 r = manager_request_product_uuid(m, link);
226 if (r < 0) {
227 if (r == -ENOMEM)
228 return r;
229
230 log_link_warning_errno(link, r,
231 "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m");
232 return 1;
233 }
234 } else {
235 r = set_put(m->links_requesting_uuid, link);
236 if (r < 0)
237 return log_oom();
238 if (r > 0)
239 link_ref(link);
240
241 r = set_put(m->duids_requesting_uuid, duid);
242 if (r < 0)
243 return log_oom();
244 }
245
246 return 0;
247 }
248
249 int config_parse_dhcp(
250 const char* unit,
251 const char *filename,
252 unsigned line,
253 const char *section,
254 unsigned section_line,
255 const char *lvalue,
256 int ltype,
257 const char *rvalue,
258 void *data,
259 void *userdata) {
260
261 AddressFamily *dhcp = data, s;
262
263 assert(filename);
264 assert(lvalue);
265 assert(rvalue);
266 assert(data);
267
268 /* Note that this is mostly like
269 * config_parse_address_family(), except that it
270 * understands some old names for the enum values */
271
272 s = address_family_from_string(rvalue);
273 if (s < 0) {
274
275 /* Previously, we had a slightly different enum here,
276 * support its values for compatibility. */
277
278 if (streq(rvalue, "none"))
279 s = ADDRESS_FAMILY_NO;
280 else if (streq(rvalue, "v4"))
281 s = ADDRESS_FAMILY_IPV4;
282 else if (streq(rvalue, "v6"))
283 s = ADDRESS_FAMILY_IPV6;
284 else if (streq(rvalue, "both"))
285 s = ADDRESS_FAMILY_YES;
286 else {
287 log_syntax(unit, LOG_WARNING, filename, line, 0,
288 "Failed to parse DHCP option, ignoring: %s", rvalue);
289 return 0;
290 }
291
292 log_syntax(unit, LOG_WARNING, filename, line, 0,
293 "DHCP=%s is deprecated, please use DHCP=%s instead.",
294 rvalue, address_family_to_string(s));
295 }
296
297 *dhcp = s;
298 return 0;
299 }
300
301 int config_parse_dhcp_route_metric(
302 const char* unit,
303 const char *filename,
304 unsigned line,
305 const char *section,
306 unsigned section_line,
307 const char *lvalue,
308 int ltype,
309 const char *rvalue,
310 void *data,
311 void *userdata) {
312
313 Network *network = data;
314 uint32_t metric;
315 int r;
316
317 assert(filename);
318 assert(lvalue);
319 assert(rvalue);
320 assert(data);
321
322 r = safe_atou32(rvalue, &metric);
323 if (r < 0) {
324 log_syntax(unit, LOG_WARNING, filename, line, r,
325 "Failed to parse RouteMetric=%s, ignoring assignment: %m", rvalue);
326 return 0;
327 }
328
329 if (streq_ptr(section, "DHCPv4")) {
330 network->dhcp_route_metric = metric;
331 network->dhcp_route_metric_set = true;
332 } else if (streq_ptr(section, "DHCPv6")) {
333 network->dhcp6_route_metric = metric;
334 network->dhcp6_route_metric_set = true;
335 } else { /* [DHCP] section */
336 if (!network->dhcp_route_metric_set)
337 network->dhcp_route_metric = metric;
338 if (!network->dhcp6_route_metric_set)
339 network->dhcp6_route_metric = metric;
340 }
341
342 return 0;
343 }
344
345 int config_parse_dhcp_use_dns(
346 const char* unit,
347 const char *filename,
348 unsigned line,
349 const char *section,
350 unsigned section_line,
351 const char *lvalue,
352 int ltype,
353 const char *rvalue,
354 void *data,
355 void *userdata) {
356
357 Network *network = data;
358 int r;
359
360 assert(filename);
361 assert(lvalue);
362 assert(rvalue);
363 assert(data);
364
365 r = parse_boolean(rvalue);
366 if (r < 0) {
367 log_syntax(unit, LOG_WARNING, filename, line, r,
368 "Failed to parse UseDNS=%s, ignoring assignment: %m", rvalue);
369 return 0;
370 }
371
372 if (streq_ptr(section, "DHCPv4")) {
373 network->dhcp_use_dns = r;
374 network->dhcp_use_dns_set = true;
375 } else if (streq_ptr(section, "DHCPv6")) {
376 network->dhcp6_use_dns = r;
377 network->dhcp6_use_dns_set = true;
378 } else { /* [DHCP] section */
379 if (!network->dhcp_use_dns_set)
380 network->dhcp_use_dns = r;
381 if (!network->dhcp6_use_dns_set)
382 network->dhcp6_use_dns = r;
383 }
384
385 return 0;
386 }
387
388 int config_parse_dhcp_use_ntp(
389 const char* unit,
390 const char *filename,
391 unsigned line,
392 const char *section,
393 unsigned section_line,
394 const char *lvalue,
395 int ltype,
396 const char *rvalue,
397 void *data,
398 void *userdata) {
399
400 Network *network = data;
401 int r;
402
403 assert(filename);
404 assert(lvalue);
405 assert(rvalue);
406 assert(data);
407
408 r = parse_boolean(rvalue);
409 if (r < 0) {
410 log_syntax(unit, LOG_WARNING, filename, line, r,
411 "Failed to parse UseNTP=%s, ignoring assignment: %m", rvalue);
412 return 0;
413 }
414
415 if (streq_ptr(section, "DHCPv4")) {
416 network->dhcp_use_ntp = r;
417 network->dhcp_use_ntp_set = true;
418 } else if (streq_ptr(section, "DHCPv6")) {
419 network->dhcp6_use_ntp = r;
420 network->dhcp6_use_ntp_set = true;
421 } else { /* [DHCP] section */
422 if (!network->dhcp_use_ntp_set)
423 network->dhcp_use_ntp = r;
424 if (!network->dhcp6_use_ntp_set)
425 network->dhcp6_use_ntp = r;
426 }
427
428 return 0;
429 }
430
431 int config_parse_section_route_table(
432 const char *unit,
433 const char *filename,
434 unsigned line,
435 const char *section,
436 unsigned section_line,
437 const char *lvalue,
438 int ltype,
439 const char *rvalue,
440 void *data,
441 void *userdata) {
442
443 Network *network = data;
444 uint32_t rt;
445 int r;
446
447 assert(filename);
448 assert(lvalue);
449 assert(rvalue);
450 assert(data);
451
452 r = safe_atou32(rvalue, &rt);
453 if (r < 0) {
454 log_syntax(unit, LOG_WARNING, filename, line, r,
455 "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue);
456 return 0;
457 }
458
459 if (STRPTR_IN_SET(section, "DHCP", "DHCPv4")) {
460 network->dhcp_route_table = rt;
461 network->dhcp_route_table_set = true;
462 } else { /* section is IPv6AcceptRA */
463 network->ipv6_accept_ra_route_table = rt;
464 network->ipv6_accept_ra_route_table_set = true;
465 }
466
467 return 0;
468 }
469
470 int config_parse_iaid(const char *unit,
471 const char *filename,
472 unsigned line,
473 const char *section,
474 unsigned section_line,
475 const char *lvalue,
476 int ltype,
477 const char *rvalue,
478 void *data,
479 void *userdata) {
480 Network *network = data;
481 uint32_t iaid;
482 int r;
483
484 assert(filename);
485 assert(lvalue);
486 assert(rvalue);
487 assert(network);
488
489 r = safe_atou32(rvalue, &iaid);
490 if (r < 0) {
491 log_syntax(unit, LOG_WARNING, filename, line, r,
492 "Unable to read IAID, ignoring assignment: %s", rvalue);
493 return 0;
494 }
495
496 network->iaid = iaid;
497 network->iaid_set = true;
498
499 return 0;
500 }
501
502 int config_parse_dhcp_user_class(
503 const char *unit,
504 const char *filename,
505 unsigned line,
506 const char *section,
507 unsigned section_line,
508 const char *lvalue,
509 int ltype,
510 const char *rvalue,
511 void *data,
512 void *userdata) {
513
514 char ***l = data;
515 int r;
516
517 assert(l);
518 assert(lvalue);
519 assert(rvalue);
520
521 if (isempty(rvalue)) {
522 *l = strv_free(*l);
523 return 0;
524 }
525
526 for (const char *p = rvalue;;) {
527 _cleanup_free_ char *w = NULL;
528
529 r = extract_first_word(&p, &w, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
530 if (r == -ENOMEM)
531 return log_oom();
532 if (r < 0) {
533 log_syntax(unit, LOG_WARNING, filename, line, r,
534 "Failed to split user classes option, ignoring: %s", rvalue);
535 return 0;
536 }
537 if (r == 0)
538 return 0;
539
540 if (ltype == AF_INET) {
541 if (strlen(w) > UINT8_MAX) {
542 log_syntax(unit, LOG_WARNING, filename, line, 0,
543 "%s length is not in the range 1-255, ignoring.", w);
544 continue;
545 }
546 } else {
547 if (strlen(w) > UINT16_MAX) {
548 log_syntax(unit, LOG_WARNING, filename, line, 0,
549 "%s length is not in the range 1-65535, ignoring.", w);
550 continue;
551 }
552 }
553
554 r = strv_push(l, w);
555 if (r < 0)
556 return log_oom();
557
558 w = NULL;
559 }
560 }
561
562 int config_parse_dhcp_vendor_class(
563 const char *unit,
564 const char *filename,
565 unsigned line,
566 const char *section,
567 unsigned section_line,
568 const char *lvalue,
569 int ltype,
570 const char *rvalue,
571 void *data,
572 void *userdata) {
573 char ***l = data;
574 int r;
575
576 assert(l);
577 assert(lvalue);
578 assert(rvalue);
579
580 if (isempty(rvalue)) {
581 *l = strv_free(*l);
582 return 0;
583 }
584
585 for (const char *p = rvalue;;) {
586 _cleanup_free_ char *w = NULL;
587
588 r = extract_first_word(&p, &w, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
589 if (r == -ENOMEM)
590 return log_oom();
591 if (r < 0) {
592 log_syntax(unit, LOG_WARNING, filename, line, r,
593 "Failed to split vendor classes option, ignoring: %s", rvalue);
594 return 0;
595 }
596 if (r == 0)
597 return 0;
598
599 if (strlen(w) > UINT8_MAX) {
600 log_syntax(unit, LOG_WARNING, filename, line, 0,
601 "%s length is not in the range 1-255, ignoring.", w);
602 continue;
603 }
604
605 r = strv_push(l, w);
606 if (r < 0)
607 return log_oom();
608
609 w = NULL;
610 }
611 }
612
613 int config_parse_dhcp_send_option(
614 const char *unit,
615 const char *filename,
616 unsigned line,
617 const char *section,
618 unsigned section_line,
619 const char *lvalue,
620 int ltype,
621 const char *rvalue,
622 void *data,
623 void *userdata) {
624
625 _cleanup_(sd_dhcp_option_unrefp) sd_dhcp_option *opt4 = NULL, *old4 = NULL;
626 _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *opt6 = NULL, *old6 = NULL;
627 uint32_t uint32_data, enterprise_identifier = 0;
628 _cleanup_free_ char *word = NULL, *q = NULL;
629 OrderedHashmap **options = data;
630 uint16_t u16, uint16_data;
631 union in_addr_union addr;
632 DHCPOptionDataType type;
633 uint8_t u8, uint8_data;
634 const void *udata;
635 const char *p;
636 ssize_t sz;
637 int r;
638
639 assert(filename);
640 assert(lvalue);
641 assert(rvalue);
642 assert(data);
643
644 if (isempty(rvalue)) {
645 *options = ordered_hashmap_free(*options);
646 return 0;
647 }
648
649 p = rvalue;
650 if (ltype == AF_INET6 && streq(lvalue, "SendVendorOption")) {
651 r = extract_first_word(&p, &word, ":", 0);
652 if (r == -ENOMEM)
653 return log_oom();
654 if (r <= 0 || isempty(p)) {
655 log_syntax(unit, LOG_WARNING, filename, line, r,
656 "Invalid DHCP option, ignoring assignment: %s", rvalue);
657 return 0;
658 }
659
660 r = safe_atou32(word, &enterprise_identifier);
661 if (r < 0) {
662 log_syntax(unit, LOG_WARNING, filename, line, r,
663 "Failed to parse DHCPv6 enterprise identifier data, ignoring assignment: %s", p);
664 return 0;
665 }
666 word = mfree(word);
667 }
668
669 r = extract_first_word(&p, &word, ":", 0);
670 if (r == -ENOMEM)
671 return log_oom();
672 if (r <= 0 || isempty(p)) {
673 log_syntax(unit, LOG_WARNING, filename, line, r,
674 "Invalid DHCP option, ignoring assignment: %s", rvalue);
675 return 0;
676 }
677
678 if (ltype == AF_INET6) {
679 r = safe_atou16(word, &u16);
680 if (r < 0) {
681 log_syntax(unit, LOG_WARNING, filename, line, r,
682 "Invalid DHCP option, ignoring assignment: %s", rvalue);
683 return 0;
684 }
685 if (u16 < 1 || u16 >= UINT16_MAX) {
686 log_syntax(unit, LOG_WARNING, filename, line, 0,
687 "Invalid DHCP option, valid range is 1-65535, ignoring assignment: %s", rvalue);
688 return 0;
689 }
690 } else {
691 r = safe_atou8(word, &u8);
692 if (r < 0) {
693 log_syntax(unit, LOG_WARNING, filename, line, r,
694 "Invalid DHCP option, ignoring assignment: %s", rvalue);
695 return 0;
696 }
697 if (u8 < 1 || u8 >= UINT8_MAX) {
698 log_syntax(unit, LOG_WARNING, filename, line, 0,
699 "Invalid DHCP option, valid range is 1-254, ignoring assignment: %s", rvalue);
700 return 0;
701 }
702 }
703
704 word = mfree(word);
705 r = extract_first_word(&p, &word, ":", 0);
706 if (r == -ENOMEM)
707 return log_oom();
708 if (r <= 0 || isempty(p)) {
709 log_syntax(unit, LOG_WARNING, filename, line, r,
710 "Invalid DHCP option, ignoring assignment: %s", rvalue);
711 return 0;
712 }
713
714 type = dhcp_option_data_type_from_string(word);
715 if (type < 0) {
716 log_syntax(unit, LOG_WARNING, filename, line, 0,
717 "Invalid DHCP option data type, ignoring assignment: %s", p);
718 return 0;
719 }
720
721 switch(type) {
722 case DHCP_OPTION_DATA_UINT8:{
723 r = safe_atou8(p, &uint8_data);
724 if (r < 0) {
725 log_syntax(unit, LOG_WARNING, filename, line, r,
726 "Failed to parse DHCP uint8 data, ignoring assignment: %s", p);
727 return 0;
728 }
729
730 udata = &uint8_data;
731 sz = sizeof(uint8_t);
732 break;
733 }
734 case DHCP_OPTION_DATA_UINT16:{
735 r = safe_atou16(p, &uint16_data);
736 if (r < 0) {
737 log_syntax(unit, LOG_WARNING, filename, line, r,
738 "Failed to parse DHCP uint16 data, ignoring assignment: %s", p);
739 return 0;
740 }
741
742 udata = &uint16_data;
743 sz = sizeof(uint16_t);
744 break;
745 }
746 case DHCP_OPTION_DATA_UINT32: {
747 r = safe_atou32(p, &uint32_data);
748 if (r < 0) {
749 log_syntax(unit, LOG_WARNING, filename, line, r,
750 "Failed to parse DHCP uint32 data, ignoring assignment: %s", p);
751 return 0;
752 }
753
754 udata = &uint32_data;
755 sz = sizeof(uint32_t);
756
757 break;
758 }
759 case DHCP_OPTION_DATA_IPV4ADDRESS: {
760 r = in_addr_from_string(AF_INET, p, &addr);
761 if (r < 0) {
762 log_syntax(unit, LOG_WARNING, filename, line, r,
763 "Failed to parse DHCP ipv4address data, ignoring assignment: %s", p);
764 return 0;
765 }
766
767 udata = &addr.in;
768 sz = sizeof(addr.in.s_addr);
769 break;
770 }
771 case DHCP_OPTION_DATA_IPV6ADDRESS: {
772 r = in_addr_from_string(AF_INET6, p, &addr);
773 if (r < 0) {
774 log_syntax(unit, LOG_WARNING, filename, line, r,
775 "Failed to parse DHCP ipv6address data, ignoring assignment: %s", p);
776 return 0;
777 }
778
779 udata = &addr.in6;
780 sz = sizeof(addr.in6.s6_addr);
781 break;
782 }
783 case DHCP_OPTION_DATA_STRING:
784 sz = cunescape(p, UNESCAPE_ACCEPT_NUL, &q);
785 if (sz < 0)
786 log_syntax(unit, LOG_WARNING, filename, line, sz,
787 "Failed to decode DHCP option data, ignoring assignment: %s", p);
788
789 udata = q;
790 break;
791 default:
792 return -EINVAL;
793 }
794
795 if (ltype == AF_INET6) {
796 r = sd_dhcp6_option_new(u16, udata, sz, enterprise_identifier, &opt6);
797 if (r < 0) {
798 log_syntax(unit, LOG_WARNING, filename, line, r,
799 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
800 return 0;
801 }
802
803 r = ordered_hashmap_ensure_allocated(options, &dhcp6_option_hash_ops);
804 if (r < 0)
805 return log_oom();
806
807 /* Overwrite existing option */
808 old6 = ordered_hashmap_get(*options, UINT_TO_PTR(u16));
809 r = ordered_hashmap_replace(*options, UINT_TO_PTR(u16), opt6);
810 if (r < 0) {
811 log_syntax(unit, LOG_WARNING, filename, line, r,
812 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
813 return 0;
814 }
815 TAKE_PTR(opt6);
816 } else {
817 r = sd_dhcp_option_new(u8, udata, sz, &opt4);
818 if (r < 0) {
819 log_syntax(unit, LOG_WARNING, filename, line, r,
820 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
821 return 0;
822 }
823
824 r = ordered_hashmap_ensure_allocated(options, &dhcp_option_hash_ops);
825 if (r < 0)
826 return log_oom();
827
828 /* Overwrite existing option */
829 old4 = ordered_hashmap_get(*options, UINT_TO_PTR(u8));
830 r = ordered_hashmap_replace(*options, UINT_TO_PTR(u8), opt4);
831 if (r < 0) {
832 log_syntax(unit, LOG_WARNING, filename, line, r,
833 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
834 return 0;
835 }
836 TAKE_PTR(opt4);
837 }
838 return 0;
839 }
840
841 int config_parse_dhcp_request_options(
842 const char *unit,
843 const char *filename,
844 unsigned line,
845 const char *section,
846 unsigned section_line,
847 const char *lvalue,
848 int ltype,
849 const char *rvalue,
850 void *data,
851 void *userdata) {
852
853 Network *network = data;
854 const char *p;
855 int r;
856
857 assert(filename);
858 assert(lvalue);
859 assert(rvalue);
860 assert(data);
861
862 if (isempty(rvalue)) {
863 if (ltype == AF_INET)
864 network->dhcp_request_options = set_free(network->dhcp_request_options);
865 else
866 network->dhcp6_request_options = set_free(network->dhcp6_request_options);
867
868 return 0;
869 }
870
871 for (p = rvalue;;) {
872 _cleanup_free_ char *n = NULL;
873 uint32_t i;
874
875 r = extract_first_word(&p, &n, NULL, 0);
876 if (r == -ENOMEM)
877 return log_oom();
878 if (r < 0) {
879 log_syntax(unit, LOG_WARNING, filename, line, r,
880 "Failed to parse DHCP request option, ignoring assignment: %s",
881 rvalue);
882 return 0;
883 }
884 if (r == 0)
885 return 0;
886
887 r = safe_atou32(n, &i);
888 if (r < 0) {
889 log_syntax(unit, LOG_WARNING, filename, line, r,
890 "DHCP request option is invalid, ignoring assignment: %s", n);
891 continue;
892 }
893
894 if (i < 1 || i >= UINT8_MAX) {
895 log_syntax(unit, LOG_WARNING, filename, line, 0,
896 "DHCP request option is invalid, valid range is 1-254, ignoring assignment: %s", n);
897 continue;
898 }
899
900 r = set_ensure_put(ltype == AF_INET ? &network->dhcp_request_options : &network->dhcp6_request_options,
901 NULL, UINT32_TO_PTR(i));
902 if (r < 0)
903 log_syntax(unit, LOG_WARNING, filename, line, r,
904 "Failed to store DHCP request option '%s', ignoring assignment: %m", n);
905 }
906 }
907
908 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains,
909 "Failed to parse DHCP use domains setting");
910
911 static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = {
912 [DHCP_USE_DOMAINS_NO] = "no",
913 [DHCP_USE_DOMAINS_ROUTE] = "route",
914 [DHCP_USE_DOMAINS_YES] = "yes",
915 };
916
917 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES);
918
919 static const char * const dhcp_option_data_type_table[_DHCP_OPTION_DATA_MAX] = {
920 [DHCP_OPTION_DATA_UINT8] = "uint8",
921 [DHCP_OPTION_DATA_UINT16] = "uint16",
922 [DHCP_OPTION_DATA_UINT32] = "uint32",
923 [DHCP_OPTION_DATA_STRING] = "string",
924 [DHCP_OPTION_DATA_IPV4ADDRESS] = "ipv4address",
925 [DHCP_OPTION_DATA_IPV6ADDRESS] = "ipv6address",
926 };
927
928 DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type, DHCPOptionDataType);