1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <linux/if_arp.h>
4 #include <linux/rtnetlink.h>
5 #include <netinet/in.h>
8 #include "sd-dhcp6-lease.h"
9 #include "sd-dhcp6-option.h"
11 #include "alloc-util.h"
12 #include "bus-error.h"
13 #include "bus-locator.h"
14 #include "dhcp-option.h"
15 #include "dhcp6-option.h"
17 #include "extract-word.h"
18 #include "hexdecoct.h"
19 #include "in-addr-prefix-util.h"
20 #include "networkd-dhcp-common.h"
21 #include "networkd-link.h"
22 #include "networkd-manager.h"
23 #include "networkd-network.h"
24 #include "networkd-route-util.h"
25 #include "parse-util.h"
27 #include "socket-util.h"
28 #include "string-table.h"
29 #include "string-util.h"
33 static uint32_t link_get_vrf_table(Link
*link
) {
35 assert(link
->network
);
37 return link
->network
->vrf
? VRF(link
->network
->vrf
)->table
: RT_TABLE_MAIN
;
40 uint32_t link_get_dhcp4_route_table(Link
*link
) {
42 assert(link
->network
);
44 /* When the interface is part of an VRF use the VRFs routing table, unless
45 * another table is explicitly specified. */
47 if (link
->network
->dhcp_route_table_set
)
48 return link
->network
->dhcp_route_table
;
49 return link_get_vrf_table(link
);
52 uint32_t link_get_ndisc_route_table(Link
*link
) {
54 assert(link
->network
);
56 if (link
->network
->ndisc_route_table_set
)
57 return link
->network
->ndisc_route_table
;
58 return link_get_vrf_table(link
);
61 bool link_dhcp_enabled(Link
*link
, int family
) {
63 assert(IN_SET(family
, AF_INET
, AF_INET6
));
65 /* Currently, sd-dhcp-client supports only ethernet and infiniband.
66 * (ARMHRD_RAWIP and ARMHRD_NONE are typically wwan modems and will be
67 * treated as ethernet devices.) */
68 if (family
== AF_INET
&& !IN_SET(link
->iftype
, ARPHRD_ETHER
, ARPHRD_INFINIBAND
, ARPHRD_RAWIP
, ARPHRD_NONE
))
71 if (family
== AF_INET6
&& !socket_ipv6_is_supported())
74 if (link
->flags
& IFF_LOOPBACK
)
77 if (link
->iftype
== ARPHRD_CAN
)
83 return link
->network
->dhcp
& AF_TO_ADDRESS_FAMILY(family
);
86 void network_adjust_dhcp(Network
*network
) {
88 assert(network
->dhcp
>= 0);
90 if (network
->dhcp
== ADDRESS_FAMILY_NO
)
93 /* Bonding slave does not support addressing. */
95 log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.",
97 network
->dhcp
= ADDRESS_FAMILY_NO
;
101 if (!FLAGS_SET(network
->link_local
, ADDRESS_FAMILY_IPV6
) &&
102 FLAGS_SET(network
->dhcp
, ADDRESS_FAMILY_IPV6
)) {
103 log_warning("%s: DHCPv6 client is enabled but IPv6 link-local addressing is disabled. "
104 "Disabling DHCPv6 client.", network
->filename
);
105 SET_FLAG(network
->dhcp
, ADDRESS_FAMILY_IPV6
, false);
108 network_adjust_dhcp4(network
);
111 static bool duid_needs_product_uuid(const DUID
*duid
) {
114 return duid
->type
== DUID_TYPE_UUID
&& duid
->raw_data_len
== 0;
117 static const struct DUID fallback_duid
= { .type
= DUID_TYPE_EN
};
119 const DUID
*link_get_duid(Link
*link
, int family
) {
123 assert(IN_SET(family
, AF_INET
, AF_INET6
));
126 duid
= family
== AF_INET
? &link
->network
->dhcp_duid
: &link
->network
->dhcp6_duid
;
127 if (duid
->type
!= _DUID_TYPE_INVALID
) {
128 if (duid_needs_product_uuid(duid
))
129 return &link
->manager
->duid_product_uuid
;
135 duid
= family
== AF_INET
? &link
->manager
->dhcp_duid
: &link
->manager
->dhcp6_duid
;
136 if (link
->hw_addr
.length
== 0 && IN_SET(duid
->type
, DUID_TYPE_LLT
, DUID_TYPE_LL
))
137 /* Fallback to DUID that works without MAC address.
138 * This is useful for tunnel devices without MAC address. */
139 return &fallback_duid
;
144 static int get_product_uuid_handler(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
145 Manager
*manager
= ASSERT_PTR(userdata
);
146 const sd_bus_error
*e
;
153 /* To avoid calling GetProductUUID() bus method so frequently, set the flag below
154 * even if the method fails. */
155 manager
->has_product_uuid
= true;
157 e
= sd_bus_message_get_error(m
);
159 r
= sd_bus_error_get_errno(e
);
160 log_warning_errno(r
, "Could not get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %s",
161 bus_error_message(e
, r
));
165 r
= sd_bus_message_read_array(m
, 'y', &a
, &sz
);
167 log_warning_errno(r
, "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m");
171 if (sz
!= sizeof(sd_id128_t
)) {
172 log_warning("Invalid product UUID. Falling back to use machine-app-specific ID as DUID-UUID.");
176 log_debug("Successfully obtained product UUID");
178 memcpy(&manager
->duid_product_uuid
.raw_data
, a
, sz
);
179 manager
->duid_product_uuid
.raw_data_len
= sz
;
184 int manager_request_product_uuid(Manager
*m
) {
185 static bool bus_method_is_called
= false;
190 if (bus_method_is_called
)
193 if (sd_bus_is_ready(m
->bus
) <= 0 && !m
->product_uuid_requested
) {
194 log_debug("Not connected to system bus, requesting product UUID later.");
195 m
->product_uuid_requested
= true;
199 m
->product_uuid_requested
= false;
201 r
= bus_call_method_async(
206 get_product_uuid_handler
,
211 return log_warning_errno(r
, "Failed to get product UUID: %m");
213 log_debug("Requesting product UUID.");
215 bus_method_is_called
= true;
220 int dhcp_configure_duid(Link
*link
, const DUID
*duid
) {
225 assert(link
->manager
);
230 if (!duid_needs_product_uuid(duid
))
233 if (m
->has_product_uuid
)
236 r
= manager_request_product_uuid(m
);
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");
241 m
->has_product_uuid
= true; /* Do not request UUID again on failure. */
248 bool address_is_filtered(int family
, const union in_addr_union
*address
, uint8_t prefixlen
, Set
*allow_list
, Set
*deny_list
) {
249 struct in_addr_prefix
*p
;
251 assert(IN_SET(family
, AF_INET
, AF_INET6
));
255 SET_FOREACH(p
, allow_list
)
256 if (p
->family
== family
&&
257 p
->prefixlen
<= prefixlen
&&
258 in_addr_prefix_covers(family
, &p
->address
, p
->prefixlen
, address
) > 0)
264 SET_FOREACH(p
, deny_list
)
265 if (p
->family
== family
&&
266 in_addr_prefix_intersect(family
, &p
->address
, p
->prefixlen
, address
, prefixlen
) > 0)
272 int link_get_captive_portal(Link
*link
, const char **ret
) {
273 const char *dhcp4_cp
= NULL
, *dhcp6_cp
= NULL
, *ndisc_cp
= NULL
;
278 if (!link
->network
) {
283 if (link
->network
->dhcp_use_captive_portal
&& link
->dhcp_lease
) {
284 r
= sd_dhcp_lease_get_captive_portal(link
->dhcp_lease
, &dhcp4_cp
);
285 if (r
< 0 && r
!= -ENODATA
)
289 if (link
->network
->dhcp6_use_captive_portal
&& link
->dhcp6_lease
) {
290 r
= sd_dhcp6_lease_get_captive_portal(link
->dhcp6_lease
, &dhcp6_cp
);
291 if (r
< 0 && r
!= -ENODATA
)
295 if (link
->network
->ndisc_use_captive_portal
) {
296 NDiscCaptivePortal
*cp
;
299 /* Use the captive portal with the longest lifetime. */
301 SET_FOREACH(cp
, link
->ndisc_captive_portals
) {
302 if (cp
->lifetime_usec
< usec
)
305 ndisc_cp
= cp
->captive_portal
;
306 usec
= cp
->lifetime_usec
;
309 if (set_size(link
->ndisc_captive_portals
) > 1)
310 log_link_debug(link
, "Multiple captive portals obtained by IPv6RA, using \"%s\" and ignoring others.",
315 if (dhcp6_cp
&& !streq(dhcp4_cp
, dhcp6_cp
))
316 log_link_debug(link
, "DHCPv6 captive portal (%s) does not match DHCPv4 (%s), ignoring DHCPv6 captive portal.",
319 if (ndisc_cp
&& !streq(dhcp4_cp
, ndisc_cp
))
320 log_link_debug(link
, "IPv6RA captive portal (%s) does not match DHCPv4 (%s), ignoring IPv6RA captive portal.",
328 if (ndisc_cp
&& !streq(dhcp6_cp
, ndisc_cp
))
329 log_link_debug(link
, "IPv6RA captive portal (%s) does not match DHCPv6 (%s), ignoring IPv6RA captive portal.",
340 int config_parse_dhcp(
342 const char *filename
,
345 unsigned section_line
,
352 AddressFamily
*dhcp
= data
, s
;
359 /* Note that this is mostly like
360 * config_parse_address_family(), except that it
361 * understands some old names for the enum values */
363 s
= address_family_from_string(rvalue
);
366 /* Previously, we had a slightly different enum here,
367 * support its values for compatibility. */
369 s
= dhcp_deprecated_address_family_from_string(rvalue
);
371 log_syntax(unit
, LOG_WARNING
, filename
, line
, s
,
372 "Failed to parse DHCP option, ignoring: %s", rvalue
);
376 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
377 "DHCP=%s is deprecated, please use DHCP=%s instead.",
378 rvalue
, address_family_to_string(s
));
385 int config_parse_dhcp_route_metric(
387 const char *filename
,
390 unsigned section_line
,
397 Network
*network
= userdata
;
403 assert(IN_SET(ltype
, AF_UNSPEC
, AF_INET
));
407 r
= safe_atou32(rvalue
, &metric
);
409 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
410 "Failed to parse RouteMetric=%s, ignoring assignment: %m", rvalue
);
416 network
->dhcp_route_metric
= metric
;
417 network
->dhcp_route_metric_set
= true;
420 /* For backward compatibility. */
421 if (!network
->dhcp_route_metric_set
)
422 network
->dhcp_route_metric
= metric
;
423 if (!network
->ndisc_route_metric_set
) {
424 network
->ndisc_route_metric_high
= metric
;
425 network
->ndisc_route_metric_medium
= metric
;
426 network
->ndisc_route_metric_low
= metric
;
430 assert_not_reached();
436 int config_parse_ndisc_route_metric(
438 const char *filename
,
441 unsigned section_line
,
448 Network
*network
= ASSERT_PTR(userdata
);
449 uint32_t metric_high
, metric_medium
, metric_low
;
455 if (safe_atou32(rvalue
, &metric_low
) >= 0)
456 metric_high
= metric_medium
= metric_low
;
458 _cleanup_free_
char *high
= NULL
, *medium
= NULL
, *low
= NULL
;
459 const char *p
= rvalue
;
461 r
= extract_many_words(&p
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
, &high
, &medium
, &low
);
464 if (r
!= 3 || !isempty(p
)) {
465 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
< 0 ? r
: 0,
466 "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue
);
470 r
= safe_atou32(high
, &metric_high
);
471 s
= safe_atou32(medium
, &metric_medium
);
472 t
= safe_atou32(low
, &metric_low
);
473 if (r
< 0 || s
< 0 || t
< 0) {
474 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
< 0 ? r
: s
< 0 ? s
: t
,
475 "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue
);
479 if (metric_high
>= metric_medium
|| metric_medium
>= metric_low
) {
480 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
481 "Invalid RouteTable=%s, ignoring assignment: %m", rvalue
);
486 network
->ndisc_route_metric_high
= metric_high
;
487 network
->ndisc_route_metric_medium
= metric_medium
;
488 network
->ndisc_route_metric_low
= metric_low
;
489 network
->ndisc_route_metric_set
= true;
494 int config_parse_dhcp_send_hostname(
496 const char *filename
,
499 unsigned section_line
,
506 Network
*network
= userdata
;
511 assert(IN_SET(ltype
, AF_UNSPEC
, AF_INET
, AF_INET6
));
515 r
= parse_boolean(rvalue
);
517 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
518 "Failed to parse SendHostname=%s, ignoring assignment: %m", rvalue
);
524 network
->dhcp_send_hostname
= r
;
525 network
->dhcp_send_hostname_set
= true;
528 network
->dhcp6_send_hostname
= r
;
529 network
->dhcp6_send_hostname_set
= true;
532 /* For backward compatibility. */
533 if (!network
->dhcp_send_hostname_set
)
534 network
->dhcp_send_hostname
= r
;
535 if (!network
->dhcp6_send_hostname_set
)
536 network
->dhcp6_send_hostname
= r
;
539 assert_not_reached();
545 int config_parse_dhcp_or_ra_route_table(
547 const char *filename
,
550 unsigned section_line
,
557 Network
*network
= ASSERT_PTR(userdata
);
563 assert(IN_SET(ltype
, AF_INET
, AF_INET6
));
566 r
= manager_get_route_table_from_string(network
->manager
, rvalue
, &rt
);
568 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
569 "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue
);
575 network
->dhcp_route_table
= rt
;
576 network
->dhcp_route_table_set
= true;
579 network
->ndisc_route_table
= rt
;
580 network
->ndisc_route_table_set
= true;
583 assert_not_reached();
589 int config_parse_iaid(
591 const char *filename
,
594 unsigned section_line
,
601 Network
*network
= ASSERT_PTR(userdata
);
608 assert(IN_SET(ltype
, AF_INET
, AF_INET6
));
610 r
= safe_atou32(rvalue
, &iaid
);
612 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
613 "Unable to read IAID, ignoring assignment: %s", rvalue
);
617 if (ltype
== AF_INET
) {
618 network
->dhcp_iaid
= iaid
;
619 network
->dhcp_iaid_set
= true;
620 if (!network
->dhcp6_iaid_set_explicitly
) {
621 /* Backward compatibility. Previously, IAID is shared by DHCPv4 and DHCPv6.
622 * If DHCPv6 IAID is not specified explicitly, then use DHCPv4 IAID for DHCPv6. */
623 network
->dhcp6_iaid
= iaid
;
624 network
->dhcp6_iaid_set
= true;
627 assert(ltype
== AF_INET6
);
628 network
->dhcp6_iaid
= iaid
;
629 network
->dhcp6_iaid_set
= true;
630 network
->dhcp6_iaid_set_explicitly
= true;
636 int config_parse_dhcp_user_or_vendor_class(
638 const char *filename
,
641 unsigned section_line
,
648 char ***l
= ASSERT_PTR(data
);
653 assert(IN_SET(ltype
, AF_INET
, AF_INET6
));
655 if (isempty(rvalue
)) {
660 for (const char *p
= rvalue
;;) {
661 _cleanup_free_
char *w
= NULL
;
664 r
= extract_first_word(&p
, &w
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_UNQUOTE
);
668 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
669 "Failed to split user classes option, ignoring: %s", rvalue
);
676 if (ltype
== AF_INET
) {
677 if (len
> UINT8_MAX
|| len
== 0) {
678 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
679 "%s length is not in the range 1…255, ignoring.", w
);
683 if (len
> UINT16_MAX
|| len
== 0) {
684 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
685 "%s length is not in the range 1…65535, ignoring.", w
);
690 r
= strv_consume(l
, TAKE_PTR(w
));
696 int config_parse_dhcp_send_option(
698 const char *filename
,
701 unsigned section_line
,
708 _cleanup_(sd_dhcp_option_unrefp
) sd_dhcp_option
*opt4
= NULL
;
709 _cleanup_(sd_dhcp6_option_unrefp
) sd_dhcp6_option
*opt6
= NULL
;
710 _unused_
_cleanup_(sd_dhcp_option_unrefp
) sd_dhcp_option
*old4
= NULL
;
711 _unused_
_cleanup_(sd_dhcp6_option_unrefp
) sd_dhcp6_option
*old6
= NULL
;
712 uint32_t uint32_data
, enterprise_identifier
= 0;
713 _cleanup_free_
char *word
= NULL
, *q
= NULL
;
714 OrderedHashmap
**options
= ASSERT_PTR(data
);
715 uint16_t u16
, uint16_data
;
716 union in_addr_union addr
;
717 DHCPOptionDataType type
;
718 uint8_t u8
, uint8_data
;
728 if (isempty(rvalue
)) {
729 *options
= ordered_hashmap_free(*options
);
734 if (ltype
== AF_INET6
&& streq(lvalue
, "SendVendorOption")) {
735 r
= extract_first_word(&p
, &word
, ":", 0);
738 if (r
<= 0 || isempty(p
)) {
739 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
740 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
744 r
= safe_atou32(word
, &enterprise_identifier
);
746 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
747 "Failed to parse DHCPv6 enterprise identifier data, ignoring assignment: %s", p
);
753 r
= extract_first_word(&p
, &word
, ":", 0);
756 if (r
<= 0 || isempty(p
)) {
757 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
758 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
762 if (ltype
== AF_INET6
) {
763 r
= safe_atou16(word
, &u16
);
765 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
766 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
769 if (u16
< 1 || u16
>= UINT16_MAX
) {
770 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
771 "Invalid DHCP option, valid range is 1-65535, ignoring assignment: %s", rvalue
);
775 r
= safe_atou8(word
, &u8
);
777 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
778 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
781 if (u8
< 1 || u8
>= UINT8_MAX
) {
782 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
783 "Invalid DHCP option, valid range is 1-254, ignoring assignment: %s", rvalue
);
789 r
= extract_first_word(&p
, &word
, ":", 0);
792 if (r
<= 0 || isempty(p
)) {
793 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
794 "Invalid DHCP option, ignoring assignment: %s", rvalue
);
798 type
= dhcp_option_data_type_from_string(word
);
800 log_syntax(unit
, LOG_WARNING
, filename
, line
, type
,
801 "Invalid DHCP option data type, ignoring assignment: %s", p
);
806 case DHCP_OPTION_DATA_UINT8
:{
807 r
= safe_atou8(p
, &uint8_data
);
809 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
810 "Failed to parse DHCP uint8 data, ignoring assignment: %s", p
);
815 sz
= sizeof(uint8_t);
818 case DHCP_OPTION_DATA_UINT16
:{
821 r
= safe_atou16(p
, &k
);
823 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
824 "Failed to parse DHCP uint16 data, ignoring assignment: %s", p
);
828 uint16_data
= htobe16(k
);
829 udata
= &uint16_data
;
830 sz
= sizeof(uint16_t);
833 case DHCP_OPTION_DATA_UINT32
: {
836 r
= safe_atou32(p
, &k
);
838 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
839 "Failed to parse DHCP uint32 data, ignoring assignment: %s", p
);
843 uint32_data
= htobe32(k
);
844 udata
= &uint32_data
;
845 sz
= sizeof(uint32_t);
849 case DHCP_OPTION_DATA_IPV4ADDRESS
: {
850 r
= in_addr_from_string(AF_INET
, p
, &addr
);
852 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
853 "Failed to parse DHCP ipv4address data, ignoring assignment: %s", p
);
858 sz
= sizeof(addr
.in
.s_addr
);
861 case DHCP_OPTION_DATA_IPV6ADDRESS
: {
862 r
= in_addr_from_string(AF_INET6
, p
, &addr
);
864 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
865 "Failed to parse DHCP ipv6address data, ignoring assignment: %s", p
);
870 sz
= sizeof(addr
.in6
.s6_addr
);
873 case DHCP_OPTION_DATA_STRING
:
874 sz
= cunescape(p
, UNESCAPE_ACCEPT_NUL
, &q
);
876 log_syntax(unit
, LOG_WARNING
, filename
, line
, sz
,
877 "Failed to decode DHCP option data, ignoring assignment: %s", p
);
887 if (ltype
== AF_INET6
) {
888 r
= sd_dhcp6_option_new(u16
, udata
, sz
, enterprise_identifier
, &opt6
);
890 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
891 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
895 r
= ordered_hashmap_ensure_allocated(options
, &dhcp6_option_hash_ops
);
899 /* Overwrite existing option */
900 old6
= ordered_hashmap_get(*options
, UINT_TO_PTR(u16
));
901 r
= ordered_hashmap_replace(*options
, UINT_TO_PTR(u16
), opt6
);
903 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
904 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
909 r
= sd_dhcp_option_new(u8
, udata
, sz
, &opt4
);
911 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
912 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
916 r
= ordered_hashmap_ensure_allocated(options
, &dhcp_option_hash_ops
);
920 /* Overwrite existing option */
921 old4
= ordered_hashmap_get(*options
, UINT_TO_PTR(u8
));
922 r
= ordered_hashmap_replace(*options
, UINT_TO_PTR(u8
), opt4
);
924 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
925 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue
);
933 int config_parse_dhcp_request_options(
935 const char *filename
,
938 unsigned section_line
,
945 Network
*network
= userdata
;
953 if (isempty(rvalue
)) {
954 if (ltype
== AF_INET
)
955 network
->dhcp_request_options
= set_free(network
->dhcp_request_options
);
957 network
->dhcp6_request_options
= set_free(network
->dhcp6_request_options
);
962 for (const char *p
= rvalue
;;) {
963 _cleanup_free_
char *n
= NULL
;
966 r
= extract_first_word(&p
, &n
, NULL
, 0);
970 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
971 "Failed to parse DHCP request option, ignoring assignment: %s",
978 r
= safe_atou32(n
, &i
);
980 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
981 "DHCP request option is invalid, ignoring assignment: %s", n
);
985 if (i
< 1 || i
>= UINT8_MAX
) {
986 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
987 "DHCP request option is invalid, valid range is 1-254, ignoring assignment: %s", n
);
991 r
= set_ensure_put(ltype
== AF_INET
? &network
->dhcp_request_options
: &network
->dhcp6_request_options
,
992 NULL
, UINT32_TO_PTR(i
));
994 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
995 "Failed to store DHCP request option '%s', ignoring assignment: %m", n
);
999 static const char * const dhcp_option_data_type_table
[_DHCP_OPTION_DATA_MAX
] = {
1000 [DHCP_OPTION_DATA_UINT8
] = "uint8",
1001 [DHCP_OPTION_DATA_UINT16
] = "uint16",
1002 [DHCP_OPTION_DATA_UINT32
] = "uint32",
1003 [DHCP_OPTION_DATA_STRING
] = "string",
1004 [DHCP_OPTION_DATA_IPV4ADDRESS
] = "ipv4address",
1005 [DHCP_OPTION_DATA_IPV6ADDRESS
] = "ipv6address",
1008 DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type
, DHCPOptionDataType
);
1010 static const char* const duid_type_table
[_DUID_TYPE_MAX
] = {
1011 [DUID_TYPE_LLT
] = "link-layer-time",
1012 [DUID_TYPE_EN
] = "vendor",
1013 [DUID_TYPE_LL
] = "link-layer",
1014 [DUID_TYPE_UUID
] = "uuid",
1016 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(duid_type
, DUIDType
);
1018 int config_parse_duid_type(
1020 const char *filename
,
1022 const char *section
,
1023 unsigned section_line
,
1030 _cleanup_free_
char *type_string
= NULL
;
1031 const char *p
= ASSERT_PTR(rvalue
);
1033 DUID
*duid
= ASSERT_PTR(data
);
1040 if (!force
&& duid
->set
)
1043 r
= extract_first_word(&p
, &type_string
, ":", 0);
1047 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1048 "Invalid syntax, ignoring: %s", rvalue
);
1052 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1053 "Failed to extract DUID type from '%s', ignoring.", rvalue
);
1057 type
= duid_type_from_string(type_string
);
1061 r
= safe_atou16(type_string
, &t
);
1063 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1064 "Failed to parse DUID type '%s', ignoring.", type_string
);
1069 assert(type
== t
); /* Check if type can store uint16_t. */
1075 if (type
!= DUID_TYPE_LLT
) {
1076 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1077 "Invalid syntax, ignoring: %s", rvalue
);
1081 r
= parse_timestamp(p
, &u
);
1083 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1084 "Failed to parse timestamp, ignoring: %s", p
);
1097 int config_parse_manager_duid_type(
1099 const char *filename
,
1101 const char *section
,
1102 unsigned section_line
,
1109 Manager
*manager
= ASSERT_PTR(userdata
);
1112 /* For backward compatibility. Setting both DHCPv4 and DHCPv6 DUID if they are not specified explicitly. */
1114 r
= config_parse_duid_type(unit
, filename
, line
, section
, section_line
, lvalue
, false, rvalue
, &manager
->dhcp_duid
, manager
);
1118 return config_parse_duid_type(unit
, filename
, line
, section
, section_line
, lvalue
, false, rvalue
, &manager
->dhcp6_duid
, manager
);
1121 int config_parse_network_duid_type(
1123 const char *filename
,
1125 const char *section
,
1126 unsigned section_line
,
1133 Network
*network
= ASSERT_PTR(userdata
);
1136 r
= config_parse_duid_type(unit
, filename
, line
, section
, section_line
, lvalue
, true, rvalue
, &network
->dhcp_duid
, network
);
1140 /* For backward compatibility, also set DHCPv6 DUID if not specified explicitly. */
1141 return config_parse_duid_type(unit
, filename
, line
, section
, section_line
, lvalue
, false, rvalue
, &network
->dhcp6_duid
, network
);
1144 int config_parse_duid_rawdata(
1146 const char *filename
,
1148 const char *section
,
1149 unsigned section_line
,
1156 uint8_t raw_data
[MAX_DUID_DATA_LEN
];
1159 DUID
*duid
= ASSERT_PTR(data
);
1165 if (!force
&& duid
->set
)
1168 /* RawData contains DUID in format "NN:NN:NN..." */
1169 for (const char *p
= rvalue
;;) {
1172 _cleanup_free_
char *cbyte
= NULL
;
1174 r
= extract_first_word(&p
, &cbyte
, ":", 0);
1178 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Failed to read DUID, ignoring assignment: %s.", rvalue
);
1184 if (count
>= MAX_DUID_DATA_LEN
) {
1185 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue
);
1189 len
= strlen(cbyte
);
1190 if (!IN_SET(len
, 1, 2)) {
1191 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte
, rvalue
);
1194 n1
= unhexchar(cbyte
[0]);
1196 n2
= unhexchar(cbyte
[1]);
1200 if (n1
< 0 || n2
< 0) {
1201 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte
, rvalue
);
1205 byte
= ((uint8_t) n1
<< (4 * (len
-1))) | (uint8_t) n2
;
1206 raw_data
[count
++] = byte
;
1209 assert_cc(sizeof(raw_data
) == sizeof(duid
->raw_data
));
1210 memcpy(duid
->raw_data
, raw_data
, count
);
1211 duid
->raw_data_len
= count
;
1217 int config_parse_manager_duid_rawdata(
1219 const char *filename
,
1221 const char *section
,
1222 unsigned section_line
,
1229 Manager
*manager
= ASSERT_PTR(userdata
);
1232 /* For backward compatibility. Setting both DHCPv4 and DHCPv6 DUID if they are not specified explicitly. */
1234 r
= config_parse_duid_rawdata(unit
, filename
, line
, section
, section_line
, lvalue
, false, rvalue
, &manager
->dhcp_duid
, manager
);
1238 return config_parse_duid_rawdata(unit
, filename
, line
, section
, section_line
, lvalue
, false, rvalue
, &manager
->dhcp6_duid
, manager
);
1241 int config_parse_network_duid_rawdata(
1243 const char *filename
,
1245 const char *section
,
1246 unsigned section_line
,
1253 Network
*network
= ASSERT_PTR(userdata
);
1256 r
= config_parse_duid_rawdata(unit
, filename
, line
, section
, section_line
, lvalue
, true, rvalue
, &network
->dhcp_duid
, network
);
1260 /* For backward compatibility, also set DHCPv6 DUID if not specified explicitly. */
1261 return config_parse_duid_rawdata(unit
, filename
, line
, section
, section_line
, lvalue
, false, rvalue
, &network
->dhcp6_duid
, network
);
1264 int config_parse_uplink(
1266 const char *filename
,
1268 const char *section
,
1269 unsigned section_line
,
1276 Network
*network
= ASSERT_PTR(userdata
);
1277 bool accept_none
= true;
1286 if (streq(section
, "DHCPServer")) {
1287 index
= &network
->dhcp_server_uplink_index
;
1288 name
= &network
->dhcp_server_uplink_name
;
1289 } else if (streq(section
, "IPv6SendRA")) {
1290 index
= &network
->router_uplink_index
;
1291 name
= &network
->router_uplink_name
;
1292 } else if (STR_IN_SET(section
, "DHCPv6PrefixDelegation", "DHCPPrefixDelegation")) {
1293 index
= &network
->dhcp_pd_uplink_index
;
1294 name
= &network
->dhcp_pd_uplink_name
;
1295 accept_none
= false;
1297 assert_not_reached();
1299 if (isempty(rvalue
) || streq(rvalue
, ":auto")) {
1300 *index
= UPLINK_INDEX_AUTO
;
1301 *name
= mfree(*name
);
1305 if (accept_none
&& streq(rvalue
, ":none")) {
1306 *index
= UPLINK_INDEX_NONE
;
1307 *name
= mfree(*name
);
1311 if (!accept_none
&& streq(rvalue
, ":self")) {
1312 *index
= UPLINK_INDEX_SELF
;
1313 *name
= mfree(*name
);
1317 r
= parse_ifindex(rvalue
);
1320 *name
= mfree(*name
);
1324 if (!ifname_valid_full(rvalue
, IFNAME_VALID_ALTERNATIVE
)) {
1325 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1326 "Invalid interface name in %s=, ignoring assignment: %s", lvalue
, rvalue
);
1330 /* The interface name will be resolved later. */
1331 r
= free_and_strdup_warn(name
, rvalue
);
1335 /* Note, if uplink_name is set, then uplink_index will be ignored. So, the below does not mean
1336 * an uplink interface will be selected automatically. */
1337 *index
= UPLINK_INDEX_AUTO
;