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