]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-dhcp4.c
core,network: major per-object logging rework
[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
25#include "networkd-link.h"
26#include "network-internal.h"
27#include "dhcp-lease-internal.h"
28
29static 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) {
79008bdd 41 log_link_error(link, "could not set DHCPv4 route: %s",
3c9b8860
TG
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
54static 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) {
79008bdd 64 log_link_warning(link,
3c9b8860
TG
65 "DHCP error: could not get gateway: %s",
66 strerror(-r));
67 return r;
68 }
69 if (r >= 0) {
46b0c76e 70 struct in_addr address;
3c9b8860
TG
71 _cleanup_route_free_ Route *route = NULL;
72 _cleanup_route_free_ Route *route_gw = NULL;
73
46b0c76e
ERB
74 r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
75 if (r < 0) {
79008bdd 76 log_link_warning(link,
46b0c76e
ERB
77 "DHCP error: could not get address: %s",
78 strerror(-r));
79 return r;
80 }
81
3c9b8860
TG
82 r = route_new_dynamic(&route, RTPROT_DHCP);
83 if (r < 0) {
79008bdd 84 log_link_error(link,
3c9b8860
TG
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) {
79008bdd 92 log_link_error(link,
3c9b8860
TG
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;
46b0c76e 104 route_gw->prefsrc_addr.in = address;
3c9b8860 105 route_gw->scope = RT_SCOPE_LINK;
84b5b79a 106 route_gw->metrics = link->network->dhcp_route_metric;
3c9b8860
TG
107
108 r = route_configure(route_gw, link, &dhcp4_route_handler);
109 if (r < 0) {
79008bdd 110 log_link_warning(link,
3c9b8860
TG
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;
46b0c76e 120 route->prefsrc_addr.in = address;
84b5b79a 121 route->metrics = link->network->dhcp_route_metric;
3c9b8860
TG
122
123 r = route_configure(route, link, &dhcp4_route_handler);
124 if (r < 0) {
79008bdd 125 log_link_warning(link,
3c9b8860
TG
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) {
79008bdd 139 log_link_warning(link,
3c9b8860
TG
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) {
79008bdd 151 log_link_error(link, "Could not allocate route: %s",
3c9b8860
TG
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;
84b5b79a 160 route->metrics = link->network->dhcp_route_metric;
3c9b8860
TG
161
162 r = route_configure(route, link, &dhcp4_route_handler);
163 if (r < 0) {
79008bdd 164 log_link_warning(link,
3c9b8860
TG
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
176static 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;
f414a269 181 unsigned prefixlen = 0;
3c9b8860
TG
182 int r;
183
184 assert(link);
185 assert(link->dhcp_lease);
186
79008bdd 187 log_link_warning(link, "DHCP lease lost");
3c9b8860
TG
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
f414a269
TG
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);
3c9b8860 245
f414a269
TG
246 address->family = AF_INET;
247 address->in_addr.in = addr;
248 address->prefixlen = prefixlen;
3c9b8860 249
f414a269
TG
250 address_drop(address, link, &link_address_drop_handler);
251 }
3c9b8860
TG
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) {
79008bdd 261 log_link_warning(link,
3c9b8860
TG
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)
79008bdd 276 log_link_error(link,
3c9b8860
TG
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
287static 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) {
79008bdd 296 log_link_error(link, "could not set DHCPv4 address: %s",
3c9b8860
TG
297 strerror(-r));
298 link_enter_failed(link);
45af44d4
TG
299 } else if (r >= 0)
300 link_rtnl_process_address(rtnl, m, link->manager);
3c9b8860
TG
301
302 link_set_dhcp_routes(link);
303
304 return 1;
305}
306
307static 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
341static 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) {
79008bdd 354 log_link_warning(link, "DHCP error: no lease %s",
3c9b8860
TG
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) {
79008bdd 365 log_link_warning(link, "DHCP error: no address: %s",
3c9b8860
TG
366 strerror(-r));
367 return r;
368 }
369
370 r = sd_dhcp_lease_get_netmask(lease, &netmask);
371 if (r < 0) {
79008bdd 372 log_link_warning(link, "DHCP error: no netmask: %s",
3c9b8860
TG
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) {
79008bdd 381 log_link_warning(link,
3c9b8860
TG
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) {
79008bdd 390 log_link_warning(link, "could not update IP address: %s",
3c9b8860
TG
391 strerror(-r));
392 link_enter_failed(link);
393 return r;
394 }
395
396 return 0;
397}
398
399static 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);
f2341e0a
LP
412 if (r < 0)
413 return log_link_error_errno(link, r, "DHCP error: no lease: %m");
3c9b8860
TG
414
415 r = sd_dhcp_lease_get_address(lease, &address);
f2341e0a
LP
416 if (r < 0)
417 return log_link_error_errno(link, r, "DHCP error: no address: %m");
3c9b8860
TG
418
419 r = sd_dhcp_lease_get_netmask(lease, &netmask);
f2341e0a
LP
420 if (r < 0)
421 return log_link_error_errno(link, r, "DHCP error: no netmask: %m");
3c9b8860
TG
422
423 prefixlen = in_addr_netmask_to_prefixlen(&netmask);
424
425 r = sd_dhcp_lease_get_router(lease, &gateway);
f2341e0a
LP
426 if (r < 0 && r != -ENOENT)
427 return log_link_error_errno(link, r, "DHCP error: could not get gateway: %m");
3c9b8860
TG
428
429 if (r >= 0)
f2341e0a
LP
430 log_struct(LOG_INFO,
431 LOG_LINK_INTERFACE(link),
432 LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
433 ADDRESS_FMT_VAL(address),
434 prefixlen,
435 ADDRESS_FMT_VAL(gateway)),
436 "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
437 "PREFIXLEN=%u", prefixlen,
438 "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(gateway),
439 NULL);
3c9b8860 440 else
f2341e0a
LP
441 log_struct(LOG_INFO,
442 LOG_LINK_INTERFACE(link),
443 LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u",
444 ADDRESS_FMT_VAL(address),
445 prefixlen),
446 "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
447 "PREFIXLEN=%u", prefixlen,
448 NULL);
3c9b8860
TG
449
450 link->dhcp_lease = lease;
451
452 if (link->network->dhcp_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)
f2341e0a 459 log_link_error_errno(link, r, "Failed to set MTU to %" PRIu16 ": %m", mtu);
3c9b8860
TG
460 }
461 }
462
463 if (link->network->dhcp_hostname) {
464 const char *hostname;
465
466 r = sd_dhcp_lease_get_hostname(lease, &hostname);
467 if (r >= 0) {
468 r = link_set_hostname(link, hostname);
469 if (r < 0)
f2341e0a 470 log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname);
3c9b8860
TG
471 }
472 }
473
474 if (!link->network->dhcp_critical) {
f2341e0a 475 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
3c9b8860 476 if (r < 0) {
f2341e0a 477 log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
3c9b8860
TG
478 return r;
479 }
480 }
481
482 r = dhcp4_update_address(link, &address, &netmask, lifetime);
483 if (r < 0) {
f2341e0a 484 log_link_warning_errno(link, r, "Could not update IP address: %m");
3c9b8860
TG
485 link_enter_failed(link);
486 return r;
487 }
488
489 return 0;
490}
491static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
492 Link *link = userdata;
493 int r = 0;
494
495 assert(link);
496 assert(link->network);
497 assert(link->manager);
498
499 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
500 return;
501
502 switch (event) {
503 case DHCP_EVENT_EXPIRED:
504 case DHCP_EVENT_STOP:
505 case DHCP_EVENT_IP_CHANGE:
506 if (link->network->dhcp_critical) {
79008bdd 507 log_link_error(link,
3c9b8860
TG
508 "DHCPv4 connection considered system critical, ignoring request to reconfigure it.");
509 return;
510 }
511
512 if (link->dhcp_lease) {
513 r = dhcp_lease_lost(link);
514 if (r < 0) {
515 link_enter_failed(link);
516 return;
517 }
518 }
519
520 if (event == DHCP_EVENT_IP_CHANGE) {
521 r = dhcp_lease_acquired(client, link);
522 if (r < 0) {
523 link_enter_failed(link);
524 return;
525 }
526 }
527
528 break;
529 case DHCP_EVENT_RENEW:
530 r = dhcp_lease_renew(client, link);
531 if (r < 0) {
532 link_enter_failed(link);
533 return;
534 }
535 break;
536 case DHCP_EVENT_IP_ACQUIRE:
537 r = dhcp_lease_acquired(client, link);
538 if (r < 0) {
539 link_enter_failed(link);
540 return;
541 }
542 break;
543 default:
544 if (event < 0)
79008bdd 545 log_link_warning(link,
3c9b8860
TG
546 "DHCP error: client failed: %s",
547 strerror(-event));
548 else
79008bdd 549 log_link_warning(link,
3c9b8860
TG
550 "DHCP unknown event: %d",
551 event);
552 break;
553 }
554
555 return;
556}
557
558int dhcp4_configure(Link *link) {
559 int r;
560
561 assert(link);
562 assert(link->network);
cb9fc36a 563 assert(IN_SET(link->network->dhcp, ADDRESS_FAMILY_YES, ADDRESS_FAMILY_IPV4));
3c9b8860
TG
564
565 r = sd_dhcp_client_new(&link->dhcp_client);
566 if (r < 0)
567 return r;
568
569 r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
570 if (r < 0)
571 return r;
572
76253e73
DW
573 r = sd_dhcp_client_set_mac(link->dhcp_client,
574 (const uint8_t *) &link->mac,
575 sizeof (link->mac), ARPHRD_ETHER);
3c9b8860
TG
576 if (r < 0)
577 return r;
578
579 r = sd_dhcp_client_set_index(link->dhcp_client, link->ifindex);
580 if (r < 0)
581 return r;
582
583 r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
584 if (r < 0)
585 return r;
586
587 r = sd_dhcp_client_set_request_broadcast(link->dhcp_client,
588 link->network->dhcp_broadcast);
589 if (r < 0)
590 return r;
591
592 if (link->mtu) {
593 r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
594 if (r < 0)
595 return r;
596 }
597
598 if (link->network->dhcp_mtu) {
599 r = sd_dhcp_client_set_request_option(link->dhcp_client,
600 DHCP_OPTION_INTERFACE_MTU);
601 if (r < 0)
602 return r;
603 }
604
605 if (link->network->dhcp_routes) {
606 r = sd_dhcp_client_set_request_option(link->dhcp_client,
607 DHCP_OPTION_STATIC_ROUTE);
608 if (r < 0)
609 return r;
610 r = sd_dhcp_client_set_request_option(link->dhcp_client,
611 DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
612 if (r < 0)
613 return r;
614 }
615
616 if (link->network->dhcp_sendhost) {
617 _cleanup_free_ char *hostname = NULL;
618
619 hostname = gethostname_malloc();
620 if (!hostname)
621 return -ENOMEM;
622
623 if (!is_localhost(hostname)) {
624 r = sd_dhcp_client_set_hostname(link->dhcp_client,
625 hostname);
626 if (r < 0)
627 return r;
628 }
629 }
630
631 if (link->network->dhcp_vendor_class_identifier) {
632 r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
633 link->network->dhcp_vendor_class_identifier);
634 if (r < 0)
635 return r;
636 }
637
3e43b2cd
JJ
638 switch (link->network->dhcp_client_identifier) {
639 case DHCP_CLIENT_ID_DUID:
640 /* Library defaults to this. */
641 break;
642 case DHCP_CLIENT_ID_MAC:
643 r = sd_dhcp_client_set_client_id(link->dhcp_client,
644 ARPHRD_ETHER,
645 (const uint8_t *) &link->mac,
646 sizeof (link->mac));
647 if (r < 0)
648 return r;
649 break;
650 default:
651 assert_not_reached("Unknown client identifier type.");
652 }
653
3c9b8860
TG
654 return 0;
655}