1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <netinet/in.h>
4 #include <linux/if_arp.h>
6 #include "dhcp-internal.h"
7 #include "dhcp6-internal.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"
19 bool link_dhcp_enabled(Link
*link
, int family
) {
21 assert(IN_SET(family
, AF_INET
, AF_INET6
));
23 if (family
== AF_INET6
&& !socket_ipv6_is_supported())
26 if (link
->flags
& IFF_LOOPBACK
)
29 if (link
->iftype
== ARPHRD_CAN
)
35 return link
->network
->dhcp
& (family
== AF_INET
? ADDRESS_FAMILY_IPV4
: ADDRESS_FAMILY_IPV6
);
38 void network_adjust_dhcp(Network
*network
) {
40 assert(network
->dhcp
>= 0);
42 if (network
->dhcp
== ADDRESS_FAMILY_NO
)
45 /* Bonding slave does not support addressing. */
47 log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.",
49 network
->dhcp
= ADDRESS_FAMILY_NO
;
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);
61 DUID
* link_get_duid(Link
*link
) {
62 if (link
->network
->duid
.type
!= _DUID_TYPE_INVALID
)
63 return &link
->network
->duid
;
65 return &link
->manager
->duid
;
68 static int duid_set_uuid(DUID
*duid
, sd_id128_t uuid
) {
71 if (duid
->raw_data_len
> 0)
74 if (duid
->type
!= DUID_TYPE_UUID
)
77 memcpy(&duid
->raw_data
, &uuid
, sizeof(sd_id128_t
));
78 duid
->raw_data_len
= sizeof(sd_id128_t
);
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
;
95 e
= sd_bus_message_get_error(m
);
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",
103 r
= sd_bus_message_read_array(m
, 'y', &a
, &sz
);
107 if (sz
!= sizeof(sd_id128_t
)) {
108 log_error("Invalid product UUID. Falling back to use machine-app-specific ID as DUID-UUID.");
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
);
116 manager
->duids_requesting_uuid
= set_free(manager
->duids_requesting_uuid
);
119 while ((link
= set_steal_first(manager
->links_requesting_uuid
))) {
122 r
= link_configure(link
);
124 link_enter_failed(link
);
127 manager
->links_requesting_uuid
= set_free(manager
->links_requesting_uuid
);
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;
136 int manager_request_product_uuid(Manager
*m
, Link
*link
) {
141 if (m
->has_product_uuid
)
144 log_debug("Requesting product UUID");
149 assert_se(duid
= link_get_duid(link
));
151 r
= set_ensure_put(&m
->links_requesting_uuid
, NULL
, link
);
157 r
= set_ensure_put(&m
->duids_requesting_uuid
, NULL
, duid
);
162 if (!m
->bus
|| sd_bus_is_ready(m
->bus
) <= 0) {
163 log_debug("Not connected to system bus, requesting product UUID later.");
167 r
= sd_bus_call_method_async(
170 "org.freedesktop.hostname1",
171 "/org/freedesktop/hostname1",
172 "org.freedesktop.hostname1",
174 get_product_uuid_handler
,
179 return log_warning_errno(r
, "Failed to get product UUID: %m");
184 static bool link_requires_uuid(Link
*link
) {
188 assert(link
->manager
);
189 assert(link
->network
);
191 duid
= link_get_duid(link
);
192 if (duid
->type
!= DUID_TYPE_UUID
|| duid
->raw_data_len
!= 0)
195 if (link_dhcp4_enabled(link
) && IN_SET(link
->network
->dhcp_client_identifier
, DHCP_CLIENT_ID_DUID
, DHCP_CLIENT_ID_DUID_ONLY
))
198 if (link_dhcp6_enabled(link
) || link_ipv6_accept_ra_enabled(link
))
204 int link_configure_duid(Link
*link
) {
210 assert(link
->manager
);
211 assert(link
->network
);
214 duid
= link_get_duid(link
);
216 if (!link_requires_uuid(link
))
219 if (m
->has_product_uuid
) {
220 (void) duid_set_uuid(duid
, m
->product_uuid
);
224 if (!m
->links_requesting_uuid
) {
225 r
= manager_request_product_uuid(m
, link
);
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");
235 r
= set_put(m
->links_requesting_uuid
, link
);
241 r
= set_put(m
->duids_requesting_uuid
, duid
);
249 int config_parse_dhcp(
251 const char *filename
,
254 unsigned section_line
,
261 AddressFamily
*dhcp
= data
, s
;
268 /* Note that this is mostly like
269 * config_parse_address_family(), except that it
270 * understands some old names for the enum values */
272 s
= address_family_from_string(rvalue
);
275 /* Previously, we had a slightly different enum here,
276 * support its values for compatibility. */
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
;
287 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
288 "Failed to parse DHCP option, ignoring: %s", rvalue
);
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
));
301 int config_parse_dhcp_route_metric(
303 const char *filename
,
306 unsigned section_line
,
313 Network
*network
= data
;
322 r
= safe_atou32(rvalue
, &metric
);
324 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
325 "Failed to parse RouteMetric=%s, ignoring assignment: %m", rvalue
);
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
;
345 int config_parse_dhcp_use_dns(
347 const char *filename
,
350 unsigned section_line
,
357 Network
*network
= data
;
365 r
= parse_boolean(rvalue
);
367 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
368 "Failed to parse UseDNS=%s, ignoring assignment: %m", rvalue
);
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
;
388 int config_parse_dhcp_use_ntp(
390 const char *filename
,
393 unsigned section_line
,
400 Network
*network
= data
;
408 r
= parse_boolean(rvalue
);
410 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
411 "Failed to parse UseNTP=%s, ignoring assignment: %m", rvalue
);
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
;
431 int config_parse_section_route_table(
433 const char *filename
,
436 unsigned section_line
,
443 Network
*network
= data
;
452 r
= safe_atou32(rvalue
, &rt
);
454 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
455 "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue
);
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;
470 int config_parse_iaid(const char *unit
,
471 const char *filename
,
474 unsigned section_line
,
480 Network
*network
= data
;
489 r
= safe_atou32(rvalue
, &iaid
);
491 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
492 "Unable to read IAID, ignoring assignment: %s", rvalue
);
496 network
->iaid
= iaid
;
497 network
->iaid_set
= true;
502 int config_parse_dhcp_user_class(
504 const char *filename
,
507 unsigned section_line
,
521 if (isempty(rvalue
)) {
526 for (const char *p
= rvalue
;;) {
527 _cleanup_free_
char *w
= NULL
;
529 r
= extract_first_word(&p
, &w
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_UNQUOTE
);
533 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
534 "Failed to split user classes option, ignoring: %s", rvalue
);
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
);
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
);
562 int config_parse_dhcp_vendor_class(
564 const char *filename
,
567 unsigned section_line
,
580 if (isempty(rvalue
)) {
585 for (const char *p
= rvalue
;;) {
586 _cleanup_free_
char *w
= NULL
;
588 r
= extract_first_word(&p
, &w
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_UNQUOTE
);
592 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
593 "Failed to split vendor classes option, ignoring: %s", rvalue
);
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
);
613 int config_parse_dhcp_send_option(
615 const char *filename
,
618 unsigned section_line
,
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
;
644 if (isempty(rvalue
)) {
645 *options
= ordered_hashmap_free(*options
);
650 if (ltype
== AF_INET6
&& streq(lvalue
, "SendVendorOption")) {
651 r
= extract_first_word(&p
, &word
, ":", 0);
654 if (r
<= 0 || isempty(p
)) {
655 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
656 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
660 r
= safe_atou32(word
, &enterprise_identifier
);
662 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
663 "Failed to parse DHCPv6 enterprise identifier data, ignoring assignment: %s", p
);
669 r
= extract_first_word(&p
, &word
, ":", 0);
672 if (r
<= 0 || isempty(p
)) {
673 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
674 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
678 if (ltype
== AF_INET6
) {
679 r
= safe_atou16(word
, &u16
);
681 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
682 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
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
);
691 r
= safe_atou8(word
, &u8
);
693 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
694 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
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
);
705 r
= extract_first_word(&p
, &word
, ":", 0);
708 if (r
<= 0 || isempty(p
)) {
709 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
710 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
714 type
= dhcp_option_data_type_from_string(word
);
716 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
717 "Invalid DHCP option data type, ignoring assignment: %s", p
);
722 case DHCP_OPTION_DATA_UINT8
:{
723 r
= safe_atou8(p
, &uint8_data
);
725 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
726 "Failed to parse DHCP uint8 data, ignoring assignment: %s", p
);
731 sz
= sizeof(uint8_t);
734 case DHCP_OPTION_DATA_UINT16
:{
735 r
= safe_atou16(p
, &uint16_data
);
737 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
738 "Failed to parse DHCP uint16 data, ignoring assignment: %s", p
);
742 udata
= &uint16_data
;
743 sz
= sizeof(uint16_t);
746 case DHCP_OPTION_DATA_UINT32
: {
747 r
= safe_atou32(p
, &uint32_data
);
749 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
750 "Failed to parse DHCP uint32 data, ignoring assignment: %s", p
);
754 udata
= &uint32_data
;
755 sz
= sizeof(uint32_t);
759 case DHCP_OPTION_DATA_IPV4ADDRESS
: {
760 r
= in_addr_from_string(AF_INET
, p
, &addr
);
762 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
763 "Failed to parse DHCP ipv4address data, ignoring assignment: %s", p
);
768 sz
= sizeof(addr
.in
.s_addr
);
771 case DHCP_OPTION_DATA_IPV6ADDRESS
: {
772 r
= in_addr_from_string(AF_INET6
, p
, &addr
);
774 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
775 "Failed to parse DHCP ipv6address data, ignoring assignment: %s", p
);
780 sz
= sizeof(addr
.in6
.s6_addr
);
783 case DHCP_OPTION_DATA_STRING
:
784 sz
= cunescape(p
, UNESCAPE_ACCEPT_NUL
, &q
);
786 log_syntax(unit
, LOG_WARNING
, filename
, line
, sz
,
787 "Failed to decode DHCP option data, ignoring assignment: %s", p
);
795 if (ltype
== AF_INET6
) {
796 r
= sd_dhcp6_option_new(u16
, udata
, sz
, enterprise_identifier
, &opt6
);
798 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
799 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
803 r
= ordered_hashmap_ensure_allocated(options
, &dhcp6_option_hash_ops
);
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
);
811 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
812 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
817 r
= sd_dhcp_option_new(u8
, udata
, sz
, &opt4
);
819 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
820 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
824 r
= ordered_hashmap_ensure_allocated(options
, &dhcp_option_hash_ops
);
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
);
832 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
833 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
841 int config_parse_dhcp_request_options(
843 const char *filename
,
846 unsigned section_line
,
853 Network
*network
= data
;
862 if (isempty(rvalue
)) {
863 if (ltype
== AF_INET
)
864 network
->dhcp_request_options
= set_free(network
->dhcp_request_options
);
866 network
->dhcp6_request_options
= set_free(network
->dhcp6_request_options
);
872 _cleanup_free_
char *n
= NULL
;
875 r
= extract_first_word(&p
, &n
, NULL
, 0);
879 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
880 "Failed to parse DHCP request option, ignoring assignment: %s",
887 r
= safe_atou32(n
, &i
);
889 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
890 "DHCP request option is invalid, ignoring assignment: %s", n
);
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
);
900 r
= set_ensure_put(ltype
== AF_INET
? &network
->dhcp_request_options
: &network
->dhcp6_request_options
,
901 NULL
, UINT32_TO_PTR(i
));
903 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
904 "Failed to store DHCP request option '%s', ignoring assignment: %m", n
);
908 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains
, dhcp_use_domains
, DHCPUseDomains
,
909 "Failed to parse DHCP use domains setting");
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",
917 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains
, DHCPUseDomains
, DHCP_USE_DOMAINS_YES
);
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",
928 DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type
, DHCPOptionDataType
);