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