]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp4.c
networkd: manager - enumerate addresses globally, rather than per-link
[thirdparty/systemd.git] / src / network / networkd-dhcp4.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013-2014 Tom Gundersen <teg@jklm.no>
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <netinet/ether.h>
23 #include <linux/if.h>
24
25 #include "networkd-link.h"
26 #include "network-internal.h"
27 #include "dhcp-lease-internal.h"
28
29 static int dhcp4_route_handler(sd_rtnl *rtnl, sd_rtnl_message *m,
30 void *userdata) {
31 _cleanup_link_unref_ Link *link = userdata;
32 int r;
33
34 assert(link);
35 assert(link->dhcp4_messages);
36
37 link->dhcp4_messages --;
38
39 r = sd_rtnl_message_get_errno(m);
40 if (r < 0 && r != -EEXIST) {
41 log_link_error(link, "could not set DHCPv4 route: %s",
42 strerror(-r));
43 link_enter_failed(link);
44 }
45
46 if (!link->dhcp4_messages) {
47 link->dhcp4_configured = true;
48 link_client_handler(link);
49 }
50
51 return 1;
52 }
53
54 static int link_set_dhcp_routes(Link *link) {
55 struct in_addr gateway;
56 struct sd_dhcp_route *static_routes;
57 int r, n, i;
58
59 assert(link);
60 assert(link->dhcp_lease);
61
62 r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
63 if (r < 0 && r != -ENOENT) {
64 log_link_warning(link,
65 "DHCP error: could not get gateway: %s",
66 strerror(-r));
67 return r;
68 }
69 if (r >= 0) {
70 struct in_addr address;
71 _cleanup_route_free_ Route *route = NULL;
72 _cleanup_route_free_ Route *route_gw = NULL;
73
74 r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
75 if (r < 0) {
76 log_link_warning(link,
77 "DHCP error: could not get address: %s",
78 strerror(-r));
79 return r;
80 }
81
82 r = route_new_dynamic(&route, RTPROT_DHCP);
83 if (r < 0) {
84 log_link_error(link,
85 "Could not allocate route: %s",
86 strerror(-r));
87 return r;
88 }
89
90 r = route_new_dynamic(&route_gw, RTPROT_DHCP);
91 if (r < 0) {
92 log_link_error(link,
93 "Could not allocate route: %s",
94 strerror(-r));
95 return r;
96 }
97
98 /* The dhcp netmask may mask out the gateway. Add an explicit
99 * route for the gw host so that we can route no matter the
100 * netmask or existing kernel route tables. */
101 route_gw->family = AF_INET;
102 route_gw->dst_addr.in = gateway;
103 route_gw->dst_prefixlen = 32;
104 route_gw->prefsrc_addr.in = address;
105 route_gw->scope = RT_SCOPE_LINK;
106 route_gw->metrics = link->network->dhcp_route_metric;
107
108 r = route_configure(route_gw, link, &dhcp4_route_handler);
109 if (r < 0) {
110 log_link_warning(link,
111 "could not set host route: %s",
112 strerror(-r));
113 return r;
114 }
115
116 link->dhcp4_messages ++;
117
118 route->family = AF_INET;
119 route->in_addr.in = gateway;
120 route->prefsrc_addr.in = address;
121 route->metrics = link->network->dhcp_route_metric;
122
123 r = route_configure(route, link, &dhcp4_route_handler);
124 if (r < 0) {
125 log_link_warning(link,
126 "could not set routes: %s",
127 strerror(-r));
128 link_enter_failed(link);
129 return r;
130 }
131
132 link->dhcp4_messages ++;
133 }
134
135 n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
136 if (n == -ENOENT)
137 return 0;
138 if (n < 0) {
139 log_link_warning(link,
140 "DHCP error: could not get routes: %s",
141 strerror(-n));
142
143 return n;
144 }
145
146 for (i = 0; i < n; i++) {
147 _cleanup_route_free_ Route *route = NULL;
148
149 r = route_new_dynamic(&route, RTPROT_DHCP);
150 if (r < 0) {
151 log_link_error(link, "Could not allocate route: %s",
152 strerror(-r));
153 return r;
154 }
155
156 route->family = AF_INET;
157 route->in_addr.in = static_routes[i].gw_addr;
158 route->dst_addr.in = static_routes[i].dst_addr;
159 route->dst_prefixlen = static_routes[i].dst_prefixlen;
160 route->metrics = link->network->dhcp_route_metric;
161
162 r = route_configure(route, link, &dhcp4_route_handler);
163 if (r < 0) {
164 log_link_warning(link,
165 "could not set host route: %s",
166 strerror(-r));
167 return r;
168 }
169
170 link->dhcp4_messages ++;
171 }
172
173 return 0;
174 }
175
176 static int dhcp_lease_lost(Link *link) {
177 _cleanup_address_free_ Address *address = NULL;
178 struct in_addr addr;
179 struct in_addr netmask;
180 struct in_addr gateway;
181 unsigned prefixlen = 0;
182 int r;
183
184 assert(link);
185 assert(link->dhcp_lease);
186
187 log_link_warning(link, "DHCP lease lost");
188
189 if (link->network->dhcp_routes) {
190 struct sd_dhcp_route *routes;
191 int n, i;
192
193 n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes);
194 if (n >= 0) {
195 for (i = 0; i < n; i++) {
196 _cleanup_route_free_ Route *route = NULL;
197
198 r = route_new_dynamic(&route, RTPROT_UNSPEC);
199 if (r >= 0) {
200 route->family = AF_INET;
201 route->in_addr.in = routes[i].gw_addr;
202 route->dst_addr.in = routes[i].dst_addr;
203 route->dst_prefixlen = routes[i].dst_prefixlen;
204
205 route_drop(route, link,
206 &link_route_drop_handler);
207 }
208 }
209 }
210 }
211
212 r = address_new_dynamic(&address);
213 if (r >= 0) {
214 r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
215 if (r >= 0) {
216 _cleanup_route_free_ Route *route_gw = NULL;
217 _cleanup_route_free_ Route *route = NULL;
218
219 r = route_new_dynamic(&route_gw, RTPROT_UNSPEC);
220 if (r >= 0) {
221 route_gw->family = AF_INET;
222 route_gw->dst_addr.in = gateway;
223 route_gw->dst_prefixlen = 32;
224 route_gw->scope = RT_SCOPE_LINK;
225
226 route_drop(route_gw, link,
227 &link_route_drop_handler);
228 }
229
230 r = route_new_dynamic(&route, RTPROT_UNSPEC);
231 if (r >= 0) {
232 route->family = AF_INET;
233 route->in_addr.in = gateway;
234
235 route_drop(route, link,
236 &link_route_drop_handler);
237 }
238 }
239
240 r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr);
241 if (r >= 0) {
242 r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
243 if (r >= 0)
244 prefixlen = in_addr_netmask_to_prefixlen(&netmask);
245
246 address->family = AF_INET;
247 address->in_addr.in = addr;
248 address->prefixlen = prefixlen;
249
250 address_drop(address, link, &link_address_drop_handler);
251 }
252 }
253
254 if (link->network->dhcp_mtu) {
255 uint16_t mtu;
256
257 r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu);
258 if (r >= 0 && link->original_mtu != mtu) {
259 r = link_set_mtu(link, link->original_mtu);
260 if (r < 0) {
261 log_link_warning(link,
262 "DHCP error: could not reset MTU");
263 link_enter_failed(link);
264 return r;
265 }
266 }
267 }
268
269 if (link->network->dhcp_hostname) {
270 const char *hostname = NULL;
271
272 r = sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
273 if (r >= 0 && hostname) {
274 r = link_set_hostname(link, "");
275 if (r < 0)
276 log_link_error(link,
277 "Failed to reset transient hostname");
278 }
279 }
280
281 link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
282 link->dhcp4_configured = false;
283
284 return 0;
285 }
286
287 static int dhcp4_address_handler(sd_rtnl *rtnl, sd_rtnl_message *m,
288 void *userdata) {
289 _cleanup_link_unref_ Link *link = userdata;
290 int r;
291
292 assert(link);
293
294 r = sd_rtnl_message_get_errno(m);
295 if (r < 0 && r != -EEXIST) {
296 log_link_error(link, "could not set DHCPv4 address: %s",
297 strerror(-r));
298 link_enter_failed(link);
299 } else if (r >= 0)
300 link_rtnl_process_address(rtnl, m, link->manager);
301
302 link_set_dhcp_routes(link);
303
304 return 1;
305 }
306
307 static int dhcp4_update_address(Link *link,
308 struct in_addr *address,
309 struct in_addr *netmask,
310 uint32_t lifetime) {
311 _cleanup_address_free_ Address *addr = NULL;
312 unsigned prefixlen;
313 int r;
314
315 assert(address);
316 assert(netmask);
317 assert(lifetime);
318
319 prefixlen = in_addr_netmask_to_prefixlen(netmask);
320
321 r = address_new_dynamic(&addr);
322 if (r < 0)
323 return r;
324
325 addr->family = AF_INET;
326 addr->in_addr.in.s_addr = address->s_addr;
327 addr->cinfo.ifa_prefered = lifetime;
328 addr->cinfo.ifa_valid = lifetime;
329 addr->prefixlen = prefixlen;
330 addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
331
332 /* use update rather than configure so that we will update the
333 * lifetime of an existing address if it has already been configured */
334 r = address_update(addr, link, &dhcp4_address_handler);
335 if (r < 0)
336 return r;
337
338 return 0;
339 }
340
341 static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
342 sd_dhcp_lease *lease;
343 struct in_addr address;
344 struct in_addr netmask;
345 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
346 int r;
347
348 assert(link);
349 assert(client);
350 assert(link->network);
351
352 r = sd_dhcp_client_get_lease(client, &lease);
353 if (r < 0) {
354 log_link_warning(link, "DHCP error: no lease %s",
355 strerror(-r));
356 return r;
357 }
358
359 sd_dhcp_lease_unref(link->dhcp_lease);
360 link->dhcp4_configured = false;
361 link->dhcp_lease = lease;
362
363 r = sd_dhcp_lease_get_address(lease, &address);
364 if (r < 0) {
365 log_link_warning(link, "DHCP error: no address: %s",
366 strerror(-r));
367 return r;
368 }
369
370 r = sd_dhcp_lease_get_netmask(lease, &netmask);
371 if (r < 0) {
372 log_link_warning(link, "DHCP error: no netmask: %s",
373 strerror(-r));
374 return r;
375 }
376
377 if (!link->network->dhcp_critical) {
378 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease,
379 &lifetime);
380 if (r < 0) {
381 log_link_warning(link,
382 "DHCP error: no lifetime: %s",
383 strerror(-r));
384 return r;
385 }
386 }
387
388 r = dhcp4_update_address(link, &address, &netmask, lifetime);
389 if (r < 0) {
390 log_link_warning(link, "could not update IP address: %s",
391 strerror(-r));
392 link_enter_failed(link);
393 return r;
394 }
395
396 return 0;
397 }
398
399 static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
400 sd_dhcp_lease *lease;
401 struct in_addr address;
402 struct in_addr netmask;
403 struct in_addr gateway;
404 unsigned prefixlen;
405 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
406 int r;
407
408 assert(client);
409 assert(link);
410
411 r = sd_dhcp_client_get_lease(client, &lease);
412 if (r < 0) {
413 log_link_warning(link, "DHCP error: no lease: %s",
414 strerror(-r));
415 return r;
416 }
417
418 r = sd_dhcp_lease_get_address(lease, &address);
419 if (r < 0) {
420 log_link_warning(link, "DHCP error: no address: %s",
421 strerror(-r));
422 return r;
423 }
424
425 r = sd_dhcp_lease_get_netmask(lease, &netmask);
426 if (r < 0) {
427 log_link_warning(link, "DHCP error: no netmask: %s",
428 strerror(-r));
429 return r;
430 }
431
432 prefixlen = in_addr_netmask_to_prefixlen(&netmask);
433
434 r = sd_dhcp_lease_get_router(lease, &gateway);
435 if (r < 0 && r != -ENOENT) {
436 log_link_warning(link, "DHCP error: could not get gateway: %s",
437 strerror(-r));
438 return r;
439 }
440
441 if (r >= 0)
442 log_link_struct(link, LOG_INFO,
443 "MESSAGE=%-*s: DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
444 IFNAMSIZ,
445 link->ifname,
446 ADDRESS_FMT_VAL(address),
447 prefixlen,
448 ADDRESS_FMT_VAL(gateway),
449 "ADDRESS=%u.%u.%u.%u",
450 ADDRESS_FMT_VAL(address),
451 "PREFIXLEN=%u",
452 prefixlen,
453 "GATEWAY=%u.%u.%u.%u",
454 ADDRESS_FMT_VAL(gateway),
455 NULL);
456 else
457 log_link_struct(link, LOG_INFO,
458 "MESSAGE=%-*s: DHCPv4 address %u.%u.%u.%u/%u",
459 IFNAMSIZ,
460 link->ifname,
461 ADDRESS_FMT_VAL(address),
462 prefixlen,
463 "ADDRESS=%u.%u.%u.%u",
464 ADDRESS_FMT_VAL(address),
465 "PREFIXLEN=%u",
466 prefixlen,
467 NULL);
468
469 link->dhcp_lease = lease;
470
471 if (link->network->dhcp_mtu) {
472 uint16_t mtu;
473
474 r = sd_dhcp_lease_get_mtu(lease, &mtu);
475 if (r >= 0) {
476 r = link_set_mtu(link, mtu);
477 if (r < 0)
478 log_link_error(link, "Failed to set MTU "
479 "to %" PRIu16, mtu);
480 }
481 }
482
483 if (link->network->dhcp_hostname) {
484 const char *hostname;
485
486 r = sd_dhcp_lease_get_hostname(lease, &hostname);
487 if (r >= 0) {
488 r = link_set_hostname(link, hostname);
489 if (r < 0)
490 log_link_error(link,
491 "Failed to set transient hostname to '%s'",
492 hostname);
493 }
494 }
495
496 if (!link->network->dhcp_critical) {
497 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease,
498 &lifetime);
499 if (r < 0) {
500 log_link_warning(link,
501 "DHCP error: no lifetime: %s",
502 strerror(-r));
503 return r;
504 }
505 }
506
507 r = dhcp4_update_address(link, &address, &netmask, lifetime);
508 if (r < 0) {
509 log_link_warning(link, "could not update IP address: %s",
510 strerror(-r));
511 link_enter_failed(link);
512 return r;
513 }
514
515 return 0;
516 }
517 static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
518 Link *link = userdata;
519 int r = 0;
520
521 assert(link);
522 assert(link->network);
523 assert(link->manager);
524
525 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
526 return;
527
528 switch (event) {
529 case DHCP_EVENT_EXPIRED:
530 case DHCP_EVENT_STOP:
531 case DHCP_EVENT_IP_CHANGE:
532 if (link->network->dhcp_critical) {
533 log_link_error(link,
534 "DHCPv4 connection considered system critical, ignoring request to reconfigure it.");
535 return;
536 }
537
538 if (link->dhcp_lease) {
539 r = dhcp_lease_lost(link);
540 if (r < 0) {
541 link_enter_failed(link);
542 return;
543 }
544 }
545
546 if (event == DHCP_EVENT_IP_CHANGE) {
547 r = dhcp_lease_acquired(client, link);
548 if (r < 0) {
549 link_enter_failed(link);
550 return;
551 }
552 }
553
554 break;
555 case DHCP_EVENT_RENEW:
556 r = dhcp_lease_renew(client, link);
557 if (r < 0) {
558 link_enter_failed(link);
559 return;
560 }
561 break;
562 case DHCP_EVENT_IP_ACQUIRE:
563 r = dhcp_lease_acquired(client, link);
564 if (r < 0) {
565 link_enter_failed(link);
566 return;
567 }
568 break;
569 default:
570 if (event < 0)
571 log_link_warning(link,
572 "DHCP error: client failed: %s",
573 strerror(-event));
574 else
575 log_link_warning(link,
576 "DHCP unknown event: %d",
577 event);
578 break;
579 }
580
581 return;
582 }
583
584 int dhcp4_configure(Link *link) {
585 int r;
586
587 assert(link);
588 assert(link->network);
589 assert(IN_SET(link->network->dhcp, DHCP_SUPPORT_BOTH, DHCP_SUPPORT_V4));
590
591 r = sd_dhcp_client_new(&link->dhcp_client);
592 if (r < 0)
593 return r;
594
595 r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
596 if (r < 0)
597 return r;
598
599 r = sd_dhcp_client_set_mac(link->dhcp_client,
600 (const uint8_t *) &link->mac,
601 sizeof (link->mac), ARPHRD_ETHER);
602 if (r < 0)
603 return r;
604
605 r = sd_dhcp_client_set_index(link->dhcp_client, link->ifindex);
606 if (r < 0)
607 return r;
608
609 r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
610 if (r < 0)
611 return r;
612
613 r = sd_dhcp_client_set_request_broadcast(link->dhcp_client,
614 link->network->dhcp_broadcast);
615 if (r < 0)
616 return r;
617
618 if (link->mtu) {
619 r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
620 if (r < 0)
621 return r;
622 }
623
624 if (link->network->dhcp_mtu) {
625 r = sd_dhcp_client_set_request_option(link->dhcp_client,
626 DHCP_OPTION_INTERFACE_MTU);
627 if (r < 0)
628 return r;
629 }
630
631 if (link->network->dhcp_routes) {
632 r = sd_dhcp_client_set_request_option(link->dhcp_client,
633 DHCP_OPTION_STATIC_ROUTE);
634 if (r < 0)
635 return r;
636 r = sd_dhcp_client_set_request_option(link->dhcp_client,
637 DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
638 if (r < 0)
639 return r;
640 }
641
642 if (link->network->dhcp_sendhost) {
643 _cleanup_free_ char *hostname = NULL;
644
645 hostname = gethostname_malloc();
646 if (!hostname)
647 return -ENOMEM;
648
649 if (!is_localhost(hostname)) {
650 r = sd_dhcp_client_set_hostname(link->dhcp_client,
651 hostname);
652 if (r < 0)
653 return r;
654 }
655 }
656
657 if (link->network->dhcp_vendor_class_identifier) {
658 r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
659 link->network->dhcp_vendor_class_identifier);
660 if (r < 0)
661 return r;
662 }
663
664 return 0;
665 }