]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp4.c
dhcp: make DHCP_OPTION_* enum public
[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 > 0);
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 == 0) {
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 _cleanup_free_ sd_dhcp_route **static_routes = NULL;
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 assert_se(sd_dhcp_route_get_gateway(static_routes[i], &route->gw.in) >= 0);
134 assert_se(sd_dhcp_route_get_destination(static_routes[i], &route->dst.in) >= 0);
135 assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0);
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 _cleanup_free_ sd_dhcp_route **routes = NULL;
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 assert_se(sd_dhcp_route_get_gateway(routes[i], &route->gw.in) >= 0);
174 assert_se(sd_dhcp_route_get_destination(routes[i], &route->dst.in) >= 0);
175 assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen) >= 0);
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_dirty(link);
259 link->dhcp4_configured = false;
260
261 return 0;
262 }
263
264 static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
265 void *userdata) {
266 _cleanup_link_unref_ Link *link = userdata;
267 int r;
268
269 assert(link);
270
271 r = sd_netlink_message_get_errno(m);
272 if (r < 0 && r != -EEXIST) {
273 log_link_error_errno(link, r, "Could not set DHCPv4 address: %m");
274 link_enter_failed(link);
275 } else if (r >= 0)
276 manager_rtnl_process_address(rtnl, m, link->manager);
277
278 link_set_dhcp_routes(link);
279
280 return 1;
281 }
282
283 static int dhcp4_update_address(Link *link,
284 struct in_addr *address,
285 struct in_addr *netmask,
286 uint32_t lifetime) {
287 _cleanup_address_free_ Address *addr = NULL;
288 unsigned prefixlen;
289 int r;
290
291 assert(address);
292 assert(netmask);
293 assert(lifetime);
294
295 prefixlen = in_addr_netmask_to_prefixlen(netmask);
296
297 r = address_new(&addr);
298 if (r < 0)
299 return r;
300
301 addr->family = AF_INET;
302 addr->in_addr.in.s_addr = address->s_addr;
303 addr->cinfo.ifa_prefered = lifetime;
304 addr->cinfo.ifa_valid = lifetime;
305 addr->prefixlen = prefixlen;
306 addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
307
308 /* allow reusing an existing address and simply update its lifetime
309 * in case it already exists */
310 r = address_configure(addr, link, &dhcp4_address_handler, true);
311 if (r < 0)
312 return r;
313
314 return 0;
315 }
316
317 static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
318 sd_dhcp_lease *lease;
319 struct in_addr address;
320 struct in_addr netmask;
321 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
322 int r;
323
324 assert(link);
325 assert(client);
326 assert(link->network);
327
328 r = sd_dhcp_client_get_lease(client, &lease);
329 if (r < 0)
330 return log_link_warning_errno(link, r, "DHCP error: no lease: %m");
331
332 sd_dhcp_lease_unref(link->dhcp_lease);
333 link->dhcp4_configured = false;
334 link->dhcp_lease = sd_dhcp_lease_ref(lease);
335 link_dirty(link);
336
337 r = sd_dhcp_lease_get_address(lease, &address);
338 if (r < 0)
339 return log_link_warning_errno(link, r, "DHCP error: no address: %m");
340
341 r = sd_dhcp_lease_get_netmask(lease, &netmask);
342 if (r < 0)
343 return log_link_warning_errno(link, r, "DHCP error: no netmask: %m");
344
345 if (!link->network->dhcp_critical) {
346 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
347 if (r < 0)
348 return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
349 }
350
351 r = dhcp4_update_address(link, &address, &netmask, lifetime);
352 if (r < 0) {
353 log_link_warning_errno(link, r, "Could not update IP address: %m");
354 link_enter_failed(link);
355 return r;
356 }
357
358 return 0;
359 }
360
361 static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
362 sd_dhcp_lease *lease;
363 struct in_addr address;
364 struct in_addr netmask;
365 struct in_addr gateway;
366 unsigned prefixlen;
367 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
368 int r;
369
370 assert(client);
371 assert(link);
372
373 r = sd_dhcp_client_get_lease(client, &lease);
374 if (r < 0)
375 return log_link_error_errno(link, r, "DHCP error: No lease: %m");
376
377 r = sd_dhcp_lease_get_address(lease, &address);
378 if (r < 0)
379 return log_link_error_errno(link, r, "DHCP error: No address: %m");
380
381 r = sd_dhcp_lease_get_netmask(lease, &netmask);
382 if (r < 0)
383 return log_link_error_errno(link, r, "DHCP error: No netmask: %m");
384
385 prefixlen = in_addr_netmask_to_prefixlen(&netmask);
386
387 r = sd_dhcp_lease_get_router(lease, &gateway);
388 if (r < 0 && r != -ENODATA)
389 return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m");
390
391 if (r >= 0)
392 log_struct(LOG_INFO,
393 LOG_LINK_INTERFACE(link),
394 LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
395 ADDRESS_FMT_VAL(address),
396 prefixlen,
397 ADDRESS_FMT_VAL(gateway)),
398 "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
399 "PREFIXLEN=%u", prefixlen,
400 "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(gateway),
401 NULL);
402 else
403 log_struct(LOG_INFO,
404 LOG_LINK_INTERFACE(link),
405 LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u",
406 ADDRESS_FMT_VAL(address),
407 prefixlen),
408 "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
409 "PREFIXLEN=%u", prefixlen,
410 NULL);
411
412 link->dhcp_lease = sd_dhcp_lease_ref(lease);
413 link_dirty(link);
414
415 if (link->network->dhcp_mtu) {
416 uint16_t mtu;
417
418 r = sd_dhcp_lease_get_mtu(lease, &mtu);
419 if (r >= 0) {
420 r = link_set_mtu(link, mtu);
421 if (r < 0)
422 log_link_error_errno(link, r, "Failed to set MTU to %" PRIu16 ": %m", mtu);
423 }
424 }
425
426 if (link->network->dhcp_hostname) {
427 const char *hostname = NULL;
428
429 if (link->network->hostname)
430 hostname = link->network->hostname;
431 else
432 (void) sd_dhcp_lease_get_hostname(lease, &hostname);
433
434 if (hostname) {
435 r = link_set_hostname(link, hostname);
436 if (r < 0)
437 log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname);
438 }
439 }
440
441 if (link->network->dhcp_timezone) {
442 const char *tz = NULL;
443
444 (void) sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz);
445
446 if (tz) {
447 r = link_set_timezone(link, tz);
448 if (r < 0)
449 log_link_error_errno(link, r, "Failed to set timezone to '%s': %m", tz);
450 }
451 }
452
453 if (!link->network->dhcp_critical) {
454 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
455 if (r < 0) {
456 log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
457 return r;
458 }
459 }
460
461 r = dhcp4_update_address(link, &address, &netmask, lifetime);
462 if (r < 0) {
463 log_link_warning_errno(link, r, "Could not update IP address: %m");
464 link_enter_failed(link);
465 return r;
466 }
467
468 return 0;
469 }
470 static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
471 Link *link = userdata;
472 int r = 0;
473
474 assert(link);
475 assert(link->network);
476 assert(link->manager);
477
478 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
479 return;
480
481 switch (event) {
482 case SD_DHCP_CLIENT_EVENT_EXPIRED:
483 case SD_DHCP_CLIENT_EVENT_STOP:
484 case SD_DHCP_CLIENT_EVENT_IP_CHANGE:
485 if (link->network->dhcp_critical) {
486 log_link_error(link, "DHCPv4 connection considered system critical, ignoring request to reconfigure it.");
487 return;
488 }
489
490 if (link->dhcp_lease) {
491 r = dhcp_lease_lost(link);
492 if (r < 0) {
493 link_enter_failed(link);
494 return;
495 }
496 }
497
498 if (event == SD_DHCP_CLIENT_EVENT_IP_CHANGE) {
499 r = dhcp_lease_acquired(client, link);
500 if (r < 0) {
501 link_enter_failed(link);
502 return;
503 }
504 }
505
506 break;
507 case SD_DHCP_CLIENT_EVENT_RENEW:
508 r = dhcp_lease_renew(client, link);
509 if (r < 0) {
510 link_enter_failed(link);
511 return;
512 }
513 break;
514 case SD_DHCP_CLIENT_EVENT_IP_ACQUIRE:
515 r = dhcp_lease_acquired(client, link);
516 if (r < 0) {
517 link_enter_failed(link);
518 return;
519 }
520 break;
521 default:
522 if (event < 0)
523 log_link_warning_errno(link, event, "DHCP error: Client failed: %m");
524 else
525 log_link_warning(link, "DHCP unknown event: %i", event);
526 break;
527 }
528
529 return;
530 }
531
532 int dhcp4_configure(Link *link) {
533 int r;
534
535 assert(link);
536 assert(link->network);
537 assert(link->network->dhcp & ADDRESS_FAMILY_IPV4);
538
539 if (!link->dhcp_client) {
540 r = sd_dhcp_client_new(&link->dhcp_client);
541 if (r < 0)
542 return r;
543 }
544
545 r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
546 if (r < 0)
547 return r;
548
549 r = sd_dhcp_client_set_mac(link->dhcp_client,
550 (const uint8_t *) &link->mac,
551 sizeof (link->mac), ARPHRD_ETHER);
552 if (r < 0)
553 return r;
554
555 r = sd_dhcp_client_set_index(link->dhcp_client, link->ifindex);
556 if (r < 0)
557 return r;
558
559 r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
560 if (r < 0)
561 return r;
562
563 r = sd_dhcp_client_set_request_broadcast(link->dhcp_client,
564 link->network->dhcp_broadcast);
565 if (r < 0)
566 return r;
567
568 if (link->mtu) {
569 r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
570 if (r < 0)
571 return r;
572 }
573
574 if (link->network->dhcp_mtu) {
575 r = sd_dhcp_client_set_request_option(link->dhcp_client,
576 SD_DHCP_OPTION_INTERFACE_MTU);
577 if (r < 0)
578 return r;
579 }
580
581 if (link->network->dhcp_routes) {
582 r = sd_dhcp_client_set_request_option(link->dhcp_client,
583 SD_DHCP_OPTION_STATIC_ROUTE);
584 if (r < 0)
585 return r;
586 r = sd_dhcp_client_set_request_option(link->dhcp_client,
587 SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
588 if (r < 0)
589 return r;
590 }
591
592 /* Always acquire the timezone and NTP*/
593 r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NTP_SERVER);
594 if (r < 0)
595 return r;
596
597 r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE);
598 if (r < 0)
599 return r;
600
601 if (link->network->dhcp_sendhost) {
602 _cleanup_free_ char *hostname = NULL;
603 const char *hn = NULL;
604
605 if (!link->network->hostname) {
606 hostname = gethostname_malloc();
607 if (!hostname)
608 return -ENOMEM;
609
610 hn = hostname;
611 } else
612 hn = link->network->hostname;
613
614 if (!is_localhost(hn)) {
615 r = sd_dhcp_client_set_hostname(link->dhcp_client, hn);
616 if (r < 0)
617 return r;
618 }
619 }
620
621 if (link->network->dhcp_vendor_class_identifier) {
622 r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
623 link->network->dhcp_vendor_class_identifier);
624 if (r < 0)
625 return r;
626 }
627
628 switch (link->network->dhcp_client_identifier) {
629 case DHCP_CLIENT_ID_DUID:
630 /* Library defaults to this. */
631 break;
632 case DHCP_CLIENT_ID_MAC:
633 r = sd_dhcp_client_set_client_id(link->dhcp_client,
634 ARPHRD_ETHER,
635 (const uint8_t *) &link->mac,
636 sizeof (link->mac));
637 if (r < 0)
638 return r;
639 break;
640 default:
641 assert_not_reached("Unknown client identifier type.");
642 }
643
644 return 0;
645 }