]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-dhcp4.c
dhcp: don't underflow in lease time calculations
[thirdparty/systemd.git] / src / network / networkd-dhcp4.c
CommitLineData
3c9b8860
TG
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
958b66ea 25#include "hostname-util.h"
3c9b8860
TG
26#include "networkd-link.h"
27#include "network-internal.h"
28#include "dhcp-lease-internal.h"
29
1c4baffc 30static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
3c9b8860
TG
31 void *userdata) {
32 _cleanup_link_unref_ Link *link = userdata;
33 int r;
34
35 assert(link);
36 assert(link->dhcp4_messages);
37
38 link->dhcp4_messages --;
39
1c4baffc 40 r = sd_netlink_message_get_errno(m);
3c9b8860 41 if (r < 0 && r != -EEXIST) {
79008bdd 42 log_link_error(link, "could not set DHCPv4 route: %s",
3c9b8860
TG
43 strerror(-r));
44 link_enter_failed(link);
45 }
46
47 if (!link->dhcp4_messages) {
48 link->dhcp4_configured = true;
49 link_client_handler(link);
50 }
51
52 return 1;
53}
54
55static int link_set_dhcp_routes(Link *link) {
56 struct in_addr gateway;
57 struct sd_dhcp_route *static_routes;
58 int r, n, i;
59
60 assert(link);
61 assert(link->dhcp_lease);
62
63 r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
64 if (r < 0 && r != -ENOENT) {
79008bdd 65 log_link_warning(link,
3c9b8860
TG
66 "DHCP error: could not get gateway: %s",
67 strerror(-r));
68 return r;
69 }
70 if (r >= 0) {
46b0c76e 71 struct in_addr address;
3c9b8860
TG
72 _cleanup_route_free_ Route *route = NULL;
73 _cleanup_route_free_ Route *route_gw = NULL;
74
46b0c76e
ERB
75 r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
76 if (r < 0) {
79008bdd 77 log_link_warning(link,
46b0c76e
ERB
78 "DHCP error: could not get address: %s",
79 strerror(-r));
80 return r;
81 }
82
3c9b8860
TG
83 r = route_new_dynamic(&route, RTPROT_DHCP);
84 if (r < 0) {
79008bdd 85 log_link_error(link,
3c9b8860
TG
86 "Could not allocate route: %s",
87 strerror(-r));
88 return r;
89 }
90
91 r = route_new_dynamic(&route_gw, RTPROT_DHCP);
92 if (r < 0) {
79008bdd 93 log_link_error(link,
3c9b8860
TG
94 "Could not allocate route: %s",
95 strerror(-r));
96 return r;
97 }
98
99 /* The dhcp netmask may mask out the gateway. Add an explicit
100 * route for the gw host so that we can route no matter the
101 * netmask or existing kernel route tables. */
102 route_gw->family = AF_INET;
103 route_gw->dst_addr.in = gateway;
104 route_gw->dst_prefixlen = 32;
46b0c76e 105 route_gw->prefsrc_addr.in = address;
3c9b8860 106 route_gw->scope = RT_SCOPE_LINK;
84b5b79a 107 route_gw->metrics = link->network->dhcp_route_metric;
3c9b8860
TG
108
109 r = route_configure(route_gw, link, &dhcp4_route_handler);
110 if (r < 0) {
79008bdd 111 log_link_warning(link,
3c9b8860
TG
112 "could not set host route: %s",
113 strerror(-r));
114 return r;
115 }
116
117 link->dhcp4_messages ++;
118
119 route->family = AF_INET;
120 route->in_addr.in = gateway;
46b0c76e 121 route->prefsrc_addr.in = address;
84b5b79a 122 route->metrics = link->network->dhcp_route_metric;
3c9b8860
TG
123
124 r = route_configure(route, link, &dhcp4_route_handler);
125 if (r < 0) {
79008bdd 126 log_link_warning(link,
3c9b8860
TG
127 "could not set routes: %s",
128 strerror(-r));
129 link_enter_failed(link);
130 return r;
131 }
132
133 link->dhcp4_messages ++;
134 }
135
136 n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
137 if (n == -ENOENT)
138 return 0;
139 if (n < 0) {
79008bdd 140 log_link_warning(link,
3c9b8860
TG
141 "DHCP error: could not get routes: %s",
142 strerror(-n));
143
144 return n;
145 }
146
147 for (i = 0; i < n; i++) {
148 _cleanup_route_free_ Route *route = NULL;
149
150 r = route_new_dynamic(&route, RTPROT_DHCP);
151 if (r < 0) {
79008bdd 152 log_link_error(link, "Could not allocate route: %s",
3c9b8860
TG
153 strerror(-r));
154 return r;
155 }
156
157 route->family = AF_INET;
158 route->in_addr.in = static_routes[i].gw_addr;
159 route->dst_addr.in = static_routes[i].dst_addr;
160 route->dst_prefixlen = static_routes[i].dst_prefixlen;
84b5b79a 161 route->metrics = link->network->dhcp_route_metric;
3c9b8860
TG
162
163 r = route_configure(route, link, &dhcp4_route_handler);
164 if (r < 0) {
79008bdd 165 log_link_warning(link,
3c9b8860
TG
166 "could not set host route: %s",
167 strerror(-r));
168 return r;
169 }
170
171 link->dhcp4_messages ++;
172 }
173
174 return 0;
175}
176
177static int dhcp_lease_lost(Link *link) {
178 _cleanup_address_free_ Address *address = NULL;
179 struct in_addr addr;
180 struct in_addr netmask;
181 struct in_addr gateway;
f414a269 182 unsigned prefixlen = 0;
3c9b8860
TG
183 int r;
184
185 assert(link);
186 assert(link->dhcp_lease);
187
79008bdd 188 log_link_warning(link, "DHCP lease lost");
3c9b8860
TG
189
190 if (link->network->dhcp_routes) {
191 struct sd_dhcp_route *routes;
192 int n, i;
193
194 n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes);
195 if (n >= 0) {
196 for (i = 0; i < n; i++) {
197 _cleanup_route_free_ Route *route = NULL;
198
199 r = route_new_dynamic(&route, RTPROT_UNSPEC);
200 if (r >= 0) {
201 route->family = AF_INET;
202 route->in_addr.in = routes[i].gw_addr;
203 route->dst_addr.in = routes[i].dst_addr;
204 route->dst_prefixlen = routes[i].dst_prefixlen;
205
206 route_drop(route, link,
207 &link_route_drop_handler);
208 }
209 }
210 }
211 }
212
213 r = address_new_dynamic(&address);
214 if (r >= 0) {
215 r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
216 if (r >= 0) {
217 _cleanup_route_free_ Route *route_gw = NULL;
218 _cleanup_route_free_ Route *route = NULL;
219
220 r = route_new_dynamic(&route_gw, RTPROT_UNSPEC);
221 if (r >= 0) {
222 route_gw->family = AF_INET;
223 route_gw->dst_addr.in = gateway;
224 route_gw->dst_prefixlen = 32;
225 route_gw->scope = RT_SCOPE_LINK;
226
227 route_drop(route_gw, link,
228 &link_route_drop_handler);
229 }
230
231 r = route_new_dynamic(&route, RTPROT_UNSPEC);
232 if (r >= 0) {
233 route->family = AF_INET;
234 route->in_addr.in = gateway;
235
236 route_drop(route, link,
237 &link_route_drop_handler);
238 }
239 }
240
f414a269
TG
241 r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr);
242 if (r >= 0) {
243 r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
244 if (r >= 0)
245 prefixlen = in_addr_netmask_to_prefixlen(&netmask);
3c9b8860 246
f414a269
TG
247 address->family = AF_INET;
248 address->in_addr.in = addr;
249 address->prefixlen = prefixlen;
3c9b8860 250
7d6884b6 251 address_drop(address, link, &link_address_drop_handler);
f414a269 252 }
3c9b8860
TG
253 }
254
255 if (link->network->dhcp_mtu) {
256 uint16_t mtu;
257
258 r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu);
259 if (r >= 0 && link->original_mtu != mtu) {
260 r = link_set_mtu(link, link->original_mtu);
261 if (r < 0) {
79008bdd 262 log_link_warning(link,
3c9b8860
TG
263 "DHCP error: could not reset MTU");
264 link_enter_failed(link);
265 return r;
266 }
267 }
268 }
269
270 if (link->network->dhcp_hostname) {
271 const char *hostname = NULL;
272
a7d0ef44
SS
273 if (!link->network->hostname)
274 r = sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
275 else
276 hostname = link->network->hostname;
277
278 if (r >= 0 || hostname) {
279 r = link_set_hostname(link, hostname);
3c9b8860 280 if (r < 0)
a7d0ef44
SS
281 log_link_error_errno(link, r,
282 "Failed to set transient hostname to '%s': %m",
283 hostname);
284
3c9b8860
TG
285 }
286 }
287
288 link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
289 link->dhcp4_configured = false;
290
291 return 0;
292}
293
1c4baffc 294static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
3c9b8860
TG
295 void *userdata) {
296 _cleanup_link_unref_ Link *link = userdata;
297 int r;
298
299 assert(link);
300
1c4baffc 301 r = sd_netlink_message_get_errno(m);
3c9b8860 302 if (r < 0 && r != -EEXIST) {
79008bdd 303 log_link_error(link, "could not set DHCPv4 address: %s",
3c9b8860
TG
304 strerror(-r));
305 link_enter_failed(link);
45af44d4
TG
306 } else if (r >= 0)
307 link_rtnl_process_address(rtnl, m, link->manager);
3c9b8860
TG
308
309 link_set_dhcp_routes(link);
310
311 return 1;
312}
313
314static int dhcp4_update_address(Link *link,
315 struct in_addr *address,
316 struct in_addr *netmask,
317 uint32_t lifetime) {
318 _cleanup_address_free_ Address *addr = NULL;
319 unsigned prefixlen;
320 int r;
321
322 assert(address);
323 assert(netmask);
324 assert(lifetime);
325
326 prefixlen = in_addr_netmask_to_prefixlen(netmask);
327
328 r = address_new_dynamic(&addr);
329 if (r < 0)
330 return r;
331
332 addr->family = AF_INET;
333 addr->in_addr.in.s_addr = address->s_addr;
334 addr->cinfo.ifa_prefered = lifetime;
335 addr->cinfo.ifa_valid = lifetime;
336 addr->prefixlen = prefixlen;
337 addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
338
339 /* use update rather than configure so that we will update the
340 * lifetime of an existing address if it has already been configured */
341 r = address_update(addr, link, &dhcp4_address_handler);
342 if (r < 0)
343 return r;
344
345 return 0;
346}
347
348static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
349 sd_dhcp_lease *lease;
350 struct in_addr address;
351 struct in_addr netmask;
352 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
353 int r;
354
355 assert(link);
356 assert(client);
357 assert(link->network);
358
359 r = sd_dhcp_client_get_lease(client, &lease);
360 if (r < 0) {
79008bdd 361 log_link_warning(link, "DHCP error: no lease %s",
3c9b8860
TG
362 strerror(-r));
363 return r;
364 }
365
366 sd_dhcp_lease_unref(link->dhcp_lease);
367 link->dhcp4_configured = false;
e6b18ffa 368 link->dhcp_lease = sd_dhcp_lease_ref(lease);
3c9b8860
TG
369
370 r = sd_dhcp_lease_get_address(lease, &address);
371 if (r < 0) {
79008bdd 372 log_link_warning(link, "DHCP error: no address: %s",
3c9b8860
TG
373 strerror(-r));
374 return r;
375 }
376
377 r = sd_dhcp_lease_get_netmask(lease, &netmask);
378 if (r < 0) {
79008bdd 379 log_link_warning(link, "DHCP error: no netmask: %s",
3c9b8860
TG
380 strerror(-r));
381 return r;
382 }
383
384 if (!link->network->dhcp_critical) {
385 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease,
386 &lifetime);
387 if (r < 0) {
79008bdd 388 log_link_warning(link,
3c9b8860
TG
389 "DHCP error: no lifetime: %s",
390 strerror(-r));
391 return r;
392 }
393 }
394
395 r = dhcp4_update_address(link, &address, &netmask, lifetime);
396 if (r < 0) {
79008bdd 397 log_link_warning(link, "could not update IP address: %s",
3c9b8860
TG
398 strerror(-r));
399 link_enter_failed(link);
400 return r;
401 }
402
403 return 0;
404}
405
406static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
407 sd_dhcp_lease *lease;
408 struct in_addr address;
409 struct in_addr netmask;
410 struct in_addr gateway;
411 unsigned prefixlen;
412 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
413 int r;
414
415 assert(client);
416 assert(link);
417
418 r = sd_dhcp_client_get_lease(client, &lease);
f2341e0a
LP
419 if (r < 0)
420 return log_link_error_errno(link, r, "DHCP error: no lease: %m");
3c9b8860
TG
421
422 r = sd_dhcp_lease_get_address(lease, &address);
f2341e0a
LP
423 if (r < 0)
424 return log_link_error_errno(link, r, "DHCP error: no address: %m");
3c9b8860
TG
425
426 r = sd_dhcp_lease_get_netmask(lease, &netmask);
f2341e0a
LP
427 if (r < 0)
428 return log_link_error_errno(link, r, "DHCP error: no netmask: %m");
3c9b8860
TG
429
430 prefixlen = in_addr_netmask_to_prefixlen(&netmask);
431
432 r = sd_dhcp_lease_get_router(lease, &gateway);
f2341e0a
LP
433 if (r < 0 && r != -ENOENT)
434 return log_link_error_errno(link, r, "DHCP error: could not get gateway: %m");
3c9b8860
TG
435
436 if (r >= 0)
f2341e0a
LP
437 log_struct(LOG_INFO,
438 LOG_LINK_INTERFACE(link),
439 LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
440 ADDRESS_FMT_VAL(address),
441 prefixlen,
442 ADDRESS_FMT_VAL(gateway)),
443 "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
444 "PREFIXLEN=%u", prefixlen,
445 "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(gateway),
446 NULL);
3c9b8860 447 else
f2341e0a
LP
448 log_struct(LOG_INFO,
449 LOG_LINK_INTERFACE(link),
450 LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u",
451 ADDRESS_FMT_VAL(address),
452 prefixlen),
453 "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
454 "PREFIXLEN=%u", prefixlen,
455 NULL);
3c9b8860 456
e6b18ffa 457 link->dhcp_lease = sd_dhcp_lease_ref(lease);
3c9b8860
TG
458
459 if (link->network->dhcp_mtu) {
460 uint16_t mtu;
461
462 r = sd_dhcp_lease_get_mtu(lease, &mtu);
463 if (r >= 0) {
464 r = link_set_mtu(link, mtu);
465 if (r < 0)
f2341e0a 466 log_link_error_errno(link, r, "Failed to set MTU to %" PRIu16 ": %m", mtu);
3c9b8860
TG
467 }
468 }
469
470 if (link->network->dhcp_hostname) {
49f6e11e 471 const char *hostname = NULL;
3c9b8860 472
a7d0ef44
SS
473 if (!link->network->hostname)
474 r = sd_dhcp_lease_get_hostname(lease, &hostname);
475 else
476 hostname = link->network->hostname;
477
478 if (r >= 0 || hostname) {
3c9b8860
TG
479 r = link_set_hostname(link, hostname);
480 if (r < 0)
f2341e0a 481 log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname);
3c9b8860
TG
482 }
483 }
484
485 if (!link->network->dhcp_critical) {
f2341e0a 486 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
3c9b8860 487 if (r < 0) {
f2341e0a 488 log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
3c9b8860
TG
489 return r;
490 }
491 }
492
493 r = dhcp4_update_address(link, &address, &netmask, lifetime);
494 if (r < 0) {
f2341e0a 495 log_link_warning_errno(link, r, "Could not update IP address: %m");
3c9b8860
TG
496 link_enter_failed(link);
497 return r;
498 }
499
500 return 0;
501}
502static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
503 Link *link = userdata;
504 int r = 0;
505
506 assert(link);
507 assert(link->network);
508 assert(link->manager);
509
510 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
511 return;
512
513 switch (event) {
514 case DHCP_EVENT_EXPIRED:
515 case DHCP_EVENT_STOP:
516 case DHCP_EVENT_IP_CHANGE:
517 if (link->network->dhcp_critical) {
79008bdd 518 log_link_error(link,
3c9b8860
TG
519 "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 == DHCP_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 DHCP_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 DHCP_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)
79008bdd 556 log_link_warning(link,
3c9b8860
TG
557 "DHCP error: client failed: %s",
558 strerror(-event));
559 else
79008bdd 560 log_link_warning(link,
3c9b8860
TG
561 "DHCP unknown event: %d",
562 event);
563 break;
564 }
565
566 return;
567}
568
569int dhcp4_configure(Link *link) {
570 int r;
571
572 assert(link);
573 assert(link->network);
e0ee46f2 574 assert(link->network->dhcp & ADDRESS_FAMILY_IPV4);
3c9b8860
TG
575
576 r = sd_dhcp_client_new(&link->dhcp_client);
577 if (r < 0)
578 return r;
579
580 r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
581 if (r < 0)
582 return r;
583
76253e73
DW
584 r = sd_dhcp_client_set_mac(link->dhcp_client,
585 (const uint8_t *) &link->mac,
586 sizeof (link->mac), ARPHRD_ETHER);
3c9b8860
TG
587 if (r < 0)
588 return r;
589
590 r = sd_dhcp_client_set_index(link->dhcp_client, link->ifindex);
591 if (r < 0)
592 return r;
593
594 r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
595 if (r < 0)
596 return r;
597
598 r = sd_dhcp_client_set_request_broadcast(link->dhcp_client,
599 link->network->dhcp_broadcast);
600 if (r < 0)
601 return r;
602
603 if (link->mtu) {
604 r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
605 if (r < 0)
606 return r;
607 }
608
609 if (link->network->dhcp_mtu) {
39745a5a
LP
610 r = sd_dhcp_client_set_request_option(link->dhcp_client,
611 DHCP_OPTION_INTERFACE_MTU);
612 if (r < 0)
613 return r;
3c9b8860
TG
614 }
615
616 if (link->network->dhcp_routes) {
617 r = sd_dhcp_client_set_request_option(link->dhcp_client,
618 DHCP_OPTION_STATIC_ROUTE);
619 if (r < 0)
620 return r;
621 r = sd_dhcp_client_set_request_option(link->dhcp_client,
622 DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
7d6884b6
TA
623 if (r < 0)
624 return r;
3c9b8860
TG
625 }
626
4b7b5abb
LP
627 /* Always acquire the timezone and NTP*/
628 r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NTP_SERVER);
629 if (r < 0)
630 return r;
631
8eb9058d
LP
632 r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NEW_TZDB_TIMEZONE);
633 if (r < 0)
634 return r;
635
3c9b8860
TG
636 if (link->network->dhcp_sendhost) {
637 _cleanup_free_ char *hostname = NULL;
a7d0ef44
SS
638 const char *hn = NULL;
639
640 if (!link->network->hostname) {
641 hostname = gethostname_malloc();
642 if (!hostname)
643 return -ENOMEM;
3c9b8860 644
a7d0ef44
SS
645 hn = hostname;
646 } else
647 hn = link->network->hostname;
3c9b8860 648
a7d0ef44
SS
649 if (!is_localhost(hn)) {
650 r = sd_dhcp_client_set_hostname(link->dhcp_client, hn);
3c9b8860
TG
651 if (r < 0)
652 return r;
653 }
654 }
655
656 if (link->network->dhcp_vendor_class_identifier) {
657 r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
658 link->network->dhcp_vendor_class_identifier);
659 if (r < 0)
660 return r;
661 }
662
3e43b2cd
JJ
663 switch (link->network->dhcp_client_identifier) {
664 case DHCP_CLIENT_ID_DUID:
665 /* Library defaults to this. */
666 break;
667 case DHCP_CLIENT_ID_MAC:
668 r = sd_dhcp_client_set_client_id(link->dhcp_client,
669 ARPHRD_ETHER,
670 (const uint8_t *) &link->mac,
671 sizeof (link->mac));
672 if (r < 0)
673 return r;
674 break;
675 default:
676 assert_not_reached("Unknown client identifier type.");
677 }
678
3c9b8860
TG
679 return 0;
680}