1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "dhcp-internal.h"
4 #include "dhcp6-internal.h"
6 #include "in-addr-util.h"
7 #include "networkd-dhcp-common.h"
8 #include "networkd-network.h"
9 #include "parse-util.h"
10 #include "string-table.h"
13 int config_parse_dhcp(
18 unsigned section_line
,
25 AddressFamily
*dhcp
= data
, s
;
32 /* Note that this is mostly like
33 * config_parse_address_family(), except that it
34 * understands some old names for the enum values */
36 s
= address_family_from_string(rvalue
);
39 /* Previously, we had a slightly different enum here,
40 * support its values for compatibility. */
42 if (streq(rvalue
, "none"))
43 s
= ADDRESS_FAMILY_NO
;
44 else if (streq(rvalue
, "v4"))
45 s
= ADDRESS_FAMILY_IPV4
;
46 else if (streq(rvalue
, "v6"))
47 s
= ADDRESS_FAMILY_IPV6
;
48 else if (streq(rvalue
, "both"))
49 s
= ADDRESS_FAMILY_YES
;
51 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
52 "Failed to parse DHCP option, ignoring: %s", rvalue
);
56 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
57 "DHCP=%s is deprecated, please use DHCP=%s instead.",
58 rvalue
, address_family_to_string(s
));
65 int config_parse_dhcp_route_metric(
70 unsigned section_line
,
77 Network
*network
= data
;
86 r
= safe_atou32(rvalue
, &metric
);
88 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
89 "Failed to parse RouteMetric=%s, ignoring assignment: %m", rvalue
);
93 if (streq_ptr(section
, "DHCPv4")) {
94 network
->dhcp_route_metric
= metric
;
95 network
->dhcp_route_metric_set
= true;
96 } else if (streq_ptr(section
, "DHCPv6")) {
97 network
->dhcp6_route_metric
= metric
;
98 network
->dhcp6_route_metric_set
= true;
99 } else { /* [DHCP] section */
100 if (!network
->dhcp_route_metric_set
)
101 network
->dhcp_route_metric
= metric
;
102 if (!network
->dhcp6_route_metric_set
)
103 network
->dhcp6_route_metric
= metric
;
109 int config_parse_dhcp_use_dns(
111 const char *filename
,
114 unsigned section_line
,
121 Network
*network
= data
;
129 r
= parse_boolean(rvalue
);
131 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
132 "Failed to parse UseDNS=%s, ignoring assignment: %m", rvalue
);
136 if (streq_ptr(section
, "DHCPv4")) {
137 network
->dhcp_use_dns
= r
;
138 network
->dhcp_use_dns_set
= true;
139 } else if (streq_ptr(section
, "DHCPv6")) {
140 network
->dhcp6_use_dns
= r
;
141 network
->dhcp6_use_dns_set
= true;
142 } else { /* [DHCP] section */
143 if (!network
->dhcp_use_dns_set
)
144 network
->dhcp_use_dns
= r
;
145 if (!network
->dhcp6_use_dns_set
)
146 network
->dhcp6_use_dns
= r
;
152 int config_parse_dhcp_use_ntp(
154 const char *filename
,
157 unsigned section_line
,
164 Network
*network
= data
;
172 r
= parse_boolean(rvalue
);
174 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
175 "Failed to parse UseNTP=%s, ignoring assignment: %m", rvalue
);
179 if (streq_ptr(section
, "DHCPv4")) {
180 network
->dhcp_use_ntp
= r
;
181 network
->dhcp_use_ntp_set
= true;
182 } else if (streq_ptr(section
, "DHCPv6")) {
183 network
->dhcp6_use_ntp
= r
;
184 network
->dhcp6_use_ntp_set
= true;
185 } else { /* [DHCP] section */
186 if (!network
->dhcp_use_ntp_set
)
187 network
->dhcp_use_ntp
= r
;
188 if (!network
->dhcp6_use_ntp_set
)
189 network
->dhcp6_use_ntp
= r
;
195 int config_parse_section_route_table(
197 const char *filename
,
200 unsigned section_line
,
207 Network
*network
= data
;
216 r
= safe_atou32(rvalue
, &rt
);
218 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
219 "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue
);
223 if (STRPTR_IN_SET(section
, "DHCP", "DHCPv4")) {
224 network
->dhcp_route_table
= rt
;
225 network
->dhcp_route_table_set
= true;
226 } else { /* section is IPv6AcceptRA */
227 network
->ipv6_accept_ra_route_table
= rt
;
228 network
->ipv6_accept_ra_route_table_set
= true;
234 int config_parse_iaid(const char *unit
,
235 const char *filename
,
238 unsigned section_line
,
244 Network
*network
= data
;
253 r
= safe_atou32(rvalue
, &iaid
);
255 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
256 "Unable to read IAID, ignoring assignment: %s", rvalue
);
260 network
->iaid
= iaid
;
261 network
->iaid_set
= true;
266 int config_parse_dhcp_user_class(
268 const char *filename
,
271 unsigned section_line
,
285 if (isempty(rvalue
)) {
290 for (const char *p
= rvalue
;;) {
291 _cleanup_free_
char *w
= NULL
;
293 r
= extract_first_word(&p
, &w
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_UNQUOTE
);
297 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
298 "Failed to split user classes option, ignoring: %s", rvalue
);
304 if (ltype
== AF_INET
) {
305 if (strlen(w
) > UINT8_MAX
) {
306 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
307 "%s length is not in the range 1-255, ignoring.", w
);
311 if (strlen(w
) > UINT16_MAX
) {
312 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
313 "%s length is not in the range 1-65535, ignoring.", w
);
326 int config_parse_dhcp_vendor_class(
328 const char *filename
,
331 unsigned section_line
,
344 if (isempty(rvalue
)) {
349 for (const char *p
= rvalue
;;) {
350 _cleanup_free_
char *w
= NULL
;
352 r
= extract_first_word(&p
, &w
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_UNQUOTE
);
356 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
357 "Failed to split vendor classes option, ignoring: %s", rvalue
);
363 if (strlen(w
) > UINT8_MAX
) {
364 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
365 "%s length is not in the range 1-255, ignoring.", w
);
377 int config_parse_dhcp_send_option(
379 const char *filename
,
382 unsigned section_line
,
389 _cleanup_(sd_dhcp_option_unrefp
) sd_dhcp_option
*opt4
= NULL
, *old4
= NULL
;
390 _cleanup_(sd_dhcp6_option_unrefp
) sd_dhcp6_option
*opt6
= NULL
, *old6
= NULL
;
391 uint32_t uint32_data
, enterprise_identifier
= 0;
392 _cleanup_free_
char *word
= NULL
, *q
= NULL
;
393 OrderedHashmap
**options
= data
;
394 uint16_t u16
, uint16_data
;
395 union in_addr_union addr
;
396 DHCPOptionDataType type
;
397 uint8_t u8
, uint8_data
;
408 if (isempty(rvalue
)) {
409 *options
= ordered_hashmap_free(*options
);
414 if (ltype
== AF_INET6
&& streq(lvalue
, "SendVendorOption")) {
415 r
= extract_first_word(&p
, &word
, ":", 0);
418 if (r
<= 0 || isempty(p
)) {
419 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
420 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
424 r
= safe_atou32(word
, &enterprise_identifier
);
426 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
427 "Failed to parse DHCPv6 enterprise identifier data, ignoring assignment: %s", p
);
433 r
= extract_first_word(&p
, &word
, ":", 0);
436 if (r
<= 0 || isempty(p
)) {
437 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
438 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
442 if (ltype
== AF_INET6
) {
443 r
= safe_atou16(word
, &u16
);
445 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
446 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
449 if (u16
< 1 || u16
>= UINT16_MAX
) {
450 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
451 "Invalid DHCP option, valid range is 1-65535, ignoring assignment: %s", rvalue
);
455 r
= safe_atou8(word
, &u8
);
457 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
458 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
461 if (u8
< 1 || u8
>= UINT8_MAX
) {
462 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
463 "Invalid DHCP option, valid range is 1-254, ignoring assignment: %s", rvalue
);
469 r
= extract_first_word(&p
, &word
, ":", 0);
472 if (r
<= 0 || isempty(p
)) {
473 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
474 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
478 type
= dhcp_option_data_type_from_string(word
);
480 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
481 "Invalid DHCP option data type, ignoring assignment: %s", p
);
486 case DHCP_OPTION_DATA_UINT8
:{
487 r
= safe_atou8(p
, &uint8_data
);
489 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
490 "Failed to parse DHCP uint8 data, ignoring assignment: %s", p
);
495 sz
= sizeof(uint8_t);
498 case DHCP_OPTION_DATA_UINT16
:{
499 r
= safe_atou16(p
, &uint16_data
);
501 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
502 "Failed to parse DHCP uint16 data, ignoring assignment: %s", p
);
506 udata
= &uint16_data
;
507 sz
= sizeof(uint16_t);
510 case DHCP_OPTION_DATA_UINT32
: {
511 r
= safe_atou32(p
, &uint32_data
);
513 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
514 "Failed to parse DHCP uint32 data, ignoring assignment: %s", p
);
518 udata
= &uint32_data
;
519 sz
= sizeof(uint32_t);
523 case DHCP_OPTION_DATA_IPV4ADDRESS
: {
524 r
= in_addr_from_string(AF_INET
, p
, &addr
);
526 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
527 "Failed to parse DHCP ipv4address data, ignoring assignment: %s", p
);
532 sz
= sizeof(addr
.in
.s_addr
);
535 case DHCP_OPTION_DATA_IPV6ADDRESS
: {
536 r
= in_addr_from_string(AF_INET6
, p
, &addr
);
538 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
539 "Failed to parse DHCP ipv6address data, ignoring assignment: %s", p
);
544 sz
= sizeof(addr
.in6
.s6_addr
);
547 case DHCP_OPTION_DATA_STRING
:
548 sz
= cunescape(p
, UNESCAPE_ACCEPT_NUL
, &q
);
550 log_syntax(unit
, LOG_WARNING
, filename
, line
, sz
,
551 "Failed to decode DHCP option data, ignoring assignment: %s", p
);
560 if (ltype
== AF_INET6
) {
561 r
= sd_dhcp6_option_new(u16
, udata
, sz
, enterprise_identifier
, &opt6
);
563 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
564 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
568 r
= ordered_hashmap_ensure_allocated(options
, &dhcp6_option_hash_ops
);
572 /* Overwrite existing option */
573 old6
= ordered_hashmap_get(*options
, UINT_TO_PTR(u16
));
574 r
= ordered_hashmap_replace(*options
, UINT_TO_PTR(u16
), opt6
);
576 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
577 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
582 r
= sd_dhcp_option_new(u8
, udata
, sz
, &opt4
);
584 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
585 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
589 r
= ordered_hashmap_ensure_allocated(options
, &dhcp_option_hash_ops
);
593 /* Overwrite existing option */
594 old4
= ordered_hashmap_get(*options
, UINT_TO_PTR(u8
));
595 r
= ordered_hashmap_replace(*options
, UINT_TO_PTR(u8
), opt4
);
597 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
598 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
606 int config_parse_dhcp_request_options(
608 const char *filename
,
611 unsigned section_line
,
618 Network
*network
= data
;
627 if (isempty(rvalue
)) {
628 if (ltype
== AF_INET
)
629 network
->dhcp_request_options
= set_free(network
->dhcp_request_options
);
631 network
->dhcp6_request_options
= set_free(network
->dhcp6_request_options
);
637 _cleanup_free_
char *n
= NULL
;
640 r
= extract_first_word(&p
, &n
, NULL
, 0);
644 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
645 "Failed to parse DHCP request option, ignoring assignment: %s",
652 r
= safe_atou32(n
, &i
);
654 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
655 "DHCP request option is invalid, ignoring assignment: %s", n
);
659 if (i
< 1 || i
>= UINT8_MAX
) {
660 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
661 "DHCP request option is invalid, valid range is 1-254, ignoring assignment: %s", n
);
665 r
= set_ensure_put(ltype
== AF_INET
? &network
->dhcp_request_options
: &network
->dhcp6_request_options
,
666 NULL
, UINT32_TO_PTR(i
));
668 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
669 "Failed to store DHCP request option '%s', ignoring assignment: %m", n
);
673 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains
, dhcp_use_domains
, DHCPUseDomains
,
674 "Failed to parse DHCP use domains setting");
676 static const char* const dhcp_use_domains_table
[_DHCP_USE_DOMAINS_MAX
] = {
677 [DHCP_USE_DOMAINS_NO
] = "no",
678 [DHCP_USE_DOMAINS_ROUTE
] = "route",
679 [DHCP_USE_DOMAINS_YES
] = "yes",
682 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains
, DHCPUseDomains
, DHCP_USE_DOMAINS_YES
);
684 static const char * const dhcp_option_data_type_table
[_DHCP_OPTION_DATA_MAX
] = {
685 [DHCP_OPTION_DATA_UINT8
] = "uint8",
686 [DHCP_OPTION_DATA_UINT16
] = "uint16",
687 [DHCP_OPTION_DATA_UINT32
] = "uint32",
688 [DHCP_OPTION_DATA_STRING
] = "string",
689 [DHCP_OPTION_DATA_IPV4ADDRESS
] = "ipv4address",
690 [DHCP_OPTION_DATA_IPV6ADDRESS
] = "ipv6address",
693 DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type
, DHCPOptionDataType
);