1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <netinet/in.h>
4 #include <linux/if_arp.h>
7 #include "bus-locator.h"
8 #include "dhcp-identifier.h"
9 #include "dhcp-option.h"
10 #include "dhcp6-internal.h"
12 #include "hexdecoct.h"
13 #include "in-addr-prefix-util.h"
14 #include "networkd-dhcp-common.h"
15 #include "networkd-link.h"
16 #include "networkd-manager.h"
17 #include "networkd-network.h"
18 #include "networkd-route-util.h"
19 #include "parse-util.h"
20 #include "socket-util.h"
21 #include "string-table.h"
25 static uint32_t link_get_vrf_table(Link
*link
) {
27 assert(link
->network
);
29 return link
->network
->vrf
? VRF(link
->network
->vrf
)->table
: RT_TABLE_MAIN
;
32 uint32_t link_get_dhcp4_route_table(Link
*link
) {
34 assert(link
->network
);
36 /* When the interface is part of an VRF use the VRFs routing table, unless
37 * another table is explicitly specified. */
39 if (link
->network
->dhcp_route_table_set
)
40 return link
->network
->dhcp_route_table
;
41 return link_get_vrf_table(link
);
44 uint32_t link_get_ipv6_accept_ra_route_table(Link
*link
) {
46 assert(link
->network
);
48 if (link
->network
->ipv6_accept_ra_route_table_set
)
49 return link
->network
->ipv6_accept_ra_route_table
;
50 return link_get_vrf_table(link
);
53 bool link_dhcp_enabled(Link
*link
, int family
) {
55 assert(IN_SET(family
, AF_INET
, AF_INET6
));
57 /* Currently, sd-dhcp-client supports only ethernet and infiniband. */
58 if (family
== AF_INET
&& !IN_SET(link
->iftype
, ARPHRD_ETHER
, ARPHRD_INFINIBAND
))
61 if (family
== AF_INET6
&& !socket_ipv6_is_supported())
64 if (link
->flags
& IFF_LOOPBACK
)
67 if (link
->iftype
== ARPHRD_CAN
)
73 return link
->network
->dhcp
& (family
== AF_INET
? ADDRESS_FAMILY_IPV4
: ADDRESS_FAMILY_IPV6
);
76 void network_adjust_dhcp(Network
*network
) {
78 assert(network
->dhcp
>= 0);
80 if (network
->dhcp
== ADDRESS_FAMILY_NO
)
83 /* Bonding slave does not support addressing. */
85 log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.",
87 network
->dhcp
= ADDRESS_FAMILY_NO
;
91 if (!FLAGS_SET(network
->link_local
, ADDRESS_FAMILY_IPV6
) &&
92 FLAGS_SET(network
->dhcp
, ADDRESS_FAMILY_IPV6
)) {
93 log_warning("%s: DHCPv6 client is enabled but IPv6 link-local addressing is disabled. "
94 "Disabling DHCPv6 client.", network
->filename
);
95 SET_FLAG(network
->dhcp
, ADDRESS_FAMILY_IPV6
, false);
98 network_adjust_dhcp4(network
);
101 static bool duid_needs_product_uuid(const DUID
*duid
) {
104 return duid
->type
== DUID_TYPE_UUID
&& duid
->raw_data_len
== 0;
107 static const struct DUID fallback_duid
= { .type
= DUID_TYPE_EN
};
109 const DUID
*link_get_duid(Link
*link
, int family
) {
113 assert(IN_SET(family
, AF_INET
, AF_INET6
));
116 duid
= family
== AF_INET
? &link
->network
->dhcp_duid
: &link
->network
->dhcp6_duid
;
117 if (duid
->type
!= _DUID_TYPE_INVALID
) {
118 if (duid_needs_product_uuid(duid
))
119 return &link
->manager
->duid_product_uuid
;
125 duid
= family
== AF_INET
? &link
->manager
->dhcp_duid
: &link
->manager
->dhcp6_duid
;
126 if (link
->hw_addr
.length
== 0 && IN_SET(duid
->type
, DUID_TYPE_LLT
, DUID_TYPE_LL
))
127 /* Fallback to DUID that works without MAC address.
128 * This is useful for tunnel devices without MAC address. */
129 return &fallback_duid
;
134 static int get_product_uuid_handler(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
135 Manager
*manager
= ASSERT_PTR(userdata
);
136 const sd_bus_error
*e
;
143 /* To avoid calling GetProductUUID() bus method so frequently, set the flag below
144 * even if the method fails. */
145 manager
->has_product_uuid
= true;
147 e
= sd_bus_message_get_error(m
);
149 r
= sd_bus_error_get_errno(e
);
150 log_warning_errno(r
, "Could not get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %s",
151 bus_error_message(e
, r
));
155 r
= sd_bus_message_read_array(m
, 'y', &a
, &sz
);
157 log_warning_errno(r
, "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m");
161 if (sz
!= sizeof(sd_id128_t
)) {
162 log_warning("Invalid product UUID. Falling back to use machine-app-specific ID as DUID-UUID.");
166 log_debug("Successfully obtained product UUID");
168 memcpy(&manager
->duid_product_uuid
.raw_data
, a
, sz
);
169 manager
->duid_product_uuid
.raw_data_len
= sz
;
174 int manager_request_product_uuid(Manager
*m
) {
175 static bool bus_method_is_called
= false;
180 if (bus_method_is_called
)
183 if (sd_bus_is_ready(m
->bus
) <= 0 && !m
->product_uuid_requested
) {
184 log_debug("Not connected to system bus, requesting product UUID later.");
185 m
->product_uuid_requested
= true;
189 m
->product_uuid_requested
= false;
191 r
= bus_call_method_async(
196 get_product_uuid_handler
,
201 return log_warning_errno(r
, "Failed to get product UUID: %m");
203 log_debug("Requesting product UUID.");
205 bus_method_is_called
= true;
210 int dhcp_configure_duid(Link
*link
, const DUID
*duid
) {
215 assert(link
->manager
);
220 if (!duid_needs_product_uuid(duid
))
223 if (m
->has_product_uuid
)
226 r
= manager_request_product_uuid(m
);
228 log_link_warning_errno(link
, r
,
229 "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m");
231 m
->has_product_uuid
= true; /* Do not request UUID again on failure. */
238 bool address_is_filtered(int family
, const union in_addr_union
*address
, uint8_t prefixlen
, Set
*allow_list
, Set
*deny_list
) {
239 struct in_addr_prefix
*p
;
241 assert(IN_SET(family
, AF_INET
, AF_INET6
));
245 SET_FOREACH(p
, allow_list
)
246 if (p
->family
== family
&&
247 p
->prefixlen
<= prefixlen
&&
248 in_addr_prefix_covers(family
, &p
->address
, p
->prefixlen
, address
) > 0)
254 SET_FOREACH(p
, deny_list
)
255 if (p
->family
== family
&&
256 in_addr_prefix_intersect(family
, &p
->address
, p
->prefixlen
, address
, prefixlen
) > 0)
262 int link_get_captive_portal(Link
*link
, const char **ret
) {
263 const char *dhcp4_cp
= NULL
, *dhcp6_cp
= NULL
, *ndisc_cp
= NULL
;
268 if (!link
->network
) {
273 if (link
->network
->dhcp_use_captive_portal
&& link
->dhcp_lease
) {
274 r
= sd_dhcp_lease_get_captive_portal(link
->dhcp_lease
, &dhcp4_cp
);
275 if (r
< 0 && r
!= -ENODATA
)
279 if (link
->network
->dhcp6_use_captive_portal
&& link
->dhcp6_lease
) {
280 r
= sd_dhcp6_lease_get_captive_portal(link
->dhcp6_lease
, &dhcp6_cp
);
281 if (r
< 0 && r
!= -ENODATA
)
285 if (link
->network
->ipv6_accept_ra_use_captive_portal
) {
286 NDiscCaptivePortal
*cp
;
289 /* Use the captive portal with the longest lifetime. */
291 SET_FOREACH(cp
, link
->ndisc_captive_portals
) {
292 if (cp
->lifetime_usec
< usec
)
295 ndisc_cp
= cp
->captive_portal
;
296 usec
= cp
->lifetime_usec
;
299 if (set_size(link
->ndisc_captive_portals
) > 1)
300 log_link_debug(link
, "Multiple captive portals obtained by IPv6RA, using \"%s\" and ignoring others.",
305 if (dhcp6_cp
&& !streq(dhcp4_cp
, dhcp6_cp
))
306 log_link_debug(link
, "DHCPv6 captive portal (%s) does not match DHCPv4 (%s), ignoring DHCPv6 captive portal.",
309 if (ndisc_cp
&& !streq(dhcp4_cp
, ndisc_cp
))
310 log_link_debug(link
, "IPv6RA captive portal (%s) does not match DHCPv4 (%s), ignoring IPv6RA captive portal.",
318 if (ndisc_cp
&& !streq(dhcp6_cp
, ndisc_cp
))
319 log_link_debug(link
, "IPv6RA captive portal (%s) does not match DHCPv6 (%s), ignoring IPv6RA captive portal.",
330 int config_parse_dhcp(
332 const char *filename
,
335 unsigned section_line
,
342 AddressFamily
*dhcp
= data
, s
;
349 /* Note that this is mostly like
350 * config_parse_address_family(), except that it
351 * understands some old names for the enum values */
353 s
= address_family_from_string(rvalue
);
356 /* Previously, we had a slightly different enum here,
357 * support its values for compatibility. */
359 s
= dhcp_deprecated_address_family_from_string(rvalue
);
361 log_syntax(unit
, LOG_WARNING
, filename
, line
, s
,
362 "Failed to parse DHCP option, ignoring: %s", rvalue
);
366 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
367 "DHCP=%s is deprecated, please use DHCP=%s instead.",
368 rvalue
, address_family_to_string(s
));
375 int config_parse_dhcp_route_metric(
377 const char *filename
,
380 unsigned section_line
,
387 Network
*network
= userdata
;
393 assert(IN_SET(ltype
, AF_UNSPEC
, AF_INET
));
397 r
= safe_atou32(rvalue
, &metric
);
399 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
400 "Failed to parse RouteMetric=%s, ignoring assignment: %m", rvalue
);
406 network
->dhcp_route_metric
= metric
;
407 network
->dhcp_route_metric_set
= true;
410 /* For backward compatibility. */
411 if (!network
->dhcp_route_metric_set
)
412 network
->dhcp_route_metric
= metric
;
413 if (!network
->ipv6_accept_ra_route_metric_set
) {
414 network
->ipv6_accept_ra_route_metric_high
= metric
;
415 network
->ipv6_accept_ra_route_metric_medium
= metric
;
416 network
->ipv6_accept_ra_route_metric_low
= metric
;
420 assert_not_reached();
426 int config_parse_ipv6_accept_ra_route_metric(
428 const char *filename
,
431 unsigned section_line
,
438 Network
*network
= ASSERT_PTR(userdata
);
439 uint32_t metric_high
, metric_medium
, metric_low
;
445 if (safe_atou32(rvalue
, &metric_low
) >= 0)
446 metric_high
= metric_medium
= metric_low
;
448 _cleanup_free_
char *high
= NULL
, *medium
= NULL
, *low
= NULL
;
449 const char *p
= rvalue
;
451 r
= extract_many_words(&p
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
, &high
, &medium
, &low
, NULL
);
454 if (r
!= 3 || !isempty(p
)) {
455 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
< 0 ? r
: 0,
456 "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue
);
460 r
= safe_atou32(high
, &metric_high
);
461 s
= safe_atou32(medium
, &metric_medium
);
462 t
= safe_atou32(low
, &metric_low
);
463 if (r
< 0 || s
< 0 || t
< 0) {
464 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
< 0 ? r
: s
< 0 ? s
: t
,
465 "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue
);
469 if (metric_high
>= metric_medium
|| metric_medium
>= metric_low
) {
470 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
471 "Invalid RouteTable=%s, ignoring assignment: %m", rvalue
);
476 network
->ipv6_accept_ra_route_metric_high
= metric_high
;
477 network
->ipv6_accept_ra_route_metric_medium
= metric_medium
;
478 network
->ipv6_accept_ra_route_metric_low
= metric_low
;
479 network
->ipv6_accept_ra_route_metric_set
= true;
484 int config_parse_dhcp_send_hostname(
486 const char *filename
,
489 unsigned section_line
,
496 Network
*network
= userdata
;
501 assert(IN_SET(ltype
, AF_UNSPEC
, AF_INET
, AF_INET6
));
505 r
= parse_boolean(rvalue
);
507 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
508 "Failed to parse SendHostname=%s, ignoring assignment: %m", rvalue
);
514 network
->dhcp_send_hostname
= r
;
515 network
->dhcp_send_hostname_set
= true;
518 network
->dhcp6_send_hostname
= r
;
519 network
->dhcp6_send_hostname_set
= true;
522 /* For backward compatibility. */
523 if (!network
->dhcp_send_hostname_set
)
524 network
->dhcp_send_hostname
= r
;
525 if (!network
->dhcp6_send_hostname_set
)
526 network
->dhcp6_send_hostname
= r
;
529 assert_not_reached();
534 int config_parse_dhcp_use_dns(
536 const char *filename
,
539 unsigned section_line
,
546 Network
*network
= userdata
;
551 assert(IN_SET(ltype
, AF_UNSPEC
, AF_INET
, AF_INET6
));
555 r
= parse_boolean(rvalue
);
557 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
558 "Failed to parse UseDNS=%s, ignoring assignment: %m", rvalue
);
564 network
->dhcp_use_dns
= r
;
565 network
->dhcp_use_dns_set
= true;
568 network
->dhcp6_use_dns
= r
;
569 network
->dhcp6_use_dns_set
= true;
572 /* For backward compatibility. */
573 if (!network
->dhcp_use_dns_set
)
574 network
->dhcp_use_dns
= r
;
575 if (!network
->dhcp6_use_dns_set
)
576 network
->dhcp6_use_dns
= r
;
579 assert_not_reached();
585 int config_parse_dhcp_use_domains(
587 const char *filename
,
590 unsigned section_line
,
597 Network
*network
= userdata
;
602 assert(IN_SET(ltype
, AF_UNSPEC
, AF_INET
, AF_INET6
));
606 d
= dhcp_use_domains_from_string(rvalue
);
608 log_syntax(unit
, LOG_WARNING
, filename
, line
, d
,
609 "Failed to parse %s=%s, ignoring assignment: %m", lvalue
, rvalue
);
615 network
->dhcp_use_domains
= d
;
616 network
->dhcp_use_domains_set
= true;
619 network
->dhcp6_use_domains
= d
;
620 network
->dhcp6_use_domains_set
= true;
623 /* For backward compatibility. */
624 if (!network
->dhcp_use_domains_set
)
625 network
->dhcp_use_domains
= d
;
626 if (!network
->dhcp6_use_domains_set
)
627 network
->dhcp6_use_domains
= d
;
630 assert_not_reached();
636 int config_parse_dhcp_use_ntp(
638 const char *filename
,
641 unsigned section_line
,
648 Network
*network
= userdata
;
653 assert(IN_SET(ltype
, AF_UNSPEC
, AF_INET
, AF_INET6
));
657 r
= parse_boolean(rvalue
);
659 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
660 "Failed to parse UseNTP=%s, ignoring assignment: %m", rvalue
);
666 network
->dhcp_use_ntp
= r
;
667 network
->dhcp_use_ntp_set
= true;
670 network
->dhcp6_use_ntp
= r
;
671 network
->dhcp6_use_ntp_set
= true;
674 /* For backward compatibility. */
675 if (!network
->dhcp_use_ntp_set
)
676 network
->dhcp_use_ntp
= r
;
677 if (!network
->dhcp6_use_ntp_set
)
678 network
->dhcp6_use_ntp
= r
;
681 assert_not_reached();
687 int config_parse_dhcp_or_ra_route_table(
689 const char *filename
,
692 unsigned section_line
,
699 Network
*network
= ASSERT_PTR(userdata
);
705 assert(IN_SET(ltype
, AF_INET
, AF_INET6
));
708 r
= manager_get_route_table_from_string(network
->manager
, rvalue
, &rt
);
710 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
711 "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue
);
717 network
->dhcp_route_table
= rt
;
718 network
->dhcp_route_table_set
= true;
721 network
->ipv6_accept_ra_route_table
= rt
;
722 network
->ipv6_accept_ra_route_table_set
= true;
725 assert_not_reached();
731 int config_parse_iaid(
733 const char *filename
,
736 unsigned section_line
,
743 Network
*network
= ASSERT_PTR(userdata
);
750 assert(IN_SET(ltype
, AF_INET
, AF_INET6
));
752 r
= safe_atou32(rvalue
, &iaid
);
754 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
755 "Unable to read IAID, ignoring assignment: %s", rvalue
);
759 if (ltype
== AF_INET
) {
760 network
->dhcp_iaid
= iaid
;
761 network
->dhcp_iaid_set
= true;
762 if (!network
->dhcp6_iaid_set_explicitly
) {
763 /* Backward compatibility. Previously, IAID is shared by DHCPv4 and DHCPv6.
764 * If DHCPv6 IAID is not specified explicitly, then use DHCPv4 IAID for DHCPv6. */
765 network
->dhcp6_iaid
= iaid
;
766 network
->dhcp6_iaid_set
= true;
769 assert(ltype
== AF_INET6
);
770 network
->dhcp6_iaid
= iaid
;
771 network
->dhcp6_iaid_set
= true;
772 network
->dhcp6_iaid_set_explicitly
= true;
778 int config_parse_dhcp_user_or_vendor_class(
780 const char *filename
,
783 unsigned section_line
,
790 char ***l
= ASSERT_PTR(data
);
795 assert(IN_SET(ltype
, AF_INET
, AF_INET6
));
797 if (isempty(rvalue
)) {
802 for (const char *p
= rvalue
;;) {
803 _cleanup_free_
char *w
= NULL
;
806 r
= extract_first_word(&p
, &w
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_UNQUOTE
);
810 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
811 "Failed to split user classes option, ignoring: %s", rvalue
);
818 if (ltype
== AF_INET
) {
819 if (len
> UINT8_MAX
|| len
== 0) {
820 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
821 "%s length is not in the range 1…255, ignoring.", w
);
825 if (len
> UINT16_MAX
|| len
== 0) {
826 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
827 "%s length is not in the range 1…65535, ignoring.", w
);
832 r
= strv_consume(l
, TAKE_PTR(w
));
838 int config_parse_dhcp_send_option(
840 const char *filename
,
843 unsigned section_line
,
850 _cleanup_(sd_dhcp_option_unrefp
) sd_dhcp_option
*opt4
= NULL
;
851 _cleanup_(sd_dhcp6_option_unrefp
) sd_dhcp6_option
*opt6
= NULL
;
852 _unused_
_cleanup_(sd_dhcp_option_unrefp
) sd_dhcp_option
*old4
= NULL
;
853 _unused_
_cleanup_(sd_dhcp6_option_unrefp
) sd_dhcp6_option
*old6
= NULL
;
854 uint32_t uint32_data
, enterprise_identifier
= 0;
855 _cleanup_free_
char *word
= NULL
, *q
= NULL
;
856 OrderedHashmap
**options
= ASSERT_PTR(data
);
857 uint16_t u16
, uint16_data
;
858 union in_addr_union addr
;
859 DHCPOptionDataType type
;
860 uint8_t u8
, uint8_data
;
870 if (isempty(rvalue
)) {
871 *options
= ordered_hashmap_free(*options
);
876 if (ltype
== AF_INET6
&& streq(lvalue
, "SendVendorOption")) {
877 r
= extract_first_word(&p
, &word
, ":", 0);
880 if (r
<= 0 || isempty(p
)) {
881 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
882 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
886 r
= safe_atou32(word
, &enterprise_identifier
);
888 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
889 "Failed to parse DHCPv6 enterprise identifier data, ignoring assignment: %s", p
);
895 r
= extract_first_word(&p
, &word
, ":", 0);
898 if (r
<= 0 || isempty(p
)) {
899 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
900 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
904 if (ltype
== AF_INET6
) {
905 r
= safe_atou16(word
, &u16
);
907 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
908 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
911 if (u16
< 1 || u16
>= UINT16_MAX
) {
912 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
913 "Invalid DHCP option, valid range is 1-65535, ignoring assignment: %s", rvalue
);
917 r
= safe_atou8(word
, &u8
);
919 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
920 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
923 if (u8
< 1 || u8
>= UINT8_MAX
) {
924 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
925 "Invalid DHCP option, valid range is 1-254, ignoring assignment: %s", rvalue
);
931 r
= extract_first_word(&p
, &word
, ":", 0);
934 if (r
<= 0 || isempty(p
)) {
935 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
936 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
940 type
= dhcp_option_data_type_from_string(word
);
942 log_syntax(unit
, LOG_WARNING
, filename
, line
, type
,
943 "Invalid DHCP option data type, ignoring assignment: %s", p
);
948 case DHCP_OPTION_DATA_UINT8
:{
949 r
= safe_atou8(p
, &uint8_data
);
951 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
952 "Failed to parse DHCP uint8 data, ignoring assignment: %s", p
);
957 sz
= sizeof(uint8_t);
960 case DHCP_OPTION_DATA_UINT16
:{
963 r
= safe_atou16(p
, &k
);
965 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
966 "Failed to parse DHCP uint16 data, ignoring assignment: %s", p
);
970 uint16_data
= htobe16(k
);
971 udata
= &uint16_data
;
972 sz
= sizeof(uint16_t);
975 case DHCP_OPTION_DATA_UINT32
: {
978 r
= safe_atou32(p
, &k
);
980 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
981 "Failed to parse DHCP uint32 data, ignoring assignment: %s", p
);
985 uint32_data
= htobe32(k
);
986 udata
= &uint32_data
;
987 sz
= sizeof(uint32_t);
991 case DHCP_OPTION_DATA_IPV4ADDRESS
: {
992 r
= in_addr_from_string(AF_INET
, p
, &addr
);
994 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
995 "Failed to parse DHCP ipv4address data, ignoring assignment: %s", p
);
1000 sz
= sizeof(addr
.in
.s_addr
);
1003 case DHCP_OPTION_DATA_IPV6ADDRESS
: {
1004 r
= in_addr_from_string(AF_INET6
, p
, &addr
);
1006 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1007 "Failed to parse DHCP ipv6address data, ignoring assignment: %s", p
);
1012 sz
= sizeof(addr
.in6
.s6_addr
);
1015 case DHCP_OPTION_DATA_STRING
:
1016 sz
= cunescape(p
, UNESCAPE_ACCEPT_NUL
, &q
);
1018 log_syntax(unit
, LOG_WARNING
, filename
, line
, sz
,
1019 "Failed to decode DHCP option data, ignoring assignment: %s", p
);
1029 if (ltype
== AF_INET6
) {
1030 r
= sd_dhcp6_option_new(u16
, udata
, sz
, enterprise_identifier
, &opt6
);
1032 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1033 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
1037 r
= ordered_hashmap_ensure_allocated(options
, &dhcp6_option_hash_ops
);
1041 /* Overwrite existing option */
1042 old6
= ordered_hashmap_get(*options
, UINT_TO_PTR(u16
));
1043 r
= ordered_hashmap_replace(*options
, UINT_TO_PTR(u16
), opt6
);
1045 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1046 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
1051 r
= sd_dhcp_option_new(u8
, udata
, sz
, &opt4
);
1053 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1054 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
1058 r
= ordered_hashmap_ensure_allocated(options
, &dhcp_option_hash_ops
);
1062 /* Overwrite existing option */
1063 old4
= ordered_hashmap_get(*options
, UINT_TO_PTR(u8
));
1064 r
= ordered_hashmap_replace(*options
, UINT_TO_PTR(u8
), opt4
);
1066 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1067 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
1075 int config_parse_dhcp_request_options(
1077 const char *filename
,
1079 const char *section
,
1080 unsigned section_line
,
1087 Network
*network
= userdata
;
1095 if (isempty(rvalue
)) {
1096 if (ltype
== AF_INET
)
1097 network
->dhcp_request_options
= set_free(network
->dhcp_request_options
);
1099 network
->dhcp6_request_options
= set_free(network
->dhcp6_request_options
);
1104 for (const char *p
= rvalue
;;) {
1105 _cleanup_free_
char *n
= NULL
;
1108 r
= extract_first_word(&p
, &n
, NULL
, 0);
1112 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1113 "Failed to parse DHCP request option, ignoring assignment: %s",
1120 r
= safe_atou32(n
, &i
);
1122 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1123 "DHCP request option is invalid, ignoring assignment: %s", n
);
1127 if (i
< 1 || i
>= UINT8_MAX
) {
1128 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1129 "DHCP request option is invalid, valid range is 1-254, ignoring assignment: %s", n
);
1133 r
= set_ensure_put(ltype
== AF_INET
? &network
->dhcp_request_options
: &network
->dhcp6_request_options
,
1134 NULL
, UINT32_TO_PTR(i
));
1136 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1137 "Failed to store DHCP request option '%s', ignoring assignment: %m", n
);
1141 static const char* const dhcp_use_domains_table
[_DHCP_USE_DOMAINS_MAX
] = {
1142 [DHCP_USE_DOMAINS_NO
] = "no",
1143 [DHCP_USE_DOMAINS_ROUTE
] = "route",
1144 [DHCP_USE_DOMAINS_YES
] = "yes",
1147 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains
, DHCPUseDomains
, DHCP_USE_DOMAINS_YES
);
1149 static const char * const dhcp_option_data_type_table
[_DHCP_OPTION_DATA_MAX
] = {
1150 [DHCP_OPTION_DATA_UINT8
] = "uint8",
1151 [DHCP_OPTION_DATA_UINT16
] = "uint16",
1152 [DHCP_OPTION_DATA_UINT32
] = "uint32",
1153 [DHCP_OPTION_DATA_STRING
] = "string",
1154 [DHCP_OPTION_DATA_IPV4ADDRESS
] = "ipv4address",
1155 [DHCP_OPTION_DATA_IPV6ADDRESS
] = "ipv6address",
1158 DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type
, DHCPOptionDataType
);
1160 static const char* const duid_type_table
[_DUID_TYPE_MAX
] = {
1161 [DUID_TYPE_LLT
] = "link-layer-time",
1162 [DUID_TYPE_EN
] = "vendor",
1163 [DUID_TYPE_LL
] = "link-layer",
1164 [DUID_TYPE_UUID
] = "uuid",
1166 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(duid_type
, DUIDType
);
1168 int config_parse_duid_type(
1170 const char *filename
,
1172 const char *section
,
1173 unsigned section_line
,
1180 _cleanup_free_
char *type_string
= NULL
;
1181 const char *p
= ASSERT_PTR(rvalue
);
1183 DUID
*duid
= ASSERT_PTR(data
);
1190 if (!force
&& duid
->set
)
1193 r
= extract_first_word(&p
, &type_string
, ":", 0);
1197 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1198 "Invalid syntax, ignoring: %s", rvalue
);
1202 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1203 "Failed to extract DUID type from '%s', ignoring.", rvalue
);
1207 type
= duid_type_from_string(type_string
);
1211 r
= safe_atou16(type_string
, &t
);
1213 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1214 "Failed to parse DUID type '%s', ignoring.", type_string
);
1219 assert(type
== t
); /* Check if type can store uint16_t. */
1225 if (type
!= DUID_TYPE_LLT
) {
1226 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1227 "Invalid syntax, ignoring: %s", rvalue
);
1231 r
= parse_timestamp(p
, &u
);
1233 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1234 "Failed to parse timestamp, ignoring: %s", p
);
1247 int config_parse_manager_duid_type(
1249 const char *filename
,
1251 const char *section
,
1252 unsigned section_line
,
1259 Manager
*manager
= ASSERT_PTR(userdata
);
1262 /* For backward compatibility. Setting both DHCPv4 and DHCPv6 DUID if they are not specified explicitly. */
1264 r
= config_parse_duid_type(unit
, filename
, line
, section
, section_line
, lvalue
, false, rvalue
, &manager
->dhcp_duid
, manager
);
1268 return config_parse_duid_type(unit
, filename
, line
, section
, section_line
, lvalue
, false, rvalue
, &manager
->dhcp6_duid
, manager
);
1271 int config_parse_network_duid_type(
1273 const char *filename
,
1275 const char *section
,
1276 unsigned section_line
,
1283 Network
*network
= ASSERT_PTR(userdata
);
1286 r
= config_parse_duid_type(unit
, filename
, line
, section
, section_line
, lvalue
, true, rvalue
, &network
->dhcp_duid
, network
);
1290 /* For backward compatibility, also set DHCPv6 DUID if not specified explicitly. */
1291 return config_parse_duid_type(unit
, filename
, line
, section
, section_line
, lvalue
, false, rvalue
, &network
->dhcp6_duid
, network
);
1294 int config_parse_duid_rawdata(
1296 const char *filename
,
1298 const char *section
,
1299 unsigned section_line
,
1306 uint8_t raw_data
[MAX_DUID_DATA_LEN
];
1309 DUID
*duid
= ASSERT_PTR(data
);
1315 if (!force
&& duid
->set
)
1318 /* RawData contains DUID in format "NN:NN:NN..." */
1319 for (const char *p
= rvalue
;;) {
1322 _cleanup_free_
char *cbyte
= NULL
;
1324 r
= extract_first_word(&p
, &cbyte
, ":", 0);
1328 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Failed to read DUID, ignoring assignment: %s.", rvalue
);
1334 if (count
>= MAX_DUID_DATA_LEN
) {
1335 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue
);
1339 len
= strlen(cbyte
);
1340 if (!IN_SET(len
, 1, 2)) {
1341 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte
, rvalue
);
1344 n1
= unhexchar(cbyte
[0]);
1346 n2
= unhexchar(cbyte
[1]);
1350 if (n1
< 0 || n2
< 0) {
1351 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte
, rvalue
);
1355 byte
= ((uint8_t) n1
<< (4 * (len
-1))) | (uint8_t) n2
;
1356 raw_data
[count
++] = byte
;
1359 assert_cc(sizeof(raw_data
) == sizeof(duid
->raw_data
));
1360 memcpy(duid
->raw_data
, raw_data
, count
);
1361 duid
->raw_data_len
= count
;
1367 int config_parse_manager_duid_rawdata(
1369 const char *filename
,
1371 const char *section
,
1372 unsigned section_line
,
1379 Manager
*manager
= ASSERT_PTR(userdata
);
1382 /* For backward compatibility. Setting both DHCPv4 and DHCPv6 DUID if they are not specified explicitly. */
1384 r
= config_parse_duid_rawdata(unit
, filename
, line
, section
, section_line
, lvalue
, false, rvalue
, &manager
->dhcp_duid
, manager
);
1388 return config_parse_duid_rawdata(unit
, filename
, line
, section
, section_line
, lvalue
, false, rvalue
, &manager
->dhcp6_duid
, manager
);
1391 int config_parse_network_duid_rawdata(
1393 const char *filename
,
1395 const char *section
,
1396 unsigned section_line
,
1403 Network
*network
= ASSERT_PTR(userdata
);
1406 r
= config_parse_duid_rawdata(unit
, filename
, line
, section
, section_line
, lvalue
, true, rvalue
, &network
->dhcp_duid
, network
);
1410 /* For backward compatibility, also set DHCPv6 DUID if not specified explicitly. */
1411 return config_parse_duid_rawdata(unit
, filename
, line
, section
, section_line
, lvalue
, false, rvalue
, &network
->dhcp6_duid
, network
);
1414 int config_parse_uplink(
1416 const char *filename
,
1418 const char *section
,
1419 unsigned section_line
,
1426 Network
*network
= ASSERT_PTR(userdata
);
1427 bool accept_none
= true;
1436 if (streq(section
, "DHCPServer")) {
1437 index
= &network
->dhcp_server_uplink_index
;
1438 name
= &network
->dhcp_server_uplink_name
;
1439 } else if (streq(section
, "IPv6SendRA")) {
1440 index
= &network
->router_uplink_index
;
1441 name
= &network
->router_uplink_name
;
1442 } else if (STR_IN_SET(section
, "DHCPv6PrefixDelegation", "DHCPPrefixDelegation")) {
1443 index
= &network
->dhcp_pd_uplink_index
;
1444 name
= &network
->dhcp_pd_uplink_name
;
1445 accept_none
= false;
1447 assert_not_reached();
1449 if (isempty(rvalue
) || streq(rvalue
, ":auto")) {
1450 *index
= UPLINK_INDEX_AUTO
;
1451 *name
= mfree(*name
);
1455 if (accept_none
&& streq(rvalue
, ":none")) {
1456 *index
= UPLINK_INDEX_NONE
;
1457 *name
= mfree(*name
);
1461 if (!accept_none
&& streq(rvalue
, ":self")) {
1462 *index
= UPLINK_INDEX_SELF
;
1463 *name
= mfree(*name
);
1467 r
= parse_ifindex(rvalue
);
1470 *name
= mfree(*name
);
1474 if (!ifname_valid_full(rvalue
, IFNAME_VALID_ALTERNATIVE
)) {
1475 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1476 "Invalid interface name in %s=, ignoring assignment: %s", lvalue
, rvalue
);
1480 /* The interface name will be resolved later. */
1481 r
= free_and_strdup_warn(name
, rvalue
);
1485 /* Note, if uplink_name is set, then uplink_index will be ignored. So, the below does not mean
1486 * an uplink interface will be selected automatically. */
1487 *index
= UPLINK_INDEX_AUTO
;