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