]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp4.c
networkd: fix commit 5f04a209eae0a2a04eb60fd7235b3fcdf3cc81ba (#7432)
[thirdparty/systemd.git] / src / network / networkd-dhcp4.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2013-2014 Tom Gundersen <teg@jklm.no>
6
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.
11
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.
16
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/>.
19 ***/
20
21 #include <netinet/ether.h>
22 #include <linux/if.h>
23
24 #include "alloc-util.h"
25 #include "dhcp-lease-internal.h"
26 #include "hostname-util.h"
27 #include "netdev/vrf.h"
28 #include "network-internal.h"
29 #include "networkd-link.h"
30 #include "networkd-manager.h"
31 #include "networkd-network.h"
32
33 static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
34 void *userdata) {
35 _cleanup_link_unref_ Link *link = userdata;
36 int r;
37
38 assert(link);
39 assert(link->dhcp4_messages > 0);
40
41 link->dhcp4_messages--;
42
43 r = sd_netlink_message_get_errno(m);
44 if (r < 0 && r != -EEXIST) {
45 log_link_error_errno(link, r, "Could not set DHCPv4 route: %m");
46 link_enter_failed(link);
47 }
48
49 if (link->dhcp4_messages == 0) {
50 link->dhcp4_configured = true;
51 link_check_ready(link);
52 }
53
54 return 1;
55 }
56
57 static int route_scope_from_address(const Route *route, const struct in_addr *self_addr) {
58 assert(route);
59 assert(self_addr);
60
61 if (in_addr_is_localhost(AF_INET, &route->dst) ||
62 (self_addr->s_addr && route->dst.in.s_addr == self_addr->s_addr))
63 return RT_SCOPE_HOST;
64 else if (in4_addr_is_null(&route->gw.in))
65 return RT_SCOPE_LINK;
66 else
67 return RT_SCOPE_UNIVERSE;
68 }
69
70 static int link_set_dhcp_routes(Link *link) {
71 struct in_addr gateway, address;
72 _cleanup_free_ sd_dhcp_route **static_routes = NULL;
73 int r, n, i;
74 uint32_t table;
75
76 assert(link);
77
78 if (!link->dhcp_lease) /* link went down while we configured the IP addresses? */
79 return 0;
80
81 if (!link->network) /* link went down while we configured the IP addresses? */
82 return 0;
83
84 if (!link->network->dhcp_use_routes)
85 return 0;
86
87 /* When the interface is part of an VRF use the VRFs routing table, unless
88 * there is a another table specified. */
89 table = link->network->dhcp_route_table;
90 if (!link->network->dhcp_route_table_set && link->network->vrf != NULL)
91 table = VRF(link->network->vrf)->table;
92
93 r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
94 if (r < 0)
95 return log_link_warning_errno(link, r, "DHCP error: could not get address: %m");
96
97 n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
98 if (n < 0)
99 log_link_debug_errno(link, n, "DHCP error: could not get routes: %m");
100
101 for (i = 0; i < n; i++) {
102 _cleanup_route_free_ Route *route = NULL;
103
104 r = route_new(&route);
105 if (r < 0)
106 return log_link_error_errno(link, r, "Could not allocate route: %m");
107
108 route->family = AF_INET;
109 route->protocol = RTPROT_DHCP;
110 assert_se(sd_dhcp_route_get_gateway(static_routes[i], &route->gw.in) >= 0);
111 assert_se(sd_dhcp_route_get_destination(static_routes[i], &route->dst.in) >= 0);
112 assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0);
113 route->priority = link->network->dhcp_route_metric;
114 route->table = table;
115 route->scope = route_scope_from_address(route, &address);
116
117 r = route_configure(route, link, dhcp4_route_handler);
118 if (r < 0)
119 return log_link_warning_errno(link, r, "Could not set host route: %m");
120
121 link->dhcp4_messages++;
122 }
123
124 r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
125 if (r == -ENODATA)
126 log_link_info_errno(link, r, "DHCP: No routes received from DHCP server: %m");
127 else if (r < 0)
128 log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m");
129
130 /* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and
131 a Router option, the DHCP client MUST ignore the Router option. */
132 if (r >= 0 && link->dhcp4_messages <= 0) {
133 _cleanup_route_free_ Route *route = NULL;
134 _cleanup_route_free_ Route *route_gw = NULL;
135
136 r = route_new(&route);
137 if (r < 0)
138 return log_link_error_errno(link, r, "Could not allocate route: %m");
139
140 route->protocol = RTPROT_DHCP;
141
142 r = route_new(&route_gw);
143 if (r < 0)
144 return log_link_error_errno(link, r, "Could not allocate route: %m");
145
146 /* The dhcp netmask may mask out the gateway. Add an explicit
147 * route for the gw host so that we can route no matter the
148 * netmask or existing kernel route tables. */
149 route_gw->family = AF_INET;
150 route_gw->dst.in = gateway;
151 route_gw->dst_prefixlen = 32;
152 route_gw->prefsrc.in = address;
153 route_gw->scope = RT_SCOPE_LINK;
154 route_gw->protocol = RTPROT_DHCP;
155 route_gw->priority = link->network->dhcp_route_metric;
156 route_gw->table = table;
157
158 r = route_configure(route_gw, link, dhcp4_route_handler);
159 if (r < 0)
160 return log_link_warning_errno(link, r, "Could not set host route: %m");
161
162 link->dhcp4_messages++;
163
164 route->family = AF_INET;
165 route->gw.in = gateway;
166 route->prefsrc.in = address;
167 route->priority = link->network->dhcp_route_metric;
168 route->table = table;
169
170 r = route_configure(route, link, dhcp4_route_handler);
171 if (r < 0) {
172 log_link_warning_errno(link, r, "Could not set routes: %m");
173 link_enter_failed(link);
174 return r;
175 }
176
177 link->dhcp4_messages++;
178 }
179
180 return 0;
181 }
182
183 static int dhcp_lease_lost(Link *link) {
184 _cleanup_address_free_ Address *address = NULL;
185 struct in_addr addr;
186 struct in_addr netmask;
187 struct in_addr gateway;
188 unsigned prefixlen = 0;
189 int r;
190
191 assert(link);
192 assert(link->dhcp_lease);
193
194 log_link_warning(link, "DHCP lease lost");
195
196 if (link->network->dhcp_use_routes) {
197 _cleanup_free_ sd_dhcp_route **routes = NULL;
198 int n, i;
199
200 n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes);
201 if (n >= 0) {
202 for (i = 0; i < n; i++) {
203 _cleanup_route_free_ Route *route = NULL;
204
205 r = route_new(&route);
206 if (r >= 0) {
207 route->family = AF_INET;
208 assert_se(sd_dhcp_route_get_gateway(routes[i], &route->gw.in) >= 0);
209 assert_se(sd_dhcp_route_get_destination(routes[i], &route->dst.in) >= 0);
210 assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen) >= 0);
211
212 route_remove(route, link,
213 link_route_remove_handler);
214 }
215 }
216 }
217 }
218
219 r = address_new(&address);
220 if (r >= 0) {
221 r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
222 if (r >= 0) {
223 _cleanup_route_free_ Route *route_gw = NULL;
224 _cleanup_route_free_ Route *route = NULL;
225
226 r = route_new(&route_gw);
227 if (r >= 0) {
228 route_gw->family = AF_INET;
229 route_gw->dst.in = gateway;
230 route_gw->dst_prefixlen = 32;
231 route_gw->scope = RT_SCOPE_LINK;
232
233 route_remove(route_gw, link,
234 link_route_remove_handler);
235 }
236
237 r = route_new(&route);
238 if (r >= 0) {
239 route->family = AF_INET;
240 route->gw.in = gateway;
241
242 route_remove(route, link,
243 link_route_remove_handler);
244 }
245 }
246
247 r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr);
248 if (r >= 0) {
249 r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
250 if (r >= 0)
251 prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
252
253 address->family = AF_INET;
254 address->in_addr.in = addr;
255 address->prefixlen = prefixlen;
256
257 address_remove(address, link, link_address_remove_handler);
258 }
259 }
260
261 if (link->network->dhcp_use_mtu) {
262 uint16_t mtu;
263
264 r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu);
265 if (r >= 0 && link->original_mtu != mtu) {
266 r = link_set_mtu(link, link->original_mtu);
267 if (r < 0) {
268 log_link_warning(link,
269 "DHCP error: could not reset MTU");
270 link_enter_failed(link);
271 return r;
272 }
273 }
274 }
275
276 if (link->network->dhcp_use_hostname) {
277 const char *hostname = NULL;
278
279 if (link->network->dhcp_hostname)
280 hostname = link->network->dhcp_hostname;
281 else
282 (void) sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
283
284 if (hostname) {
285 /* If a hostname was set due to the lease, then unset it now. */
286 r = manager_set_hostname(link->manager, NULL);
287 if (r < 0)
288 log_link_warning_errno(link, r, "Failed to reset transient hostname: %m");
289 }
290 }
291
292 link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
293 link_dirty(link);
294 link->dhcp4_configured = false;
295
296 return 0;
297 }
298
299 static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
300 void *userdata) {
301 _cleanup_link_unref_ Link *link = userdata;
302 int r;
303
304 assert(link);
305
306 r = sd_netlink_message_get_errno(m);
307 if (r < 0 && r != -EEXIST) {
308 log_link_error_errno(link, r, "Could not set DHCPv4 address: %m");
309 link_enter_failed(link);
310 } else if (r >= 0)
311 manager_rtnl_process_address(rtnl, m, link->manager);
312
313 link_set_dhcp_routes(link);
314
315 return 1;
316 }
317
318 static int dhcp4_update_address(Link *link,
319 struct in_addr *address,
320 struct in_addr *netmask,
321 uint32_t lifetime) {
322 _cleanup_address_free_ Address *addr = NULL;
323 unsigned prefixlen;
324 int r;
325
326 assert(address);
327 assert(netmask);
328 assert(lifetime);
329
330 prefixlen = in4_addr_netmask_to_prefixlen(netmask);
331
332 r = address_new(&addr);
333 if (r < 0)
334 return r;
335
336 addr->family = AF_INET;
337 addr->in_addr.in.s_addr = address->s_addr;
338 addr->cinfo.ifa_prefered = lifetime;
339 addr->cinfo.ifa_valid = lifetime;
340 addr->prefixlen = prefixlen;
341 addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
342
343 /* allow reusing an existing address and simply update its lifetime
344 * in case it already exists */
345 r = address_configure(addr, link, dhcp4_address_handler, true);
346 if (r < 0)
347 return r;
348
349 return 0;
350 }
351
352 static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
353 sd_dhcp_lease *lease;
354 struct in_addr address;
355 struct in_addr netmask;
356 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
357 int r;
358
359 assert(link);
360 assert(client);
361 assert(link->network);
362
363 r = sd_dhcp_client_get_lease(client, &lease);
364 if (r < 0)
365 return log_link_warning_errno(link, r, "DHCP error: no lease: %m");
366
367 sd_dhcp_lease_unref(link->dhcp_lease);
368 link->dhcp4_configured = false;
369 link->dhcp_lease = sd_dhcp_lease_ref(lease);
370 link_dirty(link);
371
372 r = sd_dhcp_lease_get_address(lease, &address);
373 if (r < 0)
374 return log_link_warning_errno(link, r, "DHCP error: no address: %m");
375
376 r = sd_dhcp_lease_get_netmask(lease, &netmask);
377 if (r < 0)
378 return log_link_warning_errno(link, r, "DHCP error: no netmask: %m");
379
380 if (!link->network->dhcp_critical) {
381 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
382 if (r < 0)
383 return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
384 }
385
386 r = dhcp4_update_address(link, &address, &netmask, lifetime);
387 if (r < 0) {
388 log_link_warning_errno(link, r, "Could not update IP address: %m");
389 link_enter_failed(link);
390 return r;
391 }
392
393 return 0;
394 }
395
396 static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
397 sd_dhcp_lease *lease;
398 struct in_addr address;
399 struct in_addr netmask;
400 struct in_addr gateway;
401 unsigned prefixlen;
402 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
403 int r;
404
405 assert(client);
406 assert(link);
407
408 r = sd_dhcp_client_get_lease(client, &lease);
409 if (r < 0)
410 return log_link_error_errno(link, r, "DHCP error: No lease: %m");
411
412 r = sd_dhcp_lease_get_address(lease, &address);
413 if (r < 0)
414 return log_link_error_errno(link, r, "DHCP error: No address: %m");
415
416 r = sd_dhcp_lease_get_netmask(lease, &netmask);
417 if (r < 0)
418 return log_link_error_errno(link, r, "DHCP error: No netmask: %m");
419
420 prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
421
422 r = sd_dhcp_lease_get_router(lease, &gateway);
423 if (r < 0 && r != -ENODATA)
424 return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m");
425
426 if (r >= 0)
427 log_struct(LOG_INFO,
428 LOG_LINK_INTERFACE(link),
429 LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
430 ADDRESS_FMT_VAL(address),
431 prefixlen,
432 ADDRESS_FMT_VAL(gateway)),
433 "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
434 "PREFIXLEN=%u", prefixlen,
435 "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(gateway),
436 NULL);
437 else
438 log_struct(LOG_INFO,
439 LOG_LINK_INTERFACE(link),
440 LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u",
441 ADDRESS_FMT_VAL(address),
442 prefixlen),
443 "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
444 "PREFIXLEN=%u", prefixlen,
445 NULL);
446
447 link->dhcp_lease = sd_dhcp_lease_ref(lease);
448 link_dirty(link);
449
450 if (link->network->dhcp_use_mtu) {
451 uint16_t mtu;
452
453 r = sd_dhcp_lease_get_mtu(lease, &mtu);
454 if (r >= 0) {
455 r = link_set_mtu(link, mtu);
456 if (r < 0)
457 log_link_error_errno(link, r, "Failed to set MTU to %" PRIu16 ": %m", mtu);
458 }
459 }
460
461 if (link->network->dhcp_use_hostname) {
462 const char *hostname = NULL;
463
464 if (link->network->dhcp_hostname)
465 hostname = link->network->dhcp_hostname;
466 else
467 (void) sd_dhcp_lease_get_hostname(lease, &hostname);
468
469 if (hostname) {
470 r = manager_set_hostname(link->manager, hostname);
471 if (r < 0)
472 log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname);
473 }
474 }
475
476 if (link->network->dhcp_use_timezone) {
477 const char *tz = NULL;
478
479 (void) sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz);
480
481 if (tz) {
482 r = manager_set_timezone(link->manager, tz);
483 if (r < 0)
484 log_link_error_errno(link, r, "Failed to set timezone to '%s': %m", tz);
485 }
486 }
487
488 if (!link->network->dhcp_critical) {
489 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
490 if (r < 0) {
491 log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
492 return r;
493 }
494 }
495
496 r = dhcp4_update_address(link, &address, &netmask, lifetime);
497 if (r < 0) {
498 log_link_warning_errno(link, r, "Could not update IP address: %m");
499 link_enter_failed(link);
500 return r;
501 }
502
503 return 0;
504 }
505 static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
506 Link *link = userdata;
507 int r = 0;
508
509 assert(link);
510 assert(link->network);
511 assert(link->manager);
512
513 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
514 return;
515
516 switch (event) {
517 case SD_DHCP_CLIENT_EVENT_EXPIRED:
518 case SD_DHCP_CLIENT_EVENT_STOP:
519 case SD_DHCP_CLIENT_EVENT_IP_CHANGE:
520 if (link->network->dhcp_critical) {
521 log_link_error(link, "DHCPv4 connection considered system critical, ignoring request to reconfigure it.");
522 return;
523 }
524
525 if (link->dhcp_lease) {
526 r = dhcp_lease_lost(link);
527 if (r < 0) {
528 link_enter_failed(link);
529 return;
530 }
531 }
532
533 if (event == SD_DHCP_CLIENT_EVENT_IP_CHANGE) {
534 r = dhcp_lease_acquired(client, link);
535 if (r < 0) {
536 link_enter_failed(link);
537 return;
538 }
539 }
540
541 break;
542 case SD_DHCP_CLIENT_EVENT_RENEW:
543 r = dhcp_lease_renew(client, link);
544 if (r < 0) {
545 link_enter_failed(link);
546 return;
547 }
548 break;
549 case SD_DHCP_CLIENT_EVENT_IP_ACQUIRE:
550 r = dhcp_lease_acquired(client, link);
551 if (r < 0) {
552 link_enter_failed(link);
553 return;
554 }
555 break;
556 default:
557 if (event < 0)
558 log_link_warning_errno(link, event, "DHCP error: Client failed: %m");
559 else
560 log_link_warning(link, "DHCP unknown event: %i", event);
561 break;
562 }
563
564 return;
565 }
566
567 static int dhcp4_set_hostname(Link *link) {
568 _cleanup_free_ char *hostname = NULL;
569 const char *hn;
570 int r;
571
572 assert(link);
573
574 if (!link->network->dhcp_send_hostname)
575 hn = NULL;
576 else if (link->network->dhcp_hostname)
577 hn = link->network->dhcp_hostname;
578 else {
579 r = gethostname_strict(&hostname);
580 if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
581 return r;
582
583 hn = hostname;
584 }
585
586 return sd_dhcp_client_set_hostname(link->dhcp_client, hn);
587 }
588
589 int dhcp4_configure(Link *link) {
590 int r;
591
592 assert(link);
593 assert(link->network);
594 assert(link->network->dhcp & ADDRESS_FAMILY_IPV4);
595
596 if (!link->dhcp_client) {
597 r = sd_dhcp_client_new(&link->dhcp_client, link->network->dhcp_anonymize);
598 if (r < 0)
599 return r;
600 }
601
602 r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
603 if (r < 0)
604 return r;
605
606 r = sd_dhcp_client_set_mac(link->dhcp_client,
607 (const uint8_t *) &link->mac,
608 sizeof (link->mac), ARPHRD_ETHER);
609 if (r < 0)
610 return r;
611
612 r = sd_dhcp_client_set_ifindex(link->dhcp_client, link->ifindex);
613 if (r < 0)
614 return r;
615
616 r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
617 if (r < 0)
618 return r;
619
620 r = sd_dhcp_client_set_request_broadcast(link->dhcp_client,
621 link->network->dhcp_broadcast);
622 if (r < 0)
623 return r;
624
625 if (link->mtu) {
626 r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
627 if (r < 0)
628 return r;
629 }
630
631 if (link->network->dhcp_use_mtu) {
632 r = sd_dhcp_client_set_request_option(link->dhcp_client,
633 SD_DHCP_OPTION_INTERFACE_MTU);
634 if (r < 0)
635 return r;
636 }
637
638 /* NOTE: even if this variable is called "use", it also "sends" PRL
639 * options, maybe there should be a different configuration variable
640 * to send or not route options?. */
641 /* NOTE: when using Anonymize=yes, routes PRL options are sent
642 * by default, so they don't need to be added here. */
643 if (link->network->dhcp_use_routes && !link->network->dhcp_anonymize) {
644 r = sd_dhcp_client_set_request_option(link->dhcp_client,
645 SD_DHCP_OPTION_STATIC_ROUTE);
646 if (r < 0)
647 return r;
648 r = sd_dhcp_client_set_request_option(link->dhcp_client,
649 SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
650 if (r < 0)
651 return r;
652 }
653
654 if (link->network->dhcp_use_ntp) {
655 r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NTP_SERVER);
656 if (r < 0)
657 return r;
658 }
659
660 if (link->network->dhcp_use_timezone) {
661 r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE);
662 if (r < 0)
663 return r;
664 }
665
666 r = dhcp4_set_hostname(link);
667 if (r < 0)
668 return r;
669
670 if (link->network->dhcp_vendor_class_identifier) {
671 r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
672 link->network->dhcp_vendor_class_identifier);
673 if (r < 0)
674 return r;
675 }
676
677 if (link->network->dhcp_client_port) {
678 r = sd_dhcp_client_set_client_port(link->dhcp_client, link->network->dhcp_client_port);
679 if (r < 0)
680 return r;
681 }
682
683 switch (link->network->dhcp_client_identifier) {
684 case DHCP_CLIENT_ID_DUID: {
685 /* If configured, apply user specified DUID and/or IAID */
686 const DUID *duid = link_duid(link);
687
688 r = sd_dhcp_client_set_iaid_duid(link->dhcp_client,
689 link->network->iaid,
690 duid->type,
691 duid->raw_data_len > 0 ? duid->raw_data : NULL,
692 duid->raw_data_len);
693 if (r < 0)
694 return r;
695 break;
696 }
697 case DHCP_CLIENT_ID_MAC:
698 r = sd_dhcp_client_set_client_id(link->dhcp_client,
699 ARPHRD_ETHER,
700 (const uint8_t *) &link->mac,
701 sizeof(link->mac));
702 if (r < 0)
703 return r;
704 break;
705 default:
706 assert_not_reached("Unknown client identifier type.");
707 }
708
709 return 0;
710 }