]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp4.c
treewide: use log_*_errno whenever %m is in the format string
[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 /* calling handler directly so take a ref */
301 link_ref(link);
302 link_get_address_handler(rtnl, m, link);
303 }
304
305 link_set_dhcp_routes(link);
306
307 return 1;
308 }
309
310 static int dhcp4_update_address(Link *link,
311 struct in_addr *address,
312 struct in_addr *netmask,
313 uint32_t lifetime) {
314 _cleanup_address_free_ Address *addr = NULL;
315 unsigned prefixlen;
316 int r;
317
318 assert(address);
319 assert(netmask);
320 assert(lifetime);
321
322 prefixlen = in_addr_netmask_to_prefixlen(netmask);
323
324 r = address_new_dynamic(&addr);
325 if (r < 0)
326 return r;
327
328 addr->family = AF_INET;
329 addr->in_addr.in.s_addr = address->s_addr;
330 addr->cinfo.ifa_prefered = lifetime;
331 addr->cinfo.ifa_valid = lifetime;
332 addr->prefixlen = prefixlen;
333 addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
334
335 /* use update rather than configure so that we will update the
336 * lifetime of an existing address if it has already been configured */
337 r = address_update(addr, link, &dhcp4_address_handler);
338 if (r < 0)
339 return r;
340
341 return 0;
342 }
343
344 static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
345 sd_dhcp_lease *lease;
346 struct in_addr address;
347 struct in_addr netmask;
348 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
349 int r;
350
351 assert(link);
352 assert(client);
353 assert(link->network);
354
355 r = sd_dhcp_client_get_lease(client, &lease);
356 if (r < 0) {
357 log_link_warning(link, "DHCP error: no lease %s",
358 strerror(-r));
359 return r;
360 }
361
362 sd_dhcp_lease_unref(link->dhcp_lease);
363 link->dhcp4_configured = false;
364 link->dhcp_lease = lease;
365
366 r = sd_dhcp_lease_get_address(lease, &address);
367 if (r < 0) {
368 log_link_warning(link, "DHCP error: no address: %s",
369 strerror(-r));
370 return r;
371 }
372
373 r = sd_dhcp_lease_get_netmask(lease, &netmask);
374 if (r < 0) {
375 log_link_warning(link, "DHCP error: no netmask: %s",
376 strerror(-r));
377 return r;
378 }
379
380 if (!link->network->dhcp_critical) {
381 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease,
382 &lifetime);
383 if (r < 0) {
384 log_link_warning(link,
385 "DHCP error: no lifetime: %s",
386 strerror(-r));
387 return r;
388 }
389 }
390
391 r = dhcp4_update_address(link, &address, &netmask, lifetime);
392 if (r < 0) {
393 log_link_warning(link, "could not update IP address: %s",
394 strerror(-r));
395 link_enter_failed(link);
396 return r;
397 }
398
399 return 0;
400 }
401
402 static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
403 sd_dhcp_lease *lease;
404 struct in_addr address;
405 struct in_addr netmask;
406 struct in_addr gateway;
407 unsigned prefixlen;
408 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
409 int r;
410
411 assert(client);
412 assert(link);
413
414 r = sd_dhcp_client_get_lease(client, &lease);
415 if (r < 0) {
416 log_link_warning(link, "DHCP error: no lease: %s",
417 strerror(-r));
418 return r;
419 }
420
421 r = sd_dhcp_lease_get_address(lease, &address);
422 if (r < 0) {
423 log_link_warning(link, "DHCP error: no address: %s",
424 strerror(-r));
425 return r;
426 }
427
428 r = sd_dhcp_lease_get_netmask(lease, &netmask);
429 if (r < 0) {
430 log_link_warning(link, "DHCP error: no netmask: %s",
431 strerror(-r));
432 return r;
433 }
434
435 prefixlen = in_addr_netmask_to_prefixlen(&netmask);
436
437 r = sd_dhcp_lease_get_router(lease, &gateway);
438 if (r < 0 && r != -ENOENT) {
439 log_link_warning(link, "DHCP error: could not get gateway: %s",
440 strerror(-r));
441 return r;
442 }
443
444 if (r >= 0)
445 log_link_struct(link, LOG_INFO,
446 "MESSAGE=%-*s: DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
447 IFNAMSIZ,
448 link->ifname,
449 ADDRESS_FMT_VAL(address),
450 prefixlen,
451 ADDRESS_FMT_VAL(gateway),
452 "ADDRESS=%u.%u.%u.%u",
453 ADDRESS_FMT_VAL(address),
454 "PREFIXLEN=%u",
455 prefixlen,
456 "GATEWAY=%u.%u.%u.%u",
457 ADDRESS_FMT_VAL(gateway),
458 NULL);
459 else
460 log_link_struct(link, LOG_INFO,
461 "MESSAGE=%-*s: DHCPv4 address %u.%u.%u.%u/%u",
462 IFNAMSIZ,
463 link->ifname,
464 ADDRESS_FMT_VAL(address),
465 prefixlen,
466 "ADDRESS=%u.%u.%u.%u",
467 ADDRESS_FMT_VAL(address),
468 "PREFIXLEN=%u",
469 prefixlen,
470 NULL);
471
472 link->dhcp_lease = lease;
473
474 if (link->network->dhcp_mtu) {
475 uint16_t mtu;
476
477 r = sd_dhcp_lease_get_mtu(lease, &mtu);
478 if (r >= 0) {
479 r = link_set_mtu(link, mtu);
480 if (r < 0)
481 log_link_error(link, "Failed to set MTU "
482 "to %" PRIu16, mtu);
483 }
484 }
485
486 if (link->network->dhcp_hostname) {
487 const char *hostname;
488
489 r = sd_dhcp_lease_get_hostname(lease, &hostname);
490 if (r >= 0) {
491 r = link_set_hostname(link, hostname);
492 if (r < 0)
493 log_link_error(link,
494 "Failed to set transient hostname to '%s'",
495 hostname);
496 }
497 }
498
499 if (!link->network->dhcp_critical) {
500 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease,
501 &lifetime);
502 if (r < 0) {
503 log_link_warning(link,
504 "DHCP error: no lifetime: %s",
505 strerror(-r));
506 return r;
507 }
508 }
509
510 r = dhcp4_update_address(link, &address, &netmask, lifetime);
511 if (r < 0) {
512 log_link_warning(link, "could not update IP address: %s",
513 strerror(-r));
514 link_enter_failed(link);
515 return r;
516 }
517
518 return 0;
519 }
520 static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
521 Link *link = userdata;
522 int r = 0;
523
524 assert(link);
525 assert(link->network);
526 assert(link->manager);
527
528 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
529 return;
530
531 switch (event) {
532 case DHCP_EVENT_EXPIRED:
533 case DHCP_EVENT_STOP:
534 case DHCP_EVENT_IP_CHANGE:
535 if (link->network->dhcp_critical) {
536 log_link_error(link,
537 "DHCPv4 connection considered system critical, ignoring request to reconfigure it.");
538 return;
539 }
540
541 if (link->dhcp_lease) {
542 r = dhcp_lease_lost(link);
543 if (r < 0) {
544 link_enter_failed(link);
545 return;
546 }
547 }
548
549 if (event == DHCP_EVENT_IP_CHANGE) {
550 r = dhcp_lease_acquired(client, link);
551 if (r < 0) {
552 link_enter_failed(link);
553 return;
554 }
555 }
556
557 break;
558 case DHCP_EVENT_RENEW:
559 r = dhcp_lease_renew(client, link);
560 if (r < 0) {
561 link_enter_failed(link);
562 return;
563 }
564 break;
565 case DHCP_EVENT_IP_ACQUIRE:
566 r = dhcp_lease_acquired(client, link);
567 if (r < 0) {
568 link_enter_failed(link);
569 return;
570 }
571 break;
572 default:
573 if (event < 0)
574 log_link_warning(link,
575 "DHCP error: client failed: %s",
576 strerror(-event));
577 else
578 log_link_warning(link,
579 "DHCP unknown event: %d",
580 event);
581 break;
582 }
583
584 return;
585 }
586
587 int dhcp4_configure(Link *link) {
588 int r;
589
590 assert(link);
591 assert(link->network);
592 assert(IN_SET(link->network->dhcp, DHCP_SUPPORT_BOTH, DHCP_SUPPORT_V4));
593
594 r = sd_dhcp_client_new(&link->dhcp_client);
595 if (r < 0)
596 return r;
597
598 r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
599 if (r < 0)
600 return r;
601
602 r = sd_dhcp_client_set_mac(link->dhcp_client,
603 (const uint8_t *) &link->mac,
604 sizeof (link->mac), ARPHRD_ETHER);
605 if (r < 0)
606 return r;
607
608 r = sd_dhcp_client_set_index(link->dhcp_client, link->ifindex);
609 if (r < 0)
610 return r;
611
612 r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
613 if (r < 0)
614 return r;
615
616 r = sd_dhcp_client_set_request_broadcast(link->dhcp_client,
617 link->network->dhcp_broadcast);
618 if (r < 0)
619 return r;
620
621 if (link->mtu) {
622 r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
623 if (r < 0)
624 return r;
625 }
626
627 if (link->network->dhcp_mtu) {
628 r = sd_dhcp_client_set_request_option(link->dhcp_client,
629 DHCP_OPTION_INTERFACE_MTU);
630 if (r < 0)
631 return r;
632 }
633
634 if (link->network->dhcp_routes) {
635 r = sd_dhcp_client_set_request_option(link->dhcp_client,
636 DHCP_OPTION_STATIC_ROUTE);
637 if (r < 0)
638 return r;
639 r = sd_dhcp_client_set_request_option(link->dhcp_client,
640 DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
641 if (r < 0)
642 return r;
643 }
644
645 if (link->network->dhcp_sendhost) {
646 _cleanup_free_ char *hostname = NULL;
647
648 hostname = gethostname_malloc();
649 if (!hostname)
650 return -ENOMEM;
651
652 if (!is_localhost(hostname)) {
653 r = sd_dhcp_client_set_hostname(link->dhcp_client,
654 hostname);
655 if (r < 0)
656 return r;
657 }
658 }
659
660 if (link->network->dhcp_vendor_class_identifier) {
661 r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
662 link->network->dhcp_vendor_class_identifier);
663 if (r < 0)
664 return r;
665 }
666
667 return 0;
668 }