1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <netinet/in.h>
4 #include <linux/if_arp.h>
7 #include "dhcp-identifier.h"
8 #include "dhcp-internal.h"
9 #include "dhcp6-internal.h"
11 #include "hexdecoct.h"
12 #include "in-addr-prefix-util.h"
13 #include "networkd-dhcp-common.h"
14 #include "networkd-link.h"
15 #include "networkd-manager.h"
16 #include "networkd-network.h"
17 #include "parse-util.h"
18 #include "socket-util.h"
19 #include "string-table.h"
23 static uint32_t link_get_vrf_table(Link
*link
) {
25 assert(link
->network
);
27 return link
->network
->vrf
? VRF(link
->network
->vrf
)->table
: RT_TABLE_MAIN
;
30 uint32_t link_get_dhcp4_route_table(Link
*link
) {
32 assert(link
->network
);
34 /* When the interface is part of an VRF use the VRFs routing table, unless
35 * another table is explicitly specified. */
37 if (link
->network
->dhcp_route_table_set
)
38 return link
->network
->dhcp_route_table
;
39 return link_get_vrf_table(link
);
42 uint32_t link_get_ipv6_accept_ra_route_table(Link
*link
) {
44 assert(link
->network
);
46 if (link
->network
->ipv6_accept_ra_route_table_set
)
47 return link
->network
->ipv6_accept_ra_route_table
;
48 return link_get_vrf_table(link
);
51 bool link_dhcp_enabled(Link
*link
, int family
) {
53 assert(IN_SET(family
, AF_INET
, AF_INET6
));
55 if (family
== AF_INET6
&& !socket_ipv6_is_supported())
58 if (link
->flags
& IFF_LOOPBACK
)
61 if (link
->iftype
== ARPHRD_CAN
)
64 if (!IN_SET(link
->hw_addr
.length
, ETH_ALEN
, INFINIBAND_ALEN
) &&
65 !streq_ptr(link
->kind
, "wwan"))
66 /* Currently, only interfaces whose MAC address length is ETH_ALEN or INFINIBAND_ALEN
67 * are supported. Note, wwan interfaces may be assigned MAC address slightly later.
68 * Hence, let's wait for a while.*/
74 return link
->network
->dhcp
& (family
== AF_INET
? ADDRESS_FAMILY_IPV4
: ADDRESS_FAMILY_IPV6
);
77 void network_adjust_dhcp(Network
*network
) {
79 assert(network
->dhcp
>= 0);
81 if (network
->dhcp
== ADDRESS_FAMILY_NO
)
84 /* Bonding slave does not support addressing. */
86 log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.",
88 network
->dhcp
= ADDRESS_FAMILY_NO
;
92 if (!FLAGS_SET(network
->link_local
, ADDRESS_FAMILY_IPV6
) &&
93 FLAGS_SET(network
->dhcp
, ADDRESS_FAMILY_IPV6
)) {
94 log_warning("%s: DHCPv6 client is enabled but IPv6 link local addressing is disabled. "
95 "Disabling DHCPv6 client.", network
->filename
);
96 SET_FLAG(network
->dhcp
, ADDRESS_FAMILY_IPV6
, false);
99 network_adjust_dhcp4(network
);
102 static bool duid_needs_product_uuid(const DUID
*duid
) {
105 return duid
->type
== DUID_TYPE_UUID
&& duid
->raw_data_len
== 0;
108 static const struct DUID fallback_duid
= { .type
= DUID_TYPE_EN
};
110 const DUID
*link_get_duid(Link
*link
, int family
) {
114 assert(IN_SET(family
, AF_INET
, AF_INET6
));
117 duid
= family
== AF_INET
? &link
->network
->dhcp_duid
: &link
->network
->dhcp6_duid
;
118 if (duid
->type
!= _DUID_TYPE_INVALID
) {
119 if (duid_needs_product_uuid(duid
))
120 return &link
->manager
->duid_product_uuid
;
126 duid
= family
== AF_INET
? &link
->manager
->dhcp_duid
: &link
->manager
->dhcp6_duid
;
127 if (link
->hw_addr
.length
== 0 && IN_SET(duid
->type
, DUID_TYPE_LLT
, DUID_TYPE_LL
))
128 /* Fallback to DUID that works without MAC address.
129 * This is useful for tunnel devices without MAC address. */
130 return &fallback_duid
;
135 static int get_product_uuid_handler(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
136 Manager
*manager
= userdata
;
137 const sd_bus_error
*e
;
145 /* To avoid calling GetProductUUID() bus method so frequently, set the flag below
146 * even if the method fails. */
147 manager
->has_product_uuid
= true;
149 e
= sd_bus_message_get_error(m
);
151 r
= sd_bus_error_get_errno(e
);
152 log_warning_errno(r
, "Could not get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %s",
153 bus_error_message(e
, r
));
157 r
= sd_bus_message_read_array(m
, 'y', &a
, &sz
);
159 log_warning_errno(r
, "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m");
163 if (sz
!= sizeof(sd_id128_t
)) {
164 log_warning("Invalid product UUID. Falling back to use machine-app-specific ID as DUID-UUID.");
168 log_debug("Successfully obtained product UUID");
170 memcpy(&manager
->duid_product_uuid
.raw_data
, a
, sz
);
171 manager
->duid_product_uuid
.raw_data_len
= sz
;
176 int manager_request_product_uuid(Manager
*m
) {
177 static bool bus_method_is_called
= false;
182 if (bus_method_is_called
)
185 if (sd_bus_is_ready(m
->bus
) <= 0 && !m
->product_uuid_requested
) {
186 log_debug("Not connected to system bus, requesting product UUID later.");
187 m
->product_uuid_requested
= true;
191 m
->product_uuid_requested
= false;
193 r
= sd_bus_call_method_async(
196 "org.freedesktop.hostname1",
197 "/org/freedesktop/hostname1",
198 "org.freedesktop.hostname1",
200 get_product_uuid_handler
,
205 return log_warning_errno(r
, "Failed to get product UUID: %m");
207 log_debug("Requesting product UUID.");
209 bus_method_is_called
= true;
214 int dhcp_configure_duid(Link
*link
, const DUID
*duid
) {
219 assert(link
->manager
);
224 if (!duid_needs_product_uuid(duid
))
227 if (m
->has_product_uuid
)
230 r
= manager_request_product_uuid(m
);
232 log_link_warning_errno(link
, r
,
233 "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m");
235 m
->has_product_uuid
= true; /* Do not request UUID again on failure. */
242 bool address_is_filtered(int family
, const union in_addr_union
*address
, uint8_t prefixlen
, Set
*allow_list
, Set
*deny_list
) {
243 struct in_addr_prefix
*p
;
245 assert(IN_SET(family
, AF_INET
, AF_INET6
));
249 SET_FOREACH(p
, allow_list
)
250 if (p
->family
== family
&&
251 p
->prefixlen
<= prefixlen
&&
252 in_addr_prefix_covers(family
, &p
->address
, p
->prefixlen
, address
) > 0)
258 SET_FOREACH(p
, deny_list
)
259 if (p
->family
== family
&&
260 in_addr_prefix_intersect(family
, &p
->address
, p
->prefixlen
, address
, prefixlen
) > 0)
266 int config_parse_dhcp(
268 const char *filename
,
271 unsigned section_line
,
278 AddressFamily
*dhcp
= data
, s
;
285 /* Note that this is mostly like
286 * config_parse_address_family(), except that it
287 * understands some old names for the enum values */
289 s
= address_family_from_string(rvalue
);
292 /* Previously, we had a slightly different enum here,
293 * support its values for compatibility. */
295 s
= dhcp_deprecated_address_family_from_string(rvalue
);
297 log_syntax(unit
, LOG_WARNING
, filename
, line
, s
,
298 "Failed to parse DHCP option, ignoring: %s", rvalue
);
302 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
303 "DHCP=%s is deprecated, please use DHCP=%s instead.",
304 rvalue
, address_family_to_string(s
));
311 int config_parse_dhcp_or_ra_route_metric(
313 const char *filename
,
316 unsigned section_line
,
323 Network
*network
= userdata
;
329 assert(IN_SET(ltype
, AF_UNSPEC
, AF_INET
, AF_INET6
));
333 r
= safe_atou32(rvalue
, &metric
);
335 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
336 "Failed to parse RouteMetric=%s, ignoring assignment: %m", rvalue
);
342 network
->dhcp_route_metric
= metric
;
343 network
->dhcp_route_metric_set
= true;
346 network
->ipv6_accept_ra_route_metric
= metric
;
347 network
->ipv6_accept_ra_route_metric_set
= true;
350 /* For backward compatibility. */
351 if (!network
->dhcp_route_metric_set
)
352 network
->dhcp_route_metric
= metric
;
353 if (!network
->ipv6_accept_ra_route_metric_set
)
354 network
->ipv6_accept_ra_route_metric
= metric
;
357 assert_not_reached();
363 int config_parse_dhcp_use_dns(
365 const char *filename
,
368 unsigned section_line
,
375 Network
*network
= userdata
;
380 assert(IN_SET(ltype
, AF_UNSPEC
, AF_INET
, AF_INET6
));
384 r
= parse_boolean(rvalue
);
386 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
387 "Failed to parse UseDNS=%s, ignoring assignment: %m", rvalue
);
393 network
->dhcp_use_dns
= r
;
394 network
->dhcp_use_dns_set
= true;
397 network
->dhcp6_use_dns
= r
;
398 network
->dhcp6_use_dns_set
= true;
401 /* For backward compatibility. */
402 if (!network
->dhcp_use_dns_set
)
403 network
->dhcp_use_dns
= r
;
404 if (!network
->dhcp6_use_dns_set
)
405 network
->dhcp6_use_dns
= r
;
408 assert_not_reached();
414 int config_parse_dhcp_use_domains(
416 const char *filename
,
419 unsigned section_line
,
426 Network
*network
= userdata
;
431 assert(IN_SET(ltype
, AF_UNSPEC
, AF_INET
, AF_INET6
));
435 d
= dhcp_use_domains_from_string(rvalue
);
437 log_syntax(unit
, LOG_WARNING
, filename
, line
, d
,
438 "Failed to parse %s=%s, ignoring assignment: %m", lvalue
, rvalue
);
444 network
->dhcp_use_domains
= d
;
445 network
->dhcp_use_domains_set
= true;
448 network
->dhcp6_use_domains
= d
;
449 network
->dhcp6_use_domains_set
= true;
452 /* For backward compatibility. */
453 if (!network
->dhcp_use_domains_set
)
454 network
->dhcp_use_domains
= d
;
455 if (!network
->dhcp6_use_domains_set
)
456 network
->dhcp6_use_domains
= d
;
459 assert_not_reached();
465 int config_parse_dhcp_use_ntp(
467 const char *filename
,
470 unsigned section_line
,
477 Network
*network
= userdata
;
482 assert(IN_SET(ltype
, AF_UNSPEC
, AF_INET
, AF_INET6
));
486 r
= parse_boolean(rvalue
);
488 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
489 "Failed to parse UseNTP=%s, ignoring assignment: %m", rvalue
);
495 network
->dhcp_use_ntp
= r
;
496 network
->dhcp_use_ntp_set
= true;
499 network
->dhcp6_use_ntp
= r
;
500 network
->dhcp6_use_ntp_set
= true;
503 /* For backward compatibility. */
504 if (!network
->dhcp_use_ntp_set
)
505 network
->dhcp_use_ntp
= r
;
506 if (!network
->dhcp6_use_ntp_set
)
507 network
->dhcp6_use_ntp
= r
;
510 assert_not_reached();
516 int config_parse_dhcp_or_ra_route_table(
518 const char *filename
,
521 unsigned section_line
,
528 Network
*network
= userdata
;
534 assert(IN_SET(ltype
, AF_INET
, AF_INET6
));
538 r
= safe_atou32(rvalue
, &rt
);
540 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
541 "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue
);
547 network
->dhcp_route_table
= rt
;
548 network
->dhcp_route_table_set
= true;
551 network
->ipv6_accept_ra_route_table
= rt
;
552 network
->ipv6_accept_ra_route_table_set
= true;
555 assert_not_reached();
561 int config_parse_iaid(
563 const char *filename
,
566 unsigned section_line
,
573 Network
*network
= userdata
;
581 assert(IN_SET(ltype
, AF_INET
, AF_INET6
));
583 r
= safe_atou32(rvalue
, &iaid
);
585 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
586 "Unable to read IAID, ignoring assignment: %s", rvalue
);
590 if (ltype
== AF_INET
) {
591 network
->dhcp_iaid
= iaid
;
592 network
->dhcp_iaid_set
= true;
593 if (!network
->dhcp6_iaid_set_explicitly
) {
594 /* Backward compatibility. Previously, IAID is shared by DHCPv4 and DHCPv6.
595 * If DHCPv6 IAID is not specified explicitly, then use DHCPv4 IAID for DHCPv6. */
596 network
->dhcp6_iaid
= iaid
;
597 network
->dhcp6_iaid_set
= true;
600 assert(ltype
== AF_INET6
);
601 network
->dhcp6_iaid
= iaid
;
602 network
->dhcp6_iaid_set
= true;
603 network
->dhcp6_iaid_set_explicitly
= true;
609 int config_parse_dhcp_user_or_vendor_class(
611 const char *filename
,
614 unsigned section_line
,
627 assert(IN_SET(ltype
, AF_INET
, AF_INET6
));
629 if (isempty(rvalue
)) {
634 for (const char *p
= rvalue
;;) {
635 _cleanup_free_
char *w
= NULL
;
638 r
= extract_first_word(&p
, &w
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_UNQUOTE
);
642 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
643 "Failed to split user classes option, ignoring: %s", rvalue
);
650 if (ltype
== AF_INET
) {
651 if (len
> UINT8_MAX
|| len
== 0) {
652 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
653 "%s length is not in the range 1…255, ignoring.", w
);
657 if (len
> UINT16_MAX
|| len
== 0) {
658 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
659 "%s length is not in the range 1…65535, ignoring.", w
);
664 r
= strv_consume(l
, TAKE_PTR(w
));
670 int config_parse_dhcp_send_option(
672 const char *filename
,
675 unsigned section_line
,
682 _cleanup_(sd_dhcp_option_unrefp
) sd_dhcp_option
*opt4
= NULL
;
683 _cleanup_(sd_dhcp6_option_unrefp
) sd_dhcp6_option
*opt6
= NULL
;
684 _unused_
_cleanup_(sd_dhcp_option_unrefp
) sd_dhcp_option
*old4
= NULL
;
685 _unused_
_cleanup_(sd_dhcp6_option_unrefp
) sd_dhcp6_option
*old6
= NULL
;
686 uint32_t uint32_data
, enterprise_identifier
= 0;
687 _cleanup_free_
char *word
= NULL
, *q
= NULL
;
688 OrderedHashmap
**options
= data
;
689 uint16_t u16
, uint16_data
;
690 union in_addr_union addr
;
691 DHCPOptionDataType type
;
692 uint8_t u8
, uint8_data
;
703 if (isempty(rvalue
)) {
704 *options
= ordered_hashmap_free(*options
);
709 if (ltype
== AF_INET6
&& streq(lvalue
, "SendVendorOption")) {
710 r
= extract_first_word(&p
, &word
, ":", 0);
713 if (r
<= 0 || isempty(p
)) {
714 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
715 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
719 r
= safe_atou32(word
, &enterprise_identifier
);
721 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
722 "Failed to parse DHCPv6 enterprise identifier data, ignoring assignment: %s", p
);
728 r
= extract_first_word(&p
, &word
, ":", 0);
731 if (r
<= 0 || isempty(p
)) {
732 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
733 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
737 if (ltype
== AF_INET6
) {
738 r
= safe_atou16(word
, &u16
);
740 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
741 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
744 if (u16
< 1 || u16
>= UINT16_MAX
) {
745 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
746 "Invalid DHCP option, valid range is 1-65535, ignoring assignment: %s", rvalue
);
750 r
= safe_atou8(word
, &u8
);
752 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
753 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
756 if (u8
< 1 || u8
>= UINT8_MAX
) {
757 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
758 "Invalid DHCP option, valid range is 1-254, ignoring assignment: %s", rvalue
);
764 r
= extract_first_word(&p
, &word
, ":", 0);
767 if (r
<= 0 || isempty(p
)) {
768 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
769 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
773 type
= dhcp_option_data_type_from_string(word
);
775 log_syntax(unit
, LOG_WARNING
, filename
, line
, type
,
776 "Invalid DHCP option data type, ignoring assignment: %s", p
);
781 case DHCP_OPTION_DATA_UINT8
:{
782 r
= safe_atou8(p
, &uint8_data
);
784 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
785 "Failed to parse DHCP uint8 data, ignoring assignment: %s", p
);
790 sz
= sizeof(uint8_t);
793 case DHCP_OPTION_DATA_UINT16
:{
796 r
= safe_atou16(p
, &k
);
798 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
799 "Failed to parse DHCP uint16 data, ignoring assignment: %s", p
);
803 uint16_data
= htobe16(k
);
804 udata
= &uint16_data
;
805 sz
= sizeof(uint16_t);
808 case DHCP_OPTION_DATA_UINT32
: {
811 r
= safe_atou32(p
, &k
);
813 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
814 "Failed to parse DHCP uint32 data, ignoring assignment: %s", p
);
818 uint32_data
= htobe32(k
);
819 udata
= &uint32_data
;
820 sz
= sizeof(uint32_t);
824 case DHCP_OPTION_DATA_IPV4ADDRESS
: {
825 r
= in_addr_from_string(AF_INET
, p
, &addr
);
827 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
828 "Failed to parse DHCP ipv4address data, ignoring assignment: %s", p
);
833 sz
= sizeof(addr
.in
.s_addr
);
836 case DHCP_OPTION_DATA_IPV6ADDRESS
: {
837 r
= in_addr_from_string(AF_INET6
, p
, &addr
);
839 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
840 "Failed to parse DHCP ipv6address data, ignoring assignment: %s", p
);
845 sz
= sizeof(addr
.in6
.s6_addr
);
848 case DHCP_OPTION_DATA_STRING
:
849 sz
= cunescape(p
, UNESCAPE_ACCEPT_NUL
, &q
);
851 log_syntax(unit
, LOG_WARNING
, filename
, line
, sz
,
852 "Failed to decode DHCP option data, ignoring assignment: %s", p
);
860 if (ltype
== AF_INET6
) {
861 r
= sd_dhcp6_option_new(u16
, udata
, sz
, enterprise_identifier
, &opt6
);
863 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
864 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
868 r
= ordered_hashmap_ensure_allocated(options
, &dhcp6_option_hash_ops
);
872 /* Overwrite existing option */
873 old6
= ordered_hashmap_get(*options
, UINT_TO_PTR(u16
));
874 r
= ordered_hashmap_replace(*options
, UINT_TO_PTR(u16
), opt6
);
876 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
877 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
882 r
= sd_dhcp_option_new(u8
, udata
, sz
, &opt4
);
884 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
885 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
889 r
= ordered_hashmap_ensure_allocated(options
, &dhcp_option_hash_ops
);
893 /* Overwrite existing option */
894 old4
= ordered_hashmap_get(*options
, UINT_TO_PTR(u8
));
895 r
= ordered_hashmap_replace(*options
, UINT_TO_PTR(u8
), opt4
);
897 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
898 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
906 int config_parse_dhcp_request_options(
908 const char *filename
,
911 unsigned section_line
,
918 Network
*network
= userdata
;
926 if (isempty(rvalue
)) {
927 if (ltype
== AF_INET
)
928 network
->dhcp_request_options
= set_free(network
->dhcp_request_options
);
930 network
->dhcp6_request_options
= set_free(network
->dhcp6_request_options
);
935 for (const char *p
= rvalue
;;) {
936 _cleanup_free_
char *n
= NULL
;
939 r
= extract_first_word(&p
, &n
, NULL
, 0);
943 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
944 "Failed to parse DHCP request option, ignoring assignment: %s",
951 r
= safe_atou32(n
, &i
);
953 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
954 "DHCP request option is invalid, ignoring assignment: %s", n
);
958 if (i
< 1 || i
>= UINT8_MAX
) {
959 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
960 "DHCP request option is invalid, valid range is 1-254, ignoring assignment: %s", n
);
964 r
= set_ensure_put(ltype
== AF_INET
? &network
->dhcp_request_options
: &network
->dhcp6_request_options
,
965 NULL
, UINT32_TO_PTR(i
));
967 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
968 "Failed to store DHCP request option '%s', ignoring assignment: %m", n
);
972 static const char* const dhcp_use_domains_table
[_DHCP_USE_DOMAINS_MAX
] = {
973 [DHCP_USE_DOMAINS_NO
] = "no",
974 [DHCP_USE_DOMAINS_ROUTE
] = "route",
975 [DHCP_USE_DOMAINS_YES
] = "yes",
978 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains
, DHCPUseDomains
, DHCP_USE_DOMAINS_YES
);
980 static const char * const dhcp_option_data_type_table
[_DHCP_OPTION_DATA_MAX
] = {
981 [DHCP_OPTION_DATA_UINT8
] = "uint8",
982 [DHCP_OPTION_DATA_UINT16
] = "uint16",
983 [DHCP_OPTION_DATA_UINT32
] = "uint32",
984 [DHCP_OPTION_DATA_STRING
] = "string",
985 [DHCP_OPTION_DATA_IPV4ADDRESS
] = "ipv4address",
986 [DHCP_OPTION_DATA_IPV6ADDRESS
] = "ipv6address",
989 DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type
, DHCPOptionDataType
);
991 static const char* const duid_type_table
[_DUID_TYPE_MAX
] = {
992 [DUID_TYPE_LLT
] = "link-layer-time",
993 [DUID_TYPE_EN
] = "vendor",
994 [DUID_TYPE_LL
] = "link-layer",
995 [DUID_TYPE_UUID
] = "uuid",
997 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(duid_type
, DUIDType
);
999 int config_parse_duid_type(
1001 const char *filename
,
1003 const char *section
,
1004 unsigned section_line
,
1011 _cleanup_free_
char *type_string
= NULL
;
1012 const char *p
= rvalue
;
1023 if (!force
&& duid
->set
)
1026 r
= extract_first_word(&p
, &type_string
, ":", 0);
1030 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1031 "Invalid syntax, ignoring: %s", rvalue
);
1035 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1036 "Failed to extract DUID type from '%s', ignoring.", rvalue
);
1040 type
= duid_type_from_string(type_string
);
1042 log_syntax(unit
, LOG_WARNING
, filename
, line
, type
,
1043 "Failed to parse DUID type '%s', ignoring.", type_string
);
1050 if (type
!= DUID_TYPE_LLT
) {
1051 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1052 "Invalid syntax, ignoring: %s", rvalue
);
1056 r
= parse_timestamp(p
, &u
);
1058 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1059 "Failed to parse timestamp, ignoring: %s", p
);
1072 int config_parse_manager_duid_type(
1074 const char *filename
,
1076 const char *section
,
1077 unsigned section_line
,
1084 Manager
*manager
= userdata
;
1089 /* For backward compatibility. Setting both DHCPv4 and DHCPv6 DUID if they are not specified explicitly. */
1091 r
= config_parse_duid_type(unit
, filename
, line
, section
, section_line
, lvalue
, false, rvalue
, &manager
->dhcp_duid
, manager
);
1095 return config_parse_duid_type(unit
, filename
, line
, section
, section_line
, lvalue
, false, rvalue
, &manager
->dhcp6_duid
, manager
);
1098 int config_parse_network_duid_type(
1100 const char *filename
,
1102 const char *section
,
1103 unsigned section_line
,
1110 Network
*network
= userdata
;
1115 r
= config_parse_duid_type(unit
, filename
, line
, section
, section_line
, lvalue
, true, rvalue
, &network
->dhcp_duid
, network
);
1119 /* For backward compatibility, also set DHCPv6 DUID if not specified explicitly. */
1120 return config_parse_duid_type(unit
, filename
, line
, section
, section_line
, lvalue
, false, rvalue
, &network
->dhcp6_duid
, network
);
1123 int config_parse_duid_rawdata(
1125 const char *filename
,
1127 const char *section
,
1128 unsigned section_line
,
1135 uint8_t raw_data
[MAX_DUID_LEN
];
1145 if (!force
&& duid
->set
)
1148 /* RawData contains DUID in format "NN:NN:NN..." */
1149 for (const char *p
= rvalue
;;) {
1152 _cleanup_free_
char *cbyte
= NULL
;
1154 r
= extract_first_word(&p
, &cbyte
, ":", 0);
1158 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Failed to read DUID, ignoring assignment: %s.", rvalue
);
1164 if (count
>= MAX_DUID_LEN
) {
1165 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue
);
1169 len
= strlen(cbyte
);
1170 if (!IN_SET(len
, 1, 2)) {
1171 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte
, rvalue
);
1174 n1
= unhexchar(cbyte
[0]);
1176 n2
= unhexchar(cbyte
[1]);
1180 if (n1
< 0 || n2
< 0) {
1181 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte
, rvalue
);
1185 byte
= ((uint8_t) n1
<< (4 * (len
-1))) | (uint8_t) n2
;
1186 raw_data
[count
++] = byte
;
1189 assert_cc(sizeof(raw_data
) == sizeof(duid
->raw_data
));
1190 memcpy(duid
->raw_data
, raw_data
, count
);
1191 duid
->raw_data_len
= count
;
1197 int config_parse_manager_duid_rawdata(
1199 const char *filename
,
1201 const char *section
,
1202 unsigned section_line
,
1209 Manager
*manager
= userdata
;
1214 /* For backward compatibility. Setting both DHCPv4 and DHCPv6 DUID if they are not specified explicitly. */
1216 r
= config_parse_duid_rawdata(unit
, filename
, line
, section
, section_line
, lvalue
, false, rvalue
, &manager
->dhcp_duid
, manager
);
1220 return config_parse_duid_rawdata(unit
, filename
, line
, section
, section_line
, lvalue
, false, rvalue
, &manager
->dhcp6_duid
, manager
);
1223 int config_parse_network_duid_rawdata(
1225 const char *filename
,
1227 const char *section
,
1228 unsigned section_line
,
1235 Network
*network
= userdata
;
1240 r
= config_parse_duid_rawdata(unit
, filename
, line
, section
, section_line
, lvalue
, true, rvalue
, &network
->dhcp_duid
, network
);
1244 /* For backward compatibility, also set DHCPv6 DUID if not specified explicitly. */
1245 return config_parse_duid_rawdata(unit
, filename
, line
, section
, section_line
, lvalue
, false, rvalue
, &network
->dhcp6_duid
, network
);
1248 int config_parse_uplink(
1250 const char *filename
,
1252 const char *section
,
1253 unsigned section_line
,
1260 Network
*network
= userdata
;
1261 bool accept_none
= true;
1271 if (streq(section
, "DHCPServer")) {
1272 index
= &network
->dhcp_server_uplink_index
;
1273 name
= &network
->dhcp_server_uplink_name
;
1274 } else if (streq(section
, "IPv6SendRA")) {
1275 index
= &network
->router_uplink_index
;
1276 name
= &network
->router_uplink_name
;
1277 } else if (STR_IN_SET(section
, "DHCPv6PrefixDelegation", "DHCPPrefixDelegation")) {
1278 index
= &network
->dhcp_pd_uplink_index
;
1279 name
= &network
->dhcp_pd_uplink_name
;
1280 accept_none
= false;
1282 assert_not_reached();
1284 if (isempty(rvalue
) || streq(rvalue
, ":auto")) {
1285 *index
= UPLINK_INDEX_AUTO
;
1286 *name
= mfree(*name
);
1290 if (accept_none
&& streq(rvalue
, ":none")) {
1291 *index
= UPLINK_INDEX_NONE
;
1292 *name
= mfree(*name
);
1296 if (!accept_none
&& streq(rvalue
, ":self")) {
1297 *index
= UPLINK_INDEX_SELF
;
1298 *name
= mfree(*name
);
1302 r
= parse_ifindex(rvalue
);
1305 *name
= mfree(*name
);
1309 if (!ifname_valid_full(rvalue
, IFNAME_VALID_ALTERNATIVE
)) {
1310 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1311 "Invalid interface name in %s=, ignoring assignment: %s", lvalue
, rvalue
);
1315 /* The interface name will be resolved later. */
1316 r
= free_and_strdup_warn(name
, rvalue
);
1320 /* Note, if uplink_name is set, then uplink_index will be ignored. So, the below does not mean
1321 * an uplink interface will be selected automatically. */
1322 *index
= UPLINK_INDEX_AUTO
;