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