1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013-2014 Tom Gundersen <teg@jklm.no>
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include <netinet/ether.h>
24 #include "alloc-util.h"
25 #include "dhcp-lease-internal.h"
26 #include "hostname-util.h"
27 #include "parse-util.h"
28 #include "netdev/vrf.h"
29 #include "network-internal.h"
30 #include "networkd-link.h"
31 #include "networkd-manager.h"
32 #include "networkd-network.h"
33 #include "string-util.h"
34 #include "sysctl-util.h"
36 static int dhcp4_route_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
,
38 _cleanup_link_unref_ Link
*link
= userdata
;
42 assert(link
->dhcp4_messages
> 0);
44 link
->dhcp4_messages
--;
46 r
= sd_netlink_message_get_errno(m
);
47 if (r
< 0 && r
!= -EEXIST
) {
48 log_link_error_errno(link
, r
, "Could not set DHCPv4 route: %m");
49 link_enter_failed(link
);
52 if (link
->dhcp4_messages
== 0) {
53 link
->dhcp4_configured
= true;
54 link_check_ready(link
);
60 static int route_scope_from_address(const Route
*route
, const struct in_addr
*self_addr
) {
64 if (in_addr_is_localhost(AF_INET
, &route
->dst
) ||
65 (self_addr
->s_addr
&& route
->dst
.in
.s_addr
== self_addr
->s_addr
))
67 else if (in4_addr_is_null(&route
->gw
.in
))
70 return RT_SCOPE_UNIVERSE
;
73 static int link_set_dhcp_routes(Link
*link
) {
74 _cleanup_free_ sd_dhcp_route
**static_routes
= NULL
;
75 bool classless_route
= false, static_route
= false;
76 struct in_addr gateway
, address
;
82 if (!link
->dhcp_lease
) /* link went down while we configured the IP addresses? */
85 if (!link
->network
) /* link went down while we configured the IP addresses? */
88 if (!link
->network
->dhcp_use_routes
)
91 /* When the interface is part of an VRF use the VRFs routing table, unless
92 * there is a another table specified. */
93 table
= link
->network
->dhcp_route_table
;
94 if (!link
->network
->dhcp_route_table_set
&& link
->network
->vrf
!= NULL
)
95 table
= VRF(link
->network
->vrf
)->table
;
97 r
= sd_dhcp_lease_get_address(link
->dhcp_lease
, &address
);
99 return log_link_warning_errno(link
, r
, "DHCP error: could not get address: %m");
101 n
= sd_dhcp_lease_get_routes(link
->dhcp_lease
, &static_routes
);
103 log_link_debug_errno(link
, n
, "DHCP error: could not get routes: %m");
105 for (i
= 0; i
< n
; i
++) {
106 if (static_routes
[i
]->option
== SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE
)
107 classless_route
= true;
109 if (static_routes
[i
]->option
== SD_DHCP_OPTION_STATIC_ROUTE
)
113 for (i
= 0; i
< n
; i
++) {
114 _cleanup_route_free_ Route
*route
= NULL
;
116 /* if the DHCP server returns both a Classless Static Routes option and a Static Routes option,
117 the DHCP client MUST ignore the Static Routes option. */
118 if (classless_route
&& static_routes
[i
]->option
== SD_DHCP_OPTION_STATIC_ROUTE
)
121 r
= route_new(&route
);
123 return log_link_error_errno(link
, r
, "Could not allocate route: %m");
125 route
->family
= AF_INET
;
126 route
->protocol
= RTPROT_DHCP
;
127 assert_se(sd_dhcp_route_get_gateway(static_routes
[i
], &route
->gw
.in
) >= 0);
128 assert_se(sd_dhcp_route_get_destination(static_routes
[i
], &route
->dst
.in
) >= 0);
129 assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes
[i
], &route
->dst_prefixlen
) >= 0);
130 route
->priority
= link
->network
->dhcp_route_metric
;
131 route
->table
= table
;
132 route
->scope
= route_scope_from_address(route
, &address
);
134 r
= route_configure(route
, link
, dhcp4_route_handler
);
136 return log_link_warning_errno(link
, r
, "Could not set host route: %m");
138 link
->dhcp4_messages
++;
141 r
= sd_dhcp_lease_get_router(link
->dhcp_lease
, &gateway
);
143 log_link_info_errno(link
, r
, "DHCP: No routes received from DHCP server: %m");
145 log_link_warning_errno(link
, r
, "DHCP error: could not get gateway: %m");
147 /* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and
148 a Router option, the DHCP client MUST ignore the Router option. */
149 if (classless_route
&& static_route
)
150 log_link_warning(link
, "Classless static routes received from DHCP server: ignoring static-route option and router option");
152 if (r
>= 0 && !classless_route
) {
153 _cleanup_route_free_ Route
*route
= NULL
;
154 _cleanup_route_free_ Route
*route_gw
= NULL
;
156 r
= route_new(&route
);
158 return log_link_error_errno(link
, r
, "Could not allocate route: %m");
160 route
->protocol
= RTPROT_DHCP
;
162 r
= route_new(&route_gw
);
164 return log_link_error_errno(link
, r
, "Could not allocate route: %m");
166 /* The dhcp netmask may mask out the gateway. Add an explicit
167 * route for the gw host so that we can route no matter the
168 * netmask or existing kernel route tables. */
169 route_gw
->family
= AF_INET
;
170 route_gw
->dst
.in
= gateway
;
171 route_gw
->dst_prefixlen
= 32;
172 route_gw
->prefsrc
.in
= address
;
173 route_gw
->scope
= RT_SCOPE_LINK
;
174 route_gw
->protocol
= RTPROT_DHCP
;
175 route_gw
->priority
= link
->network
->dhcp_route_metric
;
176 route_gw
->table
= table
;
178 r
= route_configure(route_gw
, link
, dhcp4_route_handler
);
180 return log_link_warning_errno(link
, r
, "Could not set host route: %m");
182 link
->dhcp4_messages
++;
184 route
->family
= AF_INET
;
185 route
->gw
.in
= gateway
;
186 route
->prefsrc
.in
= address
;
187 route
->priority
= link
->network
->dhcp_route_metric
;
188 route
->table
= table
;
190 r
= route_configure(route
, link
, dhcp4_route_handler
);
192 log_link_warning_errno(link
, r
, "Could not set routes: %m");
193 link_enter_failed(link
);
197 link
->dhcp4_messages
++;
203 static int dhcp_lease_lost(Link
*link
) {
204 _cleanup_address_free_ Address
*address
= NULL
;
206 struct in_addr netmask
;
207 struct in_addr gateway
;
208 unsigned prefixlen
= 0;
212 assert(link
->dhcp_lease
);
214 log_link_warning(link
, "DHCP lease lost");
216 if (link
->network
->dhcp_use_routes
) {
217 _cleanup_free_ sd_dhcp_route
**routes
= NULL
;
220 n
= sd_dhcp_lease_get_routes(link
->dhcp_lease
, &routes
);
222 for (i
= 0; i
< n
; i
++) {
223 _cleanup_route_free_ Route
*route
= NULL
;
225 r
= route_new(&route
);
227 route
->family
= AF_INET
;
228 assert_se(sd_dhcp_route_get_gateway(routes
[i
], &route
->gw
.in
) >= 0);
229 assert_se(sd_dhcp_route_get_destination(routes
[i
], &route
->dst
.in
) >= 0);
230 assert_se(sd_dhcp_route_get_destination_prefix_length(routes
[i
], &route
->dst_prefixlen
) >= 0);
232 route_remove(route
, link
,
233 link_route_remove_handler
);
239 r
= address_new(&address
);
241 r
= sd_dhcp_lease_get_router(link
->dhcp_lease
, &gateway
);
243 _cleanup_route_free_ Route
*route_gw
= NULL
;
244 _cleanup_route_free_ Route
*route
= NULL
;
246 r
= route_new(&route_gw
);
248 route_gw
->family
= AF_INET
;
249 route_gw
->dst
.in
= gateway
;
250 route_gw
->dst_prefixlen
= 32;
251 route_gw
->scope
= RT_SCOPE_LINK
;
253 route_remove(route_gw
, link
,
254 link_route_remove_handler
);
257 r
= route_new(&route
);
259 route
->family
= AF_INET
;
260 route
->gw
.in
= gateway
;
262 route_remove(route
, link
,
263 link_route_remove_handler
);
267 r
= sd_dhcp_lease_get_address(link
->dhcp_lease
, &addr
);
269 r
= sd_dhcp_lease_get_netmask(link
->dhcp_lease
, &netmask
);
271 prefixlen
= in4_addr_netmask_to_prefixlen(&netmask
);
273 address
->family
= AF_INET
;
274 address
->in_addr
.in
= addr
;
275 address
->prefixlen
= prefixlen
;
277 address_remove(address
, link
, link_address_remove_handler
);
281 if (link
->network
->dhcp_use_mtu
) {
284 r
= sd_dhcp_lease_get_mtu(link
->dhcp_lease
, &mtu
);
285 if (r
>= 0 && link
->original_mtu
!= mtu
) {
286 r
= link_set_mtu(link
, link
->original_mtu
);
288 log_link_warning(link
,
289 "DHCP error: could not reset MTU");
290 link_enter_failed(link
);
296 if (link
->network
->dhcp_use_hostname
) {
297 const char *hostname
= NULL
;
299 if (link
->network
->dhcp_hostname
)
300 hostname
= link
->network
->dhcp_hostname
;
302 (void) sd_dhcp_lease_get_hostname(link
->dhcp_lease
, &hostname
);
305 /* If a hostname was set due to the lease, then unset it now. */
306 r
= manager_set_hostname(link
->manager
, NULL
);
308 log_link_warning_errno(link
, r
, "Failed to reset transient hostname: %m");
312 link
->dhcp_lease
= sd_dhcp_lease_unref(link
->dhcp_lease
);
314 link
->dhcp4_configured
= false;
319 static int dhcp4_address_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
,
321 _cleanup_link_unref_ Link
*link
= userdata
;
326 r
= sd_netlink_message_get_errno(m
);
327 if (r
< 0 && r
!= -EEXIST
) {
328 log_link_error_errno(link
, r
, "Could not set DHCPv4 address: %m");
329 link_enter_failed(link
);
331 manager_rtnl_process_address(rtnl
, m
, link
->manager
);
333 link_set_dhcp_routes(link
);
338 static int dhcp4_update_address(Link
*link
,
339 struct in_addr
*address
,
340 struct in_addr
*netmask
,
342 _cleanup_address_free_ Address
*addr
= NULL
;
350 prefixlen
= in4_addr_netmask_to_prefixlen(netmask
);
352 r
= address_new(&addr
);
356 addr
->family
= AF_INET
;
357 addr
->in_addr
.in
.s_addr
= address
->s_addr
;
358 addr
->cinfo
.ifa_prefered
= lifetime
;
359 addr
->cinfo
.ifa_valid
= lifetime
;
360 addr
->prefixlen
= prefixlen
;
361 addr
->broadcast
.s_addr
= address
->s_addr
| ~netmask
->s_addr
;
363 /* allow reusing an existing address and simply update its lifetime
364 * in case it already exists */
365 r
= address_configure(addr
, link
, dhcp4_address_handler
, true);
372 static int dhcp_lease_renew(sd_dhcp_client
*client
, Link
*link
) {
373 sd_dhcp_lease
*lease
;
374 struct in_addr address
;
375 struct in_addr netmask
;
376 uint32_t lifetime
= CACHE_INFO_INFINITY_LIFE_TIME
;
381 assert(link
->network
);
383 r
= sd_dhcp_client_get_lease(client
, &lease
);
385 return log_link_warning_errno(link
, r
, "DHCP error: no lease: %m");
387 sd_dhcp_lease_unref(link
->dhcp_lease
);
388 link
->dhcp4_configured
= false;
389 link
->dhcp_lease
= sd_dhcp_lease_ref(lease
);
392 r
= sd_dhcp_lease_get_address(lease
, &address
);
394 return log_link_warning_errno(link
, r
, "DHCP error: no address: %m");
396 r
= sd_dhcp_lease_get_netmask(lease
, &netmask
);
398 return log_link_warning_errno(link
, r
, "DHCP error: no netmask: %m");
400 if (!link
->network
->dhcp_critical
) {
401 r
= sd_dhcp_lease_get_lifetime(link
->dhcp_lease
, &lifetime
);
403 return log_link_warning_errno(link
, r
, "DHCP error: no lifetime: %m");
406 r
= dhcp4_update_address(link
, &address
, &netmask
, lifetime
);
408 log_link_warning_errno(link
, r
, "Could not update IP address: %m");
409 link_enter_failed(link
);
416 static int dhcp_lease_acquired(sd_dhcp_client
*client
, Link
*link
) {
417 sd_dhcp_lease
*lease
;
418 struct in_addr address
;
419 struct in_addr netmask
;
420 struct in_addr gateway
;
422 uint32_t lifetime
= CACHE_INFO_INFINITY_LIFE_TIME
;
428 r
= sd_dhcp_client_get_lease(client
, &lease
);
430 return log_link_error_errno(link
, r
, "DHCP error: No lease: %m");
432 r
= sd_dhcp_lease_get_address(lease
, &address
);
434 return log_link_error_errno(link
, r
, "DHCP error: No address: %m");
436 r
= sd_dhcp_lease_get_netmask(lease
, &netmask
);
438 return log_link_error_errno(link
, r
, "DHCP error: No netmask: %m");
440 prefixlen
= in4_addr_netmask_to_prefixlen(&netmask
);
442 r
= sd_dhcp_lease_get_router(lease
, &gateway
);
443 if (r
< 0 && r
!= -ENODATA
)
444 return log_link_error_errno(link
, r
, "DHCP error: Could not get gateway: %m");
448 LOG_LINK_INTERFACE(link
),
449 LOG_LINK_MESSAGE(link
, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
450 ADDRESS_FMT_VAL(address
),
452 ADDRESS_FMT_VAL(gateway
)),
453 "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address
),
454 "PREFIXLEN=%u", prefixlen
,
455 "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(gateway
),
459 LOG_LINK_INTERFACE(link
),
460 LOG_LINK_MESSAGE(link
, "DHCPv4 address %u.%u.%u.%u/%u",
461 ADDRESS_FMT_VAL(address
),
463 "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address
),
464 "PREFIXLEN=%u", prefixlen
,
467 link
->dhcp_lease
= sd_dhcp_lease_ref(lease
);
470 if (link
->network
->dhcp_use_mtu
) {
473 r
= sd_dhcp_lease_get_mtu(lease
, &mtu
);
475 r
= link_set_mtu(link
, mtu
);
477 log_link_error_errno(link
, r
, "Failed to set MTU to %" PRIu16
": %m", mtu
);
481 if (link
->network
->dhcp_use_hostname
) {
482 const char *dhcpname
= NULL
;
483 _cleanup_free_
char *hostname
= NULL
;
485 if (link
->network
->dhcp_hostname
)
486 dhcpname
= link
->network
->dhcp_hostname
;
488 (void) sd_dhcp_lease_get_hostname(lease
, &dhcpname
);
491 r
= shorten_overlong(dhcpname
, &hostname
);
493 log_link_warning_errno(link
, r
, "Unable to shorten overlong DHCP hostname '%s', ignoring: %m", dhcpname
);
495 log_link_notice(link
, "Overlong DCHP hostname received, shortened from '%s' to '%s'", dhcpname
, hostname
);
499 r
= manager_set_hostname(link
->manager
, hostname
);
501 log_link_error_errno(link
, r
, "Failed to set transient hostname to '%s': %m", hostname
);
505 if (link
->network
->dhcp_use_timezone
) {
506 const char *tz
= NULL
;
508 (void) sd_dhcp_lease_get_timezone(link
->dhcp_lease
, &tz
);
511 r
= manager_set_timezone(link
->manager
, tz
);
513 log_link_error_errno(link
, r
, "Failed to set timezone to '%s': %m", tz
);
517 if (!link
->network
->dhcp_critical
) {
518 r
= sd_dhcp_lease_get_lifetime(link
->dhcp_lease
, &lifetime
);
520 log_link_warning_errno(link
, r
, "DHCP error: no lifetime: %m");
525 r
= dhcp4_update_address(link
, &address
, &netmask
, lifetime
);
527 log_link_warning_errno(link
, r
, "Could not update IP address: %m");
528 link_enter_failed(link
);
534 static void dhcp4_handler(sd_dhcp_client
*client
, int event
, void *userdata
) {
535 Link
*link
= userdata
;
539 assert(link
->network
);
540 assert(link
->manager
);
542 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
546 case SD_DHCP_CLIENT_EVENT_EXPIRED
:
547 case SD_DHCP_CLIENT_EVENT_STOP
:
548 case SD_DHCP_CLIENT_EVENT_IP_CHANGE
:
549 if (link
->network
->dhcp_critical
) {
550 log_link_error(link
, "DHCPv4 connection considered system critical, ignoring request to reconfigure it.");
554 if (link
->dhcp_lease
) {
555 r
= dhcp_lease_lost(link
);
557 link_enter_failed(link
);
562 if (event
== SD_DHCP_CLIENT_EVENT_IP_CHANGE
) {
563 r
= dhcp_lease_acquired(client
, link
);
565 link_enter_failed(link
);
571 case SD_DHCP_CLIENT_EVENT_RENEW
:
572 r
= dhcp_lease_renew(client
, link
);
574 link_enter_failed(link
);
578 case SD_DHCP_CLIENT_EVENT_IP_ACQUIRE
:
579 r
= dhcp_lease_acquired(client
, link
);
581 link_enter_failed(link
);
587 log_link_warning_errno(link
, event
, "DHCP error: Client failed: %m");
589 log_link_warning(link
, "DHCP unknown event: %i", event
);
596 static int dhcp4_set_hostname(Link
*link
) {
597 _cleanup_free_
char *hostname
= NULL
;
603 if (!link
->network
->dhcp_send_hostname
)
605 else if (link
->network
->dhcp_hostname
)
606 hn
= link
->network
->dhcp_hostname
;
608 r
= gethostname_strict(&hostname
);
609 if (r
< 0 && r
!= -ENXIO
) /* ENXIO: no hostname set or hostname is "localhost" */
615 return sd_dhcp_client_set_hostname(link
->dhcp_client
, hn
);
618 static bool promote_secondaries_enabled(const char *ifname
) {
619 _cleanup_free_
char *promote_secondaries_sysctl
= NULL
;
620 char *promote_secondaries_path
;
623 promote_secondaries_path
= strjoina("net/ipv4/conf/", ifname
, "/promote_secondaries");
624 r
= sysctl_read(promote_secondaries_path
, &promote_secondaries_sysctl
);
626 log_debug_errno(r
, "Cannot read sysctl %s", promote_secondaries_path
);
630 truncate_nl(promote_secondaries_sysctl
);
631 r
= parse_boolean(promote_secondaries_sysctl
);
633 log_warning_errno(r
, "Cannot parse sysctl %s with content %s as boolean", promote_secondaries_path
, promote_secondaries_sysctl
);
637 /* dhcp4_set_promote_secondaries will ensure this interface has
638 * the "promote_secondaries" option in the kernel set. If this sysctl
639 * is not set DHCP will work only as long as the IP address does not
640 * changes between leases. The kernel will remove all secondary IP
641 * addresses of an interface otherwise. The way systemd-network works
642 * is that the new IP of a lease is added as a secondary IP and when
643 * the primary one expires it relies on the kernel to promote the
644 * secondary IP. See also https://github.com/systemd/systemd/issues/7163
646 int dhcp4_set_promote_secondaries(Link
*link
) {
650 assert(link
->network
);
651 assert(link
->network
->dhcp
& ADDRESS_FAMILY_IPV4
);
653 /* check if the kernel has promote_secondaries enabled for our
654 * interface. If it is not globally enabled or enabled for the
655 * specific interface we must either enable it.
657 if (!(promote_secondaries_enabled("all") || promote_secondaries_enabled(link
->ifname
))) {
658 char *promote_secondaries_path
= NULL
;
660 log_link_debug(link
, "promote_secondaries is unset, setting it");
661 promote_secondaries_path
= strjoina("net/ipv4/conf/", link
->ifname
, "/promote_secondaries");
662 r
= sysctl_write(promote_secondaries_path
, "1");
664 log_link_warning_errno(link
, r
, "cannot set sysctl %s to 1", promote_secondaries_path
);
671 int dhcp4_configure(Link
*link
) {
675 assert(link
->network
);
676 assert(link
->network
->dhcp
& ADDRESS_FAMILY_IPV4
);
678 if (!link
->dhcp_client
) {
679 r
= sd_dhcp_client_new(&link
->dhcp_client
, link
->network
->dhcp_anonymize
);
684 r
= sd_dhcp_client_attach_event(link
->dhcp_client
, NULL
, 0);
688 r
= sd_dhcp_client_set_mac(link
->dhcp_client
,
689 (const uint8_t *) &link
->mac
,
690 sizeof (link
->mac
), ARPHRD_ETHER
);
694 r
= sd_dhcp_client_set_ifindex(link
->dhcp_client
, link
->ifindex
);
698 r
= sd_dhcp_client_set_callback(link
->dhcp_client
, dhcp4_handler
, link
);
702 r
= sd_dhcp_client_set_request_broadcast(link
->dhcp_client
,
703 link
->network
->dhcp_broadcast
);
708 r
= sd_dhcp_client_set_mtu(link
->dhcp_client
, link
->mtu
);
713 if (link
->network
->dhcp_use_mtu
) {
714 r
= sd_dhcp_client_set_request_option(link
->dhcp_client
,
715 SD_DHCP_OPTION_INTERFACE_MTU
);
720 /* NOTE: even if this variable is called "use", it also "sends" PRL
721 * options, maybe there should be a different configuration variable
722 * to send or not route options?. */
723 /* NOTE: when using Anonymize=yes, routes PRL options are sent
724 * by default, so they don't need to be added here. */
725 if (link
->network
->dhcp_use_routes
&& !link
->network
->dhcp_anonymize
) {
726 r
= sd_dhcp_client_set_request_option(link
->dhcp_client
,
727 SD_DHCP_OPTION_STATIC_ROUTE
);
730 r
= sd_dhcp_client_set_request_option(link
->dhcp_client
,
731 SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE
);
736 if (link
->network
->dhcp_use_ntp
) {
737 r
= sd_dhcp_client_set_request_option(link
->dhcp_client
, SD_DHCP_OPTION_NTP_SERVER
);
742 if (link
->network
->dhcp_use_timezone
) {
743 r
= sd_dhcp_client_set_request_option(link
->dhcp_client
, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE
);
748 r
= dhcp4_set_hostname(link
);
752 if (link
->network
->dhcp_vendor_class_identifier
) {
753 r
= sd_dhcp_client_set_vendor_class_identifier(link
->dhcp_client
,
754 link
->network
->dhcp_vendor_class_identifier
);
759 if (link
->network
->dhcp_client_port
) {
760 r
= sd_dhcp_client_set_client_port(link
->dhcp_client
, link
->network
->dhcp_client_port
);
765 switch (link
->network
->dhcp_client_identifier
) {
766 case DHCP_CLIENT_ID_DUID
: {
767 /* If configured, apply user specified DUID and/or IAID */
768 const DUID
*duid
= link_duid(link
);
770 r
= sd_dhcp_client_set_iaid_duid(link
->dhcp_client
,
773 duid
->raw_data_len
> 0 ? duid
->raw_data
: NULL
,
779 case DHCP_CLIENT_ID_DUID_ONLY
: {
780 /* If configured, apply user specified DUID */
781 const DUID
*duid
= link_duid(link
);
783 r
= sd_dhcp_client_set_duid(link
->dhcp_client
,
785 duid
->raw_data_len
> 0 ? duid
->raw_data
: NULL
,
791 case DHCP_CLIENT_ID_MAC
:
792 r
= sd_dhcp_client_set_client_id(link
->dhcp_client
,
794 (const uint8_t *) &link
->mac
,
800 assert_not_reached("Unknown client identifier type.");