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