]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp4.c
Merge pull request #1626 from teg/networkd
[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_errno(link, r, "Could not set DHCPv4 route: %m");
43 link_enter_failed(link);
44 }
45
46 if (!link->dhcp4_messages) {
47 link->dhcp4_configured = true;
48 link_check_ready(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 != -ENODATA)
64 return log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m");
65
66 if (r >= 0) {
67 struct in_addr address;
68 _cleanup_route_free_ Route *route = NULL;
69 _cleanup_route_free_ Route *route_gw = NULL;
70
71 r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
72 if (r < 0)
73 return log_link_warning_errno(link, r, "DHCP error: could not get address: %m");
74
75 r = route_new(&route);
76 if (r < 0)
77 return log_link_error_errno(link, r, "Could not allocate route: %m");
78
79 route->protocol = RTPROT_DHCP;
80
81 r = route_new(&route_gw);
82 if (r < 0)
83 return log_link_error_errno(link, r, "Could not allocate route: %m");
84
85 /* The dhcp netmask may mask out the gateway. Add an explicit
86 * route for the gw host so that we can route no matter the
87 * netmask or existing kernel route tables. */
88 route_gw->family = AF_INET;
89 route_gw->dst_addr.in = gateway;
90 route_gw->dst_prefixlen = 32;
91 route_gw->prefsrc_addr.in = address;
92 route_gw->scope = RT_SCOPE_LINK;
93 route_gw->protocol = RTPROT_DHCP;
94 route_gw->metrics = link->network->dhcp_route_metric;
95
96 r = route_configure(route_gw, link, &dhcp4_route_handler);
97 if (r < 0)
98 return log_link_warning_errno(link, r, "Could not set host route: %m");
99
100 link->dhcp4_messages ++;
101
102 route->family = AF_INET;
103 route->in_addr.in = gateway;
104 route->prefsrc_addr.in = address;
105 route->metrics = link->network->dhcp_route_metric;
106
107 r = route_configure(route, link, &dhcp4_route_handler);
108 if (r < 0) {
109 log_link_warning_errno(link, r, "Could not set routes: %m");
110 link_enter_failed(link);
111 return r;
112 }
113
114 link->dhcp4_messages ++;
115 }
116
117 n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
118 if (n == -ENODATA)
119 return 0;
120 if (n < 0)
121 return log_link_warning_errno(link, n, "DHCP error: could not get routes: %m");
122
123 for (i = 0; i < n; i++) {
124 _cleanup_route_free_ Route *route = NULL;
125
126 r = route_new(&route);
127 if (r < 0)
128 return log_link_error_errno(link, r, "Could not allocate route: %m");
129
130 route->family = AF_INET;
131 route->protocol = RTPROT_DHCP;
132 route->in_addr.in = static_routes[i].gw_addr;
133 route->dst_addr.in = static_routes[i].dst_addr;
134 route->dst_prefixlen = static_routes[i].dst_prefixlen;
135 route->metrics = link->network->dhcp_route_metric;
136
137 r = route_configure(route, link, &dhcp4_route_handler);
138 if (r < 0)
139 return log_link_warning_errno(link, r, "Could not set host route: %m");
140
141 link->dhcp4_messages ++;
142 }
143
144 return 0;
145 }
146
147 static int dhcp_lease_lost(Link *link) {
148 _cleanup_address_free_ Address *address = NULL;
149 struct in_addr addr;
150 struct in_addr netmask;
151 struct in_addr gateway;
152 unsigned prefixlen = 0;
153 int r;
154
155 assert(link);
156 assert(link->dhcp_lease);
157
158 log_link_warning(link, "DHCP lease lost");
159
160 if (link->network->dhcp_routes) {
161 struct sd_dhcp_route *routes;
162 int n, i;
163
164 n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes);
165 if (n >= 0) {
166 for (i = 0; i < n; i++) {
167 _cleanup_route_free_ Route *route = NULL;
168
169 r = route_new(&route);
170 if (r >= 0) {
171 route->family = AF_INET;
172 route->in_addr.in = routes[i].gw_addr;
173 route->dst_addr.in = routes[i].dst_addr;
174 route->dst_prefixlen = routes[i].dst_prefixlen;
175
176 route_remove(route, link,
177 &link_route_remove_handler);
178 }
179 }
180 }
181 }
182
183 r = address_new(&address);
184 if (r >= 0) {
185 r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
186 if (r >= 0) {
187 _cleanup_route_free_ Route *route_gw = NULL;
188 _cleanup_route_free_ Route *route = NULL;
189
190 r = route_new(&route_gw);
191 if (r >= 0) {
192 route_gw->family = AF_INET;
193 route_gw->dst_addr.in = gateway;
194 route_gw->dst_prefixlen = 32;
195 route_gw->scope = RT_SCOPE_LINK;
196
197 route_remove(route_gw, link,
198 &link_route_remove_handler);
199 }
200
201 r = route_new(&route);
202 if (r >= 0) {
203 route->family = AF_INET;
204 route->in_addr.in = gateway;
205
206 route_remove(route, link,
207 &link_route_remove_handler);
208 }
209 }
210
211 r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr);
212 if (r >= 0) {
213 r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
214 if (r >= 0)
215 prefixlen = in_addr_netmask_to_prefixlen(&netmask);
216
217 address->family = AF_INET;
218 address->in_addr.in = addr;
219 address->prefixlen = prefixlen;
220
221 address_remove(address, link, &link_address_remove_handler);
222 }
223 }
224
225 if (link->network->dhcp_mtu) {
226 uint16_t mtu;
227
228 r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu);
229 if (r >= 0 && link->original_mtu != mtu) {
230 r = link_set_mtu(link, link->original_mtu);
231 if (r < 0) {
232 log_link_warning(link,
233 "DHCP error: could not reset MTU");
234 link_enter_failed(link);
235 return r;
236 }
237 }
238 }
239
240 if (link->network->dhcp_hostname) {
241 const char *hostname = NULL;
242
243 if (link->network->hostname)
244 hostname = link->network->hostname;
245 else
246 (void) sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
247
248 if (hostname) {
249 /* If a hostname was set due to the lease, then unset it now. */
250 r = link_set_hostname(link, NULL);
251 if (r < 0)
252 log_link_warning_errno(link, r, "Failed to reset transient hostname: %m");
253 }
254 }
255
256 link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
257 link->dhcp4_configured = false;
258
259 return 0;
260 }
261
262 static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
263 void *userdata) {
264 _cleanup_link_unref_ Link *link = userdata;
265 int r;
266
267 assert(link);
268
269 r = sd_netlink_message_get_errno(m);
270 if (r < 0 && r != -EEXIST) {
271 log_link_error_errno(link, r, "Could not set DHCPv4 address: %m");
272 link_enter_failed(link);
273 } else if (r >= 0)
274 manager_rtnl_process_address(rtnl, m, link->manager);
275
276 link_set_dhcp_routes(link);
277
278 return 1;
279 }
280
281 static int dhcp4_update_address(Link *link,
282 struct in_addr *address,
283 struct in_addr *netmask,
284 uint32_t lifetime) {
285 _cleanup_address_free_ Address *addr = NULL;
286 unsigned prefixlen;
287 int r;
288
289 assert(address);
290 assert(netmask);
291 assert(lifetime);
292
293 prefixlen = in_addr_netmask_to_prefixlen(netmask);
294
295 r = address_new(&addr);
296 if (r < 0)
297 return r;
298
299 addr->family = AF_INET;
300 addr->in_addr.in.s_addr = address->s_addr;
301 addr->cinfo.ifa_prefered = lifetime;
302 addr->cinfo.ifa_valid = lifetime;
303 addr->prefixlen = prefixlen;
304 addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
305
306 /* allow reusing an existing address and simply update its lifetime
307 * in case it already exists */
308 r = address_configure(addr, link, &dhcp4_address_handler, true);
309 if (r < 0)
310 return r;
311
312 return 0;
313 }
314
315 static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
316 sd_dhcp_lease *lease;
317 struct in_addr address;
318 struct in_addr netmask;
319 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
320 int r;
321
322 assert(link);
323 assert(client);
324 assert(link->network);
325
326 r = sd_dhcp_client_get_lease(client, &lease);
327 if (r < 0)
328 return log_link_warning_errno(link, r, "DHCP error: no lease: %m");
329
330 sd_dhcp_lease_unref(link->dhcp_lease);
331 link->dhcp4_configured = false;
332 link->dhcp_lease = sd_dhcp_lease_ref(lease);
333
334 r = sd_dhcp_lease_get_address(lease, &address);
335 if (r < 0)
336 return log_link_warning_errno(link, r, "DHCP error: no address: %m");
337
338 r = sd_dhcp_lease_get_netmask(lease, &netmask);
339 if (r < 0)
340 return log_link_warning_errno(link, r, "DHCP error: no netmask: %m");
341
342 if (!link->network->dhcp_critical) {
343 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
344 if (r < 0)
345 return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
346 }
347
348 r = dhcp4_update_address(link, &address, &netmask, lifetime);
349 if (r < 0) {
350 log_link_warning_errno(link, r, "Could not update IP address: %m");
351 link_enter_failed(link);
352 return r;
353 }
354
355 return 0;
356 }
357
358 static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
359 sd_dhcp_lease *lease;
360 struct in_addr address;
361 struct in_addr netmask;
362 struct in_addr gateway;
363 unsigned prefixlen;
364 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
365 int r;
366
367 assert(client);
368 assert(link);
369
370 r = sd_dhcp_client_get_lease(client, &lease);
371 if (r < 0)
372 return log_link_error_errno(link, r, "DHCP error: No lease: %m");
373
374 r = sd_dhcp_lease_get_address(lease, &address);
375 if (r < 0)
376 return log_link_error_errno(link, r, "DHCP error: No address: %m");
377
378 r = sd_dhcp_lease_get_netmask(lease, &netmask);
379 if (r < 0)
380 return log_link_error_errno(link, r, "DHCP error: No netmask: %m");
381
382 prefixlen = in_addr_netmask_to_prefixlen(&netmask);
383
384 r = sd_dhcp_lease_get_router(lease, &gateway);
385 if (r < 0 && r != -ENODATA)
386 return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m");
387
388 if (r >= 0)
389 log_struct(LOG_INFO,
390 LOG_LINK_INTERFACE(link),
391 LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
392 ADDRESS_FMT_VAL(address),
393 prefixlen,
394 ADDRESS_FMT_VAL(gateway)),
395 "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
396 "PREFIXLEN=%u", prefixlen,
397 "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(gateway),
398 NULL);
399 else
400 log_struct(LOG_INFO,
401 LOG_LINK_INTERFACE(link),
402 LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u",
403 ADDRESS_FMT_VAL(address),
404 prefixlen),
405 "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
406 "PREFIXLEN=%u", prefixlen,
407 NULL);
408
409 link->dhcp_lease = sd_dhcp_lease_ref(lease);
410
411 if (link->network->dhcp_mtu) {
412 uint16_t mtu;
413
414 r = sd_dhcp_lease_get_mtu(lease, &mtu);
415 if (r >= 0) {
416 r = link_set_mtu(link, mtu);
417 if (r < 0)
418 log_link_error_errno(link, r, "Failed to set MTU to %" PRIu16 ": %m", mtu);
419 }
420 }
421
422 if (link->network->dhcp_hostname) {
423 const char *hostname = NULL;
424
425 if (link->network->hostname)
426 hostname = link->network->hostname;
427 else
428 (void) sd_dhcp_lease_get_hostname(lease, &hostname);
429
430 if (hostname) {
431 r = link_set_hostname(link, hostname);
432 if (r < 0)
433 log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname);
434 }
435 }
436
437 if (link->network->dhcp_timezone) {
438 const char *tz = NULL;
439
440 (void) sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz);
441
442 if (tz) {
443 r = link_set_timezone(link, tz);
444 if (r < 0)
445 log_link_error_errno(link, r, "Failed to set timezone to '%s': %m", tz);
446 }
447 }
448
449 if (!link->network->dhcp_critical) {
450 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
451 if (r < 0) {
452 log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
453 return r;
454 }
455 }
456
457 r = dhcp4_update_address(link, &address, &netmask, lifetime);
458 if (r < 0) {
459 log_link_warning_errno(link, r, "Could not update IP address: %m");
460 link_enter_failed(link);
461 return r;
462 }
463
464 return 0;
465 }
466 static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
467 Link *link = userdata;
468 int r = 0;
469
470 assert(link);
471 assert(link->network);
472 assert(link->manager);
473
474 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
475 return;
476
477 switch (event) {
478 case SD_DHCP_CLIENT_EVENT_EXPIRED:
479 case SD_DHCP_CLIENT_EVENT_STOP:
480 case SD_DHCP_CLIENT_EVENT_IP_CHANGE:
481 if (link->network->dhcp_critical) {
482 log_link_error(link, "DHCPv4 connection considered system critical, ignoring request to reconfigure it.");
483 return;
484 }
485
486 if (link->dhcp_lease) {
487 r = dhcp_lease_lost(link);
488 if (r < 0) {
489 link_enter_failed(link);
490 return;
491 }
492 }
493
494 if (event == SD_DHCP_CLIENT_EVENT_IP_CHANGE) {
495 r = dhcp_lease_acquired(client, link);
496 if (r < 0) {
497 link_enter_failed(link);
498 return;
499 }
500 }
501
502 break;
503 case SD_DHCP_CLIENT_EVENT_RENEW:
504 r = dhcp_lease_renew(client, link);
505 if (r < 0) {
506 link_enter_failed(link);
507 return;
508 }
509 break;
510 case SD_DHCP_CLIENT_EVENT_IP_ACQUIRE:
511 r = dhcp_lease_acquired(client, link);
512 if (r < 0) {
513 link_enter_failed(link);
514 return;
515 }
516 break;
517 default:
518 if (event < 0)
519 log_link_warning_errno(link, event, "DHCP error: Client failed: %m");
520 else
521 log_link_warning(link, "DHCP unknown event: %i", event);
522 break;
523 }
524
525 return;
526 }
527
528 int dhcp4_configure(Link *link) {
529 int r;
530
531 assert(link);
532 assert(link->network);
533 assert(link->network->dhcp & ADDRESS_FAMILY_IPV4);
534
535 r = sd_dhcp_client_new(&link->dhcp_client);
536 if (r < 0)
537 return r;
538
539 r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
540 if (r < 0)
541 return r;
542
543 r = sd_dhcp_client_set_mac(link->dhcp_client,
544 (const uint8_t *) &link->mac,
545 sizeof (link->mac), ARPHRD_ETHER);
546 if (r < 0)
547 return r;
548
549 r = sd_dhcp_client_set_index(link->dhcp_client, link->ifindex);
550 if (r < 0)
551 return r;
552
553 r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
554 if (r < 0)
555 return r;
556
557 r = sd_dhcp_client_set_request_broadcast(link->dhcp_client,
558 link->network->dhcp_broadcast);
559 if (r < 0)
560 return r;
561
562 if (link->mtu) {
563 r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
564 if (r < 0)
565 return r;
566 }
567
568 if (link->network->dhcp_mtu) {
569 r = sd_dhcp_client_set_request_option(link->dhcp_client,
570 DHCP_OPTION_INTERFACE_MTU);
571 if (r < 0)
572 return r;
573 }
574
575 if (link->network->dhcp_routes) {
576 r = sd_dhcp_client_set_request_option(link->dhcp_client,
577 DHCP_OPTION_STATIC_ROUTE);
578 if (r < 0)
579 return r;
580 r = sd_dhcp_client_set_request_option(link->dhcp_client,
581 DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
582 if (r < 0)
583 return r;
584 }
585
586 /* Always acquire the timezone and NTP*/
587 r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NTP_SERVER);
588 if (r < 0)
589 return r;
590
591 r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NEW_TZDB_TIMEZONE);
592 if (r < 0)
593 return r;
594
595 if (link->network->dhcp_sendhost) {
596 _cleanup_free_ char *hostname = NULL;
597 const char *hn = NULL;
598
599 if (!link->network->hostname) {
600 hostname = gethostname_malloc();
601 if (!hostname)
602 return -ENOMEM;
603
604 hn = hostname;
605 } else
606 hn = link->network->hostname;
607
608 if (!is_localhost(hn)) {
609 r = sd_dhcp_client_set_hostname(link->dhcp_client, hn);
610 if (r < 0)
611 return r;
612 }
613 }
614
615 if (link->network->dhcp_vendor_class_identifier) {
616 r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
617 link->network->dhcp_vendor_class_identifier);
618 if (r < 0)
619 return r;
620 }
621
622 switch (link->network->dhcp_client_identifier) {
623 case DHCP_CLIENT_ID_DUID:
624 /* Library defaults to this. */
625 break;
626 case DHCP_CLIENT_ID_MAC:
627 r = sd_dhcp_client_set_client_id(link->dhcp_client,
628 ARPHRD_ETHER,
629 (const uint8_t *) &link->mac,
630 sizeof (link->mac));
631 if (r < 0)
632 return r;
633 break;
634 default:
635 assert_not_reached("Unknown client identifier type.");
636 }
637
638 return 0;
639 }