]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp4.c
Merge branch 'hostnamectl-dot-v2'
[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 "hostname-util.h"
26 #include "networkd-link.h"
27 #include "network-internal.h"
28 #include "dhcp-lease-internal.h"
29
30 static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
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
40 r = sd_netlink_message_get_errno(m);
41 if (r < 0 && r != -EEXIST) {
42 log_link_error(link, "could not set DHCPv4 route: %s",
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
55 static 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) {
65 log_link_warning(link,
66 "DHCP error: could not get gateway: %s",
67 strerror(-r));
68 return r;
69 }
70 if (r >= 0) {
71 struct in_addr address;
72 _cleanup_route_free_ Route *route = NULL;
73 _cleanup_route_free_ Route *route_gw = NULL;
74
75 r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
76 if (r < 0) {
77 log_link_warning(link,
78 "DHCP error: could not get address: %s",
79 strerror(-r));
80 return r;
81 }
82
83 r = route_new_dynamic(&route, RTPROT_DHCP);
84 if (r < 0) {
85 log_link_error(link,
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) {
93 log_link_error(link,
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;
105 route_gw->prefsrc_addr.in = address;
106 route_gw->scope = RT_SCOPE_LINK;
107 route_gw->metrics = link->network->dhcp_route_metric;
108
109 r = route_configure(route_gw, link, &dhcp4_route_handler);
110 if (r < 0) {
111 log_link_warning(link,
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;
121 route->prefsrc_addr.in = address;
122 route->metrics = link->network->dhcp_route_metric;
123
124 r = route_configure(route, link, &dhcp4_route_handler);
125 if (r < 0) {
126 log_link_warning(link,
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) {
140 log_link_warning(link,
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) {
152 log_link_error(link, "Could not allocate route: %s",
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;
161 route->metrics = link->network->dhcp_route_metric;
162
163 r = route_configure(route, link, &dhcp4_route_handler);
164 if (r < 0) {
165 log_link_warning(link,
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
177 static 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;
182 unsigned prefixlen = 0;
183 int r;
184
185 assert(link);
186 assert(link->dhcp_lease);
187
188 log_link_warning(link, "DHCP lease lost");
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
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);
246
247 address->family = AF_INET;
248 address->in_addr.in = addr;
249 address->prefixlen = prefixlen;
250
251 address_drop(address, link, &link_address_drop_handler);
252 }
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) {
262 log_link_warning(link,
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
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);
280 if (r < 0)
281 log_link_error_errno(link, r,
282 "Failed to set transient hostname to '%s': %m",
283 hostname);
284
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
294 static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
295 void *userdata) {
296 _cleanup_link_unref_ Link *link = userdata;
297 int r;
298
299 assert(link);
300
301 r = sd_netlink_message_get_errno(m);
302 if (r < 0 && r != -EEXIST) {
303 log_link_error(link, "could not set DHCPv4 address: %s",
304 strerror(-r));
305 link_enter_failed(link);
306 } else if (r >= 0)
307 link_rtnl_process_address(rtnl, m, link->manager);
308
309 link_set_dhcp_routes(link);
310
311 return 1;
312 }
313
314 static 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
348 static 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) {
361 log_link_warning(link, "DHCP error: no lease %s",
362 strerror(-r));
363 return r;
364 }
365
366 sd_dhcp_lease_unref(link->dhcp_lease);
367 link->dhcp4_configured = false;
368 link->dhcp_lease = lease;
369
370 r = sd_dhcp_lease_get_address(lease, &address);
371 if (r < 0) {
372 log_link_warning(link, "DHCP error: no address: %s",
373 strerror(-r));
374 return r;
375 }
376
377 r = sd_dhcp_lease_get_netmask(lease, &netmask);
378 if (r < 0) {
379 log_link_warning(link, "DHCP error: no netmask: %s",
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) {
388 log_link_warning(link,
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) {
397 log_link_warning(link, "could not update IP address: %s",
398 strerror(-r));
399 link_enter_failed(link);
400 return r;
401 }
402
403 return 0;
404 }
405
406 static 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);
419 if (r < 0)
420 return log_link_error_errno(link, r, "DHCP error: no lease: %m");
421
422 r = sd_dhcp_lease_get_address(lease, &address);
423 if (r < 0)
424 return log_link_error_errno(link, r, "DHCP error: no address: %m");
425
426 r = sd_dhcp_lease_get_netmask(lease, &netmask);
427 if (r < 0)
428 return log_link_error_errno(link, r, "DHCP error: no netmask: %m");
429
430 prefixlen = in_addr_netmask_to_prefixlen(&netmask);
431
432 r = sd_dhcp_lease_get_router(lease, &gateway);
433 if (r < 0 && r != -ENOENT)
434 return log_link_error_errno(link, r, "DHCP error: could not get gateway: %m");
435
436 if (r >= 0)
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);
447 else
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);
456
457 link->dhcp_lease = lease;
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)
466 log_link_error_errno(link, r, "Failed to set MTU to %" PRIu16 ": %m", mtu);
467 }
468 }
469
470 if (link->network->dhcp_hostname) {
471 const char *hostname = NULL;
472
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) {
479 r = link_set_hostname(link, hostname);
480 if (r < 0)
481 log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname);
482 }
483 }
484
485 if (!link->network->dhcp_critical) {
486 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
487 if (r < 0) {
488 log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
489 return r;
490 }
491 }
492
493 r = dhcp4_update_address(link, &address, &netmask, lifetime);
494 if (r < 0) {
495 log_link_warning_errno(link, r, "Could not update IP address: %m");
496 link_enter_failed(link);
497 return r;
498 }
499
500 return 0;
501 }
502 static 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) {
518 log_link_error(link,
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)
556 log_link_warning(link,
557 "DHCP error: client failed: %s",
558 strerror(-event));
559 else
560 log_link_warning(link,
561 "DHCP unknown event: %d",
562 event);
563 break;
564 }
565
566 return;
567 }
568
569 int dhcp4_configure(Link *link) {
570 int r;
571
572 assert(link);
573 assert(link->network);
574 assert(link->network->dhcp & ADDRESS_FAMILY_IPV4);
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
584 r = sd_dhcp_client_set_mac(link->dhcp_client,
585 (const uint8_t *) &link->mac,
586 sizeof (link->mac), ARPHRD_ETHER);
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) {
610 r = sd_dhcp_client_set_request_option(link->dhcp_client,
611 DHCP_OPTION_INTERFACE_MTU);
612 if (r < 0)
613 return r;
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);
623 if (r < 0)
624 return r;
625 }
626
627 if (link->network->dhcp_sendhost) {
628 _cleanup_free_ char *hostname = NULL;
629 const char *hn = NULL;
630
631 if (!link->network->hostname) {
632 hostname = gethostname_malloc();
633 if (!hostname)
634 return -ENOMEM;
635
636 hn = hostname;
637 } else
638 hn = link->network->hostname;
639
640 if (!is_localhost(hn)) {
641 r = sd_dhcp_client_set_hostname(link->dhcp_client, hn);
642 if (r < 0)
643 return r;
644 }
645 }
646
647 if (link->network->dhcp_vendor_class_identifier) {
648 r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
649 link->network->dhcp_vendor_class_identifier);
650 if (r < 0)
651 return r;
652 }
653
654 switch (link->network->dhcp_client_identifier) {
655 case DHCP_CLIENT_ID_DUID:
656 /* Library defaults to this. */
657 break;
658 case DHCP_CLIENT_ID_MAC:
659 r = sd_dhcp_client_set_client_id(link->dhcp_client,
660 ARPHRD_ETHER,
661 (const uint8_t *) &link->mac,
662 sizeof (link->mac));
663 if (r < 0)
664 return r;
665 break;
666 default:
667 assert_not_reached("Unknown client identifier type.");
668 }
669
670 return 0;
671 }