1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013-2014 Tom Gundersen <teg@jklm.no>
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.
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.
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/>.
22 #include <netinet/ether.h>
25 #include "networkd-link.h"
26 #include "network-internal.h"
27 #include "dhcp-lease-internal.h"
29 static int dhcp4_route_handler(sd_rtnl *rtnl, sd_rtnl_message *m,
31 _cleanup_link_unref_ Link *link = userdata;
35 assert(link->dhcp4_messages);
37 link->dhcp4_messages --;
39 r = sd_rtnl_message_get_errno(m);
40 if (r < 0 && r != -EEXIST) {
41 log_error_link(link, "could not set DHCPv4 route: %s",
43 link_enter_failed(link);
46 if (!link->dhcp4_messages) {
47 link->dhcp4_configured = true;
48 link_client_handler(link);
54 static int link_set_dhcp_routes(Link *link) {
55 struct in_addr gateway;
56 struct sd_dhcp_route *static_routes;
60 assert(link->dhcp_lease);
62 r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
63 if (r < 0 && r != -ENOENT) {
64 log_warning_link(link,
65 "DHCP error: could not get gateway: %s",
70 struct in_addr address;
71 _cleanup_route_free_ Route *route = NULL;
72 _cleanup_route_free_ Route *route_gw = NULL;
74 r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
76 log_warning_link(link,
77 "DHCP error: could not get address: %s",
82 r = route_new_dynamic(&route, RTPROT_DHCP);
85 "Could not allocate route: %s",
90 r = route_new_dynamic(&route_gw, RTPROT_DHCP);
93 "Could not allocate route: %s",
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;
104 route_gw->prefsrc_addr.in = address;
105 route_gw->scope = RT_SCOPE_LINK;
106 route_gw->metrics = link->network->dhcp_route_metric;
108 r = route_configure(route_gw, link, &dhcp4_route_handler);
110 log_warning_link(link,
111 "could not set host route: %s",
116 link->dhcp4_messages ++;
118 route->family = AF_INET;
119 route->in_addr.in = gateway;
120 route->prefsrc_addr.in = address;
121 route->metrics = link->network->dhcp_route_metric;
123 r = route_configure(route, link, &dhcp4_route_handler);
125 log_warning_link(link,
126 "could not set routes: %s",
128 link_enter_failed(link);
132 link->dhcp4_messages ++;
135 n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
139 log_warning_link(link,
140 "DHCP error: could not get routes: %s",
146 for (i = 0; i < n; i++) {
147 _cleanup_route_free_ Route *route = NULL;
149 r = route_new_dynamic(&route, RTPROT_DHCP);
151 log_error_link(link, "Could not allocate route: %s",
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;
160 route->metrics = link->network->dhcp_route_metric;
162 r = route_configure(route, link, &dhcp4_route_handler);
164 log_warning_link(link,
165 "could not set host route: %s",
170 link->dhcp4_messages ++;
176 static int dhcp_lease_lost(Link *link) {
177 _cleanup_address_free_ Address *address = NULL;
179 struct in_addr netmask;
180 struct in_addr gateway;
181 unsigned prefixlen = 0;
185 assert(link->dhcp_lease);
187 log_warning_link(link, "DHCP lease lost");
189 if (link->network->dhcp_routes) {
190 struct sd_dhcp_route *routes;
193 n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes);
195 for (i = 0; i < n; i++) {
196 _cleanup_route_free_ Route *route = NULL;
198 r = route_new_dynamic(&route, RTPROT_UNSPEC);
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;
205 route_drop(route, link,
206 &link_route_drop_handler);
212 r = address_new_dynamic(&address);
214 r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
216 _cleanup_route_free_ Route *route_gw = NULL;
217 _cleanup_route_free_ Route *route = NULL;
219 r = route_new_dynamic(&route_gw, RTPROT_UNSPEC);
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;
226 route_drop(route_gw, link,
227 &link_route_drop_handler);
230 r = route_new_dynamic(&route, RTPROT_UNSPEC);
232 route->family = AF_INET;
233 route->in_addr.in = gateway;
235 route_drop(route, link,
236 &link_route_drop_handler);
240 r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr);
242 r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
244 prefixlen = in_addr_netmask_to_prefixlen(&netmask);
246 address->family = AF_INET;
247 address->in_addr.in = addr;
248 address->prefixlen = prefixlen;
250 address_drop(address, link, &link_address_drop_handler);
254 if (link->network->dhcp_mtu) {
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);
261 log_warning_link(link,
262 "DHCP error: could not reset MTU");
263 link_enter_failed(link);
269 if (link->network->dhcp_hostname) {
270 const char *hostname = NULL;
272 r = sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
273 if (r >= 0 && hostname) {
274 r = link_set_hostname(link, "");
277 "Failed to reset transient hostname");
281 link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
282 link->dhcp4_configured = false;
287 static int dhcp4_address_handler(sd_rtnl *rtnl, sd_rtnl_message *m,
289 _cleanup_link_unref_ Link *link = userdata;
294 r = sd_rtnl_message_get_errno(m);
295 if (r < 0 && r != -EEXIST) {
296 log_error_link(link, "could not set DHCPv4 address: %s",
298 link_enter_failed(link);
300 /* calling handler directly so take a ref */
302 link_get_address_handler(rtnl, m, link);
305 link_set_dhcp_routes(link);
310 static int dhcp4_update_address(Link *link,
311 struct in_addr *address,
312 struct in_addr *netmask,
314 _cleanup_address_free_ Address *addr = NULL;
322 prefixlen = in_addr_netmask_to_prefixlen(netmask);
324 r = address_new_dynamic(&addr);
328 addr->family = AF_INET;
329 addr->in_addr.in.s_addr = address->s_addr;
330 addr->cinfo.ifa_prefered = lifetime;
331 addr->cinfo.ifa_valid = lifetime;
332 addr->prefixlen = prefixlen;
333 addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
335 /* use update rather than configure so that we will update the
336 * lifetime of an existing address if it has already been configured */
337 r = address_update(addr, link, &dhcp4_address_handler);
344 static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
345 sd_dhcp_lease *lease;
346 struct in_addr address;
347 struct in_addr netmask;
348 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
353 assert(link->network);
355 r = sd_dhcp_client_get_lease(client, &lease);
357 log_warning_link(link, "DHCP error: no lease %s",
362 sd_dhcp_lease_unref(link->dhcp_lease);
363 link->dhcp4_configured = false;
364 link->dhcp_lease = lease;
366 r = sd_dhcp_lease_get_address(lease, &address);
368 log_warning_link(link, "DHCP error: no address: %s",
373 r = sd_dhcp_lease_get_netmask(lease, &netmask);
375 log_warning_link(link, "DHCP error: no netmask: %s",
380 if (!link->network->dhcp_critical) {
381 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease,
384 log_warning_link(link,
385 "DHCP error: no lifetime: %s",
391 r = dhcp4_update_address(link, &address, &netmask, lifetime);
393 log_warning_link(link, "could not update IP address: %s",
395 link_enter_failed(link);
402 static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
403 sd_dhcp_lease *lease;
404 struct in_addr address;
405 struct in_addr netmask;
406 struct in_addr gateway;
408 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
414 r = sd_dhcp_client_get_lease(client, &lease);
416 log_warning_link(link, "DHCP error: no lease: %s",
421 r = sd_dhcp_lease_get_address(lease, &address);
423 log_warning_link(link, "DHCP error: no address: %s",
428 r = sd_dhcp_lease_get_netmask(lease, &netmask);
430 log_warning_link(link, "DHCP error: no netmask: %s",
435 prefixlen = in_addr_netmask_to_prefixlen(&netmask);
437 r = sd_dhcp_lease_get_router(lease, &gateway);
438 if (r < 0 && r != -ENOENT) {
439 log_warning_link(link, "DHCP error: could not get gateway: %s",
445 log_struct_link(LOG_INFO, link,
446 "MESSAGE=%-*s: DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
449 ADDRESS_FMT_VAL(address),
451 ADDRESS_FMT_VAL(gateway),
452 "ADDRESS=%u.%u.%u.%u",
453 ADDRESS_FMT_VAL(address),
456 "GATEWAY=%u.%u.%u.%u",
457 ADDRESS_FMT_VAL(gateway),
460 log_struct_link(LOG_INFO, link,
461 "MESSAGE=%-*s: DHCPv4 address %u.%u.%u.%u/%u",
464 ADDRESS_FMT_VAL(address),
466 "ADDRESS=%u.%u.%u.%u",
467 ADDRESS_FMT_VAL(address),
472 link->dhcp_lease = lease;
474 if (link->network->dhcp_mtu) {
477 r = sd_dhcp_lease_get_mtu(lease, &mtu);
479 r = link_set_mtu(link, mtu);
481 log_error_link(link, "Failed to set MTU "
486 if (link->network->dhcp_hostname) {
487 const char *hostname;
489 r = sd_dhcp_lease_get_hostname(lease, &hostname);
491 r = link_set_hostname(link, hostname);
494 "Failed to set transient hostname to '%s'",
499 if (!link->network->dhcp_critical) {
500 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease,
503 log_warning_link(link,
504 "DHCP error: no lifetime: %s",
510 r = dhcp4_update_address(link, &address, &netmask, lifetime);
512 log_warning_link(link, "could not update IP address: %s",
514 link_enter_failed(link);
520 static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
521 Link *link = userdata;
525 assert(link->network);
526 assert(link->manager);
528 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
532 case DHCP_EVENT_EXPIRED:
533 case DHCP_EVENT_STOP:
534 case DHCP_EVENT_IP_CHANGE:
535 if (link->network->dhcp_critical) {
537 "DHCPv4 connection considered system critical, ignoring request to reconfigure it.");
541 if (link->dhcp_lease) {
542 r = dhcp_lease_lost(link);
544 link_enter_failed(link);
549 if (event == DHCP_EVENT_IP_CHANGE) {
550 r = dhcp_lease_acquired(client, link);
552 link_enter_failed(link);
558 case DHCP_EVENT_RENEW:
559 r = dhcp_lease_renew(client, link);
561 link_enter_failed(link);
565 case DHCP_EVENT_IP_ACQUIRE:
566 r = dhcp_lease_acquired(client, link);
568 link_enter_failed(link);
574 log_warning_link(link,
575 "DHCP error: client failed: %s",
578 log_warning_link(link,
579 "DHCP unknown event: %d",
587 int dhcp4_configure(Link *link) {
591 assert(link->network);
592 assert(IN_SET(link->network->dhcp, DHCP_SUPPORT_BOTH, DHCP_SUPPORT_V4));
594 r = sd_dhcp_client_new(&link->dhcp_client);
598 r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
602 r = sd_dhcp_client_set_mac(link->dhcp_client, &link->mac);
606 r = sd_dhcp_client_set_index(link->dhcp_client, link->ifindex);
610 r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
614 r = sd_dhcp_client_set_request_broadcast(link->dhcp_client,
615 link->network->dhcp_broadcast);
620 r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
625 if (link->network->dhcp_mtu) {
626 r = sd_dhcp_client_set_request_option(link->dhcp_client,
627 DHCP_OPTION_INTERFACE_MTU);
632 if (link->network->dhcp_routes) {
633 r = sd_dhcp_client_set_request_option(link->dhcp_client,
634 DHCP_OPTION_STATIC_ROUTE);
637 r = sd_dhcp_client_set_request_option(link->dhcp_client,
638 DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
643 if (link->network->dhcp_sendhost) {
644 _cleanup_free_ char *hostname = NULL;
646 hostname = gethostname_malloc();
650 if (!is_localhost(hostname)) {
651 r = sd_dhcp_client_set_hostname(link->dhcp_client,
658 if (link->network->dhcp_vendor_class_identifier) {
659 r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
660 link->network->dhcp_vendor_class_identifier);