1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <netinet/in.h>
4 #include <linux/if_arp.h>
7 #include "dhcp-internal.h"
8 #include "dhcp6-internal.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"
20 bool link_dhcp_enabled(Link
*link
, int family
) {
22 assert(IN_SET(family
, AF_INET
, AF_INET6
));
24 if (family
== AF_INET6
&& !socket_ipv6_is_supported())
27 if (link
->flags
& IFF_LOOPBACK
)
30 if (link
->iftype
== ARPHRD_CAN
)
36 return link
->network
->dhcp
& (family
== AF_INET
? ADDRESS_FAMILY_IPV4
: ADDRESS_FAMILY_IPV6
);
39 void network_adjust_dhcp(Network
*network
) {
41 assert(network
->dhcp
>= 0);
43 if (network
->dhcp
== ADDRESS_FAMILY_NO
)
46 /* Bonding slave does not support addressing. */
48 log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.",
50 network
->dhcp
= ADDRESS_FAMILY_NO
;
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);
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
;
71 return &link
->manager
->duid
;
74 static int duid_set_uuid(DUID
*duid
, sd_id128_t uuid
) {
77 if (duid
->raw_data_len
> 0)
80 if (duid
->type
!= DUID_TYPE_UUID
)
83 memcpy(&duid
->raw_data
, &uuid
, sizeof(sd_id128_t
));
84 duid
->raw_data_len
= sizeof(sd_id128_t
);
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
;
101 e
= sd_bus_message_get_error(m
);
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
));
109 r
= sd_bus_message_read_array(m
, 'y', &a
, &sz
);
111 log_warning_errno(r
, "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m");
115 if (sz
!= sizeof(sd_id128_t
)) {
116 log_warning("Invalid product UUID. Falling back to use machine-app-specific ID as DUID-UUID.");
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
);
124 manager
->duids_requesting_uuid
= set_free(manager
->duids_requesting_uuid
);
127 while ((link
= set_steal_first(manager
->links_requesting_uuid
))) {
130 r
= link_configure(link
);
132 link_enter_failed(link
);
135 manager
->links_requesting_uuid
= set_free(manager
->links_requesting_uuid
);
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;
144 int manager_request_product_uuid(Manager
*m
, Link
*link
) {
149 if (m
->has_product_uuid
)
152 log_debug("Requesting product UUID");
157 assert_se(duid
= link_get_duid(link
));
159 r
= set_ensure_put(&m
->links_requesting_uuid
, NULL
, link
);
165 r
= set_ensure_put(&m
->duids_requesting_uuid
, NULL
, duid
);
170 if (!m
->bus
|| sd_bus_is_ready(m
->bus
) <= 0) {
171 log_debug("Not connected to system bus, requesting product UUID later.");
175 r
= sd_bus_call_method_async(
178 "org.freedesktop.hostname1",
179 "/org/freedesktop/hostname1",
180 "org.freedesktop.hostname1",
182 get_product_uuid_handler
,
187 return log_warning_errno(r
, "Failed to get product UUID: %m");
192 static bool link_requires_uuid(Link
*link
) {
196 assert(link
->manager
);
197 assert(link
->network
);
199 duid
= link_get_duid(link
);
200 if (duid
->type
!= DUID_TYPE_UUID
|| duid
->raw_data_len
!= 0)
203 if (link_dhcp4_enabled(link
) && IN_SET(link
->network
->dhcp_client_identifier
, DHCP_CLIENT_ID_DUID
, DHCP_CLIENT_ID_DUID_ONLY
))
206 if (link_dhcp6_enabled(link
) || link_ipv6_accept_ra_enabled(link
))
212 int link_configure_duid(Link
*link
) {
218 assert(link
->manager
);
219 assert(link
->network
);
222 duid
= link_get_duid(link
);
224 if (!link_requires_uuid(link
))
227 if (m
->has_product_uuid
) {
228 (void) duid_set_uuid(duid
, m
->product_uuid
);
232 if (!m
->links_requesting_uuid
) {
233 r
= manager_request_product_uuid(m
, link
);
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");
243 r
= set_put(m
->links_requesting_uuid
, link
);
249 r
= set_put(m
->duids_requesting_uuid
, duid
);
257 int config_parse_dhcp(
259 const char *filename
,
262 unsigned section_line
,
269 AddressFamily
*dhcp
= data
, s
;
276 /* Note that this is mostly like
277 * config_parse_address_family(), except that it
278 * understands some old names for the enum values */
280 s
= address_family_from_string(rvalue
);
283 /* Previously, we had a slightly different enum here,
284 * support its values for compatibility. */
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
;
295 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
296 "Failed to parse DHCP option, ignoring: %s", rvalue
);
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
));
309 int config_parse_dhcp_route_metric(
311 const char *filename
,
314 unsigned section_line
,
321 Network
*network
= data
;
330 r
= safe_atou32(rvalue
, &metric
);
332 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
333 "Failed to parse RouteMetric=%s, ignoring assignment: %m", rvalue
);
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
;
353 int config_parse_dhcp_use_dns(
355 const char *filename
,
358 unsigned section_line
,
365 Network
*network
= data
;
373 r
= parse_boolean(rvalue
);
375 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
376 "Failed to parse UseDNS=%s, ignoring assignment: %m", rvalue
);
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
;
396 int config_parse_dhcp_use_ntp(
398 const char *filename
,
401 unsigned section_line
,
408 Network
*network
= data
;
416 r
= parse_boolean(rvalue
);
418 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
419 "Failed to parse UseNTP=%s, ignoring assignment: %m", rvalue
);
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
;
439 int config_parse_section_route_table(
441 const char *filename
,
444 unsigned section_line
,
451 Network
*network
= data
;
460 r
= safe_atou32(rvalue
, &rt
);
462 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
463 "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue
);
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;
478 int config_parse_iaid(const char *unit
,
479 const char *filename
,
482 unsigned section_line
,
488 Network
*network
= data
;
497 r
= safe_atou32(rvalue
, &iaid
);
499 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
500 "Unable to read IAID, ignoring assignment: %s", rvalue
);
504 network
->iaid
= iaid
;
505 network
->iaid_set
= true;
510 int config_parse_dhcp_user_or_vendor_class(
512 const char *filename
,
515 unsigned section_line
,
528 assert(IN_SET(ltype
, AF_INET
, AF_INET6
));
530 if (isempty(rvalue
)) {
535 for (const char *p
= rvalue
;;) {
536 _cleanup_free_
char *w
= NULL
;
539 r
= extract_first_word(&p
, &w
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_UNQUOTE
);
543 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
544 "Failed to split user classes option, ignoring: %s", rvalue
);
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
);
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
);
565 r
= strv_consume(l
, TAKE_PTR(w
));
571 int config_parse_dhcp_send_option(
573 const char *filename
,
576 unsigned section_line
,
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
;
602 if (isempty(rvalue
)) {
603 *options
= ordered_hashmap_free(*options
);
608 if (ltype
== AF_INET6
&& streq(lvalue
, "SendVendorOption")) {
609 r
= extract_first_word(&p
, &word
, ":", 0);
612 if (r
<= 0 || isempty(p
)) {
613 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
614 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
618 r
= safe_atou32(word
, &enterprise_identifier
);
620 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
621 "Failed to parse DHCPv6 enterprise identifier data, ignoring assignment: %s", p
);
627 r
= extract_first_word(&p
, &word
, ":", 0);
630 if (r
<= 0 || isempty(p
)) {
631 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
632 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
636 if (ltype
== AF_INET6
) {
637 r
= safe_atou16(word
, &u16
);
639 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
640 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
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
);
649 r
= safe_atou8(word
, &u8
);
651 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
652 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
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
);
663 r
= extract_first_word(&p
, &word
, ":", 0);
666 if (r
<= 0 || isempty(p
)) {
667 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
668 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
672 type
= dhcp_option_data_type_from_string(word
);
674 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
675 "Invalid DHCP option data type, ignoring assignment: %s", p
);
680 case DHCP_OPTION_DATA_UINT8
:{
681 r
= safe_atou8(p
, &uint8_data
);
683 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
684 "Failed to parse DHCP uint8 data, ignoring assignment: %s", p
);
689 sz
= sizeof(uint8_t);
692 case DHCP_OPTION_DATA_UINT16
:{
693 r
= safe_atou16(p
, &uint16_data
);
695 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
696 "Failed to parse DHCP uint16 data, ignoring assignment: %s", p
);
700 udata
= &uint16_data
;
701 sz
= sizeof(uint16_t);
704 case DHCP_OPTION_DATA_UINT32
: {
705 r
= safe_atou32(p
, &uint32_data
);
707 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
708 "Failed to parse DHCP uint32 data, ignoring assignment: %s", p
);
712 udata
= &uint32_data
;
713 sz
= sizeof(uint32_t);
717 case DHCP_OPTION_DATA_IPV4ADDRESS
: {
718 r
= in_addr_from_string(AF_INET
, p
, &addr
);
720 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
721 "Failed to parse DHCP ipv4address data, ignoring assignment: %s", p
);
726 sz
= sizeof(addr
.in
.s_addr
);
729 case DHCP_OPTION_DATA_IPV6ADDRESS
: {
730 r
= in_addr_from_string(AF_INET6
, p
, &addr
);
732 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
733 "Failed to parse DHCP ipv6address data, ignoring assignment: %s", p
);
738 sz
= sizeof(addr
.in6
.s6_addr
);
741 case DHCP_OPTION_DATA_STRING
:
742 sz
= cunescape(p
, UNESCAPE_ACCEPT_NUL
, &q
);
744 log_syntax(unit
, LOG_WARNING
, filename
, line
, sz
,
745 "Failed to decode DHCP option data, ignoring assignment: %s", p
);
753 if (ltype
== AF_INET6
) {
754 r
= sd_dhcp6_option_new(u16
, udata
, sz
, enterprise_identifier
, &opt6
);
756 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
757 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
761 r
= ordered_hashmap_ensure_allocated(options
, &dhcp6_option_hash_ops
);
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
);
769 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
770 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
775 r
= sd_dhcp_option_new(u8
, udata
, sz
, &opt4
);
777 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
778 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
782 r
= ordered_hashmap_ensure_allocated(options
, &dhcp_option_hash_ops
);
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
);
790 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
791 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
799 int config_parse_dhcp_request_options(
801 const char *filename
,
804 unsigned section_line
,
811 Network
*network
= data
;
819 if (isempty(rvalue
)) {
820 if (ltype
== AF_INET
)
821 network
->dhcp_request_options
= set_free(network
->dhcp_request_options
);
823 network
->dhcp6_request_options
= set_free(network
->dhcp6_request_options
);
828 for (const char *p
= rvalue
;;) {
829 _cleanup_free_
char *n
= NULL
;
832 r
= extract_first_word(&p
, &n
, NULL
, 0);
836 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
837 "Failed to parse DHCP request option, ignoring assignment: %s",
844 r
= safe_atou32(n
, &i
);
846 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
847 "DHCP request option is invalid, ignoring assignment: %s", n
);
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
);
857 r
= set_ensure_put(ltype
== AF_INET
? &network
->dhcp_request_options
: &network
->dhcp6_request_options
,
858 NULL
, UINT32_TO_PTR(i
));
860 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
861 "Failed to store DHCP request option '%s', ignoring assignment: %m", n
);
865 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains
, dhcp_use_domains
, DHCPUseDomains
,
866 "Failed to parse DHCP use domains setting");
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",
874 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains
, DHCPUseDomains
, DHCP_USE_DOMAINS_YES
);
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",
885 DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type
, DHCPOptionDataType
);