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"
14 int config_parse_dhcp(
19 unsigned section_line
,
26 AddressFamily
*dhcp
= data
, s
;
33 /* Note that this is mostly like
34 * config_parse_address_family(), except that it
35 * understands some old names for the enum values */
37 s
= address_family_from_string(rvalue
);
40 /* Previously, we had a slightly different enum here,
41 * support its values for compatibility. */
43 if (streq(rvalue
, "none"))
44 s
= ADDRESS_FAMILY_NO
;
45 else if (streq(rvalue
, "v4"))
46 s
= ADDRESS_FAMILY_IPV4
;
47 else if (streq(rvalue
, "v6"))
48 s
= ADDRESS_FAMILY_IPV6
;
49 else if (streq(rvalue
, "both"))
50 s
= ADDRESS_FAMILY_YES
;
52 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
53 "Failed to parse DHCP option, ignoring: %s", rvalue
);
57 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
58 "DHCP=%s is deprecated, please use DHCP=%s instead.",
59 rvalue
, address_family_to_string(s
));
66 int config_parse_dhcp_use_dns(
71 unsigned section_line
,
78 Network
*network
= data
;
86 r
= parse_boolean(rvalue
);
88 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
89 "Failed to parse UseDNS=%s, ignoring assignment: %m", rvalue
);
93 network
->dhcp_use_dns
= r
;
94 network
->dhcp6_use_dns
= r
;
99 int config_parse_dhcp_use_sip(
101 const char *filename
,
104 unsigned section_line
,
111 Network
*network
= data
;
119 r
= parse_boolean(rvalue
);
121 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
122 "Failed to parse UseSIP=%s, ignoring assignment: %m", rvalue
);
126 network
->dhcp_use_sip
= r
;
131 int config_parse_dhcp_use_ntp(
133 const char *filename
,
136 unsigned section_line
,
143 Network
*network
= data
;
151 r
= parse_boolean(rvalue
);
153 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
154 "Failed to parse UseNTP=%s, ignoring assignment: %m", rvalue
);
158 network
->dhcp_use_ntp
= r
;
159 network
->dhcp6_use_ntp
= r
;
164 int config_parse_section_route_table(
166 const char *filename
,
169 unsigned section_line
,
176 Network
*network
= data
;
185 r
= safe_atou32(rvalue
, &rt
);
187 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
188 "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue
);
192 if (STRPTR_IN_SET(section
, "DHCP", "DHCPv4")) {
193 network
->dhcp_route_table
= rt
;
194 network
->dhcp_route_table_set
= true;
195 } else { /* section is IPv6AcceptRA */
196 network
->ipv6_accept_ra_route_table
= rt
;
197 network
->ipv6_accept_ra_route_table_set
= true;
203 int config_parse_iaid(const char *unit
,
204 const char *filename
,
207 unsigned section_line
,
213 Network
*network
= data
;
222 r
= safe_atou32(rvalue
, &iaid
);
224 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
225 "Unable to read IAID, ignoring assignment: %s", rvalue
);
229 network
->iaid
= iaid
;
230 network
->iaid_set
= true;
235 int config_parse_dhcp6_pd_hint(
237 const char *filename
,
240 unsigned section_line
,
247 Network
*network
= data
;
255 r
= in_addr_prefix_from_string(rvalue
, AF_INET6
, (union in_addr_union
*) &network
->dhcp6_pd_address
, &network
->dhcp6_pd_length
);
257 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse PrefixDelegationHint=%s, ignoring assignment", rvalue
);
261 if (network
->dhcp6_pd_length
< 1 || network
->dhcp6_pd_length
> 128) {
262 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid prefix length='%d', ignoring assignment", network
->dhcp6_pd_length
);
263 network
->dhcp6_pd_length
= 0;
270 int config_parse_dhcp_user_class(
272 const char *filename
,
275 unsigned section_line
,
289 if (isempty(rvalue
)) {
295 _cleanup_free_
char *w
= NULL
;
297 r
= extract_first_word(&rvalue
, &w
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_UNQUOTE
);
301 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
302 "Failed to split user classes option, ignoring: %s", rvalue
);
308 if (ltype
== AF_INET
) {
309 if (strlen(w
) > UINT8_MAX
) {
310 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
311 "%s length is not in the range 1-255, ignoring.", w
);
315 if (strlen(w
) > UINT16_MAX
) {
316 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
317 "%s length is not in the range 1-65535, ignoring.", w
);
332 int config_parse_dhcp_vendor_class(
334 const char *filename
,
337 unsigned section_line
,
350 if (isempty(rvalue
)) {
356 _cleanup_free_
char *w
= NULL
;
358 r
= extract_first_word(&rvalue
, &w
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_UNQUOTE
);
362 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
363 "Failed to split vendor classes option, ignoring: %s", rvalue
);
369 if (strlen(w
) > UINT8_MAX
) {
370 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
371 "%s length is not in the range 1-255, ignoring.", w
);
385 int config_parse_dhcp6_mud_url(
387 const char *filename
,
390 unsigned section_line
,
396 _cleanup_free_
char *unescaped
= NULL
;
397 Network
*network
= data
;
404 if (isempty(rvalue
)) {
405 network
->dhcp6_mudurl
= mfree(network
->dhcp6_mudurl
);
409 r
= cunescape(rvalue
, 0, &unescaped
);
411 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
412 "Failed to Failed to unescape MUD URL, ignoring: %s", rvalue
);
416 if (!http_url_is_valid(unescaped
) || strlen(unescaped
) > UINT8_MAX
) {
417 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
418 "Failed to parse MUD URL '%s', ignoring: %m", rvalue
);
423 return free_and_replace(network
->dhcp6_mudurl
, unescaped
);
426 int config_parse_dhcp_send_option(
428 const char *filename
,
431 unsigned section_line
,
438 _cleanup_(sd_dhcp_option_unrefp
) sd_dhcp_option
*opt4
= NULL
, *old4
= NULL
;
439 _cleanup_(sd_dhcp6_option_unrefp
) sd_dhcp6_option
*opt6
= NULL
, *old6
= NULL
;
440 _cleanup_free_
char *word
= NULL
, *q
= NULL
;
441 OrderedHashmap
**options
= data
;
442 union in_addr_union addr
;
443 DHCPOptionDataType type
;
444 uint8_t u8
, uint8_data
;
445 uint16_t u16
, uint16_data
;
446 uint32_t uint32_data
;
457 if (isempty(rvalue
)) {
458 *options
= ordered_hashmap_free(*options
);
463 r
= extract_first_word(&p
, &word
, ":", 0);
467 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
468 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
472 if (ltype
== AF_INET6
) {
473 r
= safe_atou16(word
, &u16
);
475 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
476 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
479 if (u16
< 1 || u16
>= UINT16_MAX
) {
480 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
481 "Invalid DHCP option, valid range is 1-65535, ignoring assignment: %s", rvalue
);
485 r
= safe_atou8(word
, &u8
);
487 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
488 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
491 if (u8
< 1 || u8
>= UINT8_MAX
) {
492 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
493 "Invalid DHCP option, valid range is 1-254, ignoring assignment: %s", rvalue
);
499 r
= extract_first_word(&p
, &word
, ":", 0);
502 if (r
<= 0 || isempty(p
)) {
503 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
504 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
508 type
= dhcp_option_data_type_from_string(word
);
510 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
511 "Invalid DHCP option data type, ignoring assignment: %s", p
);
516 case DHCP_OPTION_DATA_UINT8
:{
517 r
= safe_atou8(p
, &uint8_data
);
519 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
520 "Failed to parse DHCP uint8 data, ignoring assignment: %s", p
);
525 sz
= sizeof(uint8_t);
528 case DHCP_OPTION_DATA_UINT16
:{
529 r
= safe_atou16(p
, &uint16_data
);
531 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
532 "Failed to parse DHCP uint16 data, ignoring assignment: %s", p
);
536 udata
= &uint16_data
;
537 sz
= sizeof(uint16_t);
540 case DHCP_OPTION_DATA_UINT32
: {
541 r
= safe_atou32(p
, &uint32_data
);
543 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
544 "Failed to parse DHCP uint32 data, ignoring assignment: %s", p
);
548 udata
= &uint32_data
;
549 sz
= sizeof(uint32_t);
553 case DHCP_OPTION_DATA_IPV4ADDRESS
: {
554 r
= in_addr_from_string(AF_INET
, p
, &addr
);
556 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
557 "Failed to parse DHCP ipv4address data, ignoring assignment: %s", p
);
562 sz
= sizeof(addr
.in
.s_addr
);
565 case DHCP_OPTION_DATA_IPV6ADDRESS
: {
566 r
= in_addr_from_string(AF_INET6
, p
, &addr
);
568 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
569 "Failed to parse DHCP ipv6address data, ignoring assignment: %s", p
);
574 sz
= sizeof(addr
.in6
.s6_addr
);
577 case DHCP_OPTION_DATA_STRING
:
578 sz
= cunescape(p
, UNESCAPE_ACCEPT_NUL
, &q
);
580 log_syntax(unit
, LOG_ERR
, filename
, line
, sz
,
581 "Failed to decode DHCP option data, ignoring assignment: %s", p
);
590 if (ltype
== AF_INET6
) {
591 r
= sd_dhcp6_option_new(u16
, udata
, sz
, &opt6
);
593 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
594 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
598 r
= ordered_hashmap_ensure_allocated(options
, &dhcp6_option_hash_ops
);
602 /* Overwrite existing option */
603 old6
= ordered_hashmap_get(*options
, UINT_TO_PTR(u16
));
604 r
= ordered_hashmap_replace(*options
, UINT_TO_PTR(u16
), opt6
);
606 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
607 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
612 r
= sd_dhcp_option_new(u8
, udata
, sz
, &opt4
);
614 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
615 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
619 r
= ordered_hashmap_ensure_allocated(options
, &dhcp_option_hash_ops
);
623 /* Overwrite existing option */
624 old4
= ordered_hashmap_get(*options
, UINT_TO_PTR(u8
));
625 r
= ordered_hashmap_replace(*options
, UINT_TO_PTR(u8
), opt4
);
627 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
628 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
636 int config_parse_dhcp_request_options(
638 const char *filename
,
641 unsigned section_line
,
648 Network
*network
= data
;
657 if (isempty(rvalue
)) {
658 if (ltype
== AF_INET
)
659 network
->dhcp_request_options
= set_free(network
->dhcp_request_options
);
661 network
->dhcp6_request_options
= set_free(network
->dhcp6_request_options
);
667 _cleanup_free_
char *n
= NULL
;
670 r
= extract_first_word(&p
, &n
, NULL
, 0);
672 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
673 "Failed to parse DHCP request option, ignoring assignment: %s",
680 r
= safe_atou32(n
, &i
);
682 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
683 "DHCP request option is invalid, ignoring assignment: %s", n
);
687 if (i
< 1 || i
>= UINT8_MAX
) {
688 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
689 "DHCP request option is invalid, valid range is 1-254, ignoring assignment: %s", n
);
693 if (ltype
== AF_INET
)
694 r
= set_ensure_allocated(&network
->dhcp_request_options
, NULL
);
696 r
= set_ensure_allocated(&network
->dhcp6_request_options
, NULL
);
700 if (ltype
== AF_INET
)
701 r
= set_put(network
->dhcp_request_options
, UINT32_TO_PTR(i
));
703 r
= set_put(network
->dhcp6_request_options
, UINT32_TO_PTR(i
));
705 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
706 "Failed to store DHCP request option '%s', ignoring assignment: %m", n
);
712 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains
, dhcp_use_domains
, DHCPUseDomains
,
713 "Failed to parse DHCP use domains setting");
715 static const char* const dhcp_use_domains_table
[_DHCP_USE_DOMAINS_MAX
] = {
716 [DHCP_USE_DOMAINS_NO
] = "no",
717 [DHCP_USE_DOMAINS_ROUTE
] = "route",
718 [DHCP_USE_DOMAINS_YES
] = "yes",
721 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains
, DHCPUseDomains
, DHCP_USE_DOMAINS_YES
);
723 static const char * const dhcp_option_data_type_table
[_DHCP_OPTION_DATA_MAX
] = {
724 [DHCP_OPTION_DATA_UINT8
] = "uint8",
725 [DHCP_OPTION_DATA_UINT16
] = "uint16",
726 [DHCP_OPTION_DATA_UINT32
] = "uint32",
727 [DHCP_OPTION_DATA_STRING
] = "string",
728 [DHCP_OPTION_DATA_IPV4ADDRESS
] = "ipv4address",
729 [DHCP_OPTION_DATA_IPV6ADDRESS
] = "ipv6address",
732 DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type
, DHCPOptionDataType
);