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_link_error(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_link_warning(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_link_warning(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_link_warning(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_link_warning(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_link_warning(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_link_error(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_link_warning(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_link_warning(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_link_warning(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_link_error(link, "could not set DHCPv4 address: %s",
298 link_enter_failed(link);
300 link_rtnl_process_address(rtnl, m, link->manager);
302 link_set_dhcp_routes(link);
307 static int dhcp4_update_address(Link *link,
308 struct in_addr *address,
309 struct in_addr *netmask,
311 _cleanup_address_free_ Address *addr = NULL;
319 prefixlen = in_addr_netmask_to_prefixlen(netmask);
321 r = address_new_dynamic(&addr);
325 addr->family = AF_INET;
326 addr->in_addr.in.s_addr = address->s_addr;
327 addr->cinfo.ifa_prefered = lifetime;
328 addr->cinfo.ifa_valid = lifetime;
329 addr->prefixlen = prefixlen;
330 addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
332 /* use update rather than configure so that we will update the
333 * lifetime of an existing address if it has already been configured */
334 r = address_update(addr, link, &dhcp4_address_handler);
341 static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
342 sd_dhcp_lease *lease;
343 struct in_addr address;
344 struct in_addr netmask;
345 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
350 assert(link->network);
352 r = sd_dhcp_client_get_lease(client, &lease);
354 log_link_warning(link, "DHCP error: no lease %s",
359 sd_dhcp_lease_unref(link->dhcp_lease);
360 link->dhcp4_configured = false;
361 link->dhcp_lease = lease;
363 r = sd_dhcp_lease_get_address(lease, &address);
365 log_link_warning(link, "DHCP error: no address: %s",
370 r = sd_dhcp_lease_get_netmask(lease, &netmask);
372 log_link_warning(link, "DHCP error: no netmask: %s",
377 if (!link->network->dhcp_critical) {
378 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease,
381 log_link_warning(link,
382 "DHCP error: no lifetime: %s",
388 r = dhcp4_update_address(link, &address, &netmask, lifetime);
390 log_link_warning(link, "could not update IP address: %s",
392 link_enter_failed(link);
399 static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
400 sd_dhcp_lease *lease;
401 struct in_addr address;
402 struct in_addr netmask;
403 struct in_addr gateway;
405 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
411 r = sd_dhcp_client_get_lease(client, &lease);
413 log_link_warning(link, "DHCP error: no lease: %s",
418 r = sd_dhcp_lease_get_address(lease, &address);
420 log_link_warning(link, "DHCP error: no address: %s",
425 r = sd_dhcp_lease_get_netmask(lease, &netmask);
427 log_link_warning(link, "DHCP error: no netmask: %s",
432 prefixlen = in_addr_netmask_to_prefixlen(&netmask);
434 r = sd_dhcp_lease_get_router(lease, &gateway);
435 if (r < 0 && r != -ENOENT) {
436 log_link_warning(link, "DHCP error: could not get gateway: %s",
442 log_link_struct(link, LOG_INFO,
443 "MESSAGE=%-*s: DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
446 ADDRESS_FMT_VAL(address),
448 ADDRESS_FMT_VAL(gateway),
449 "ADDRESS=%u.%u.%u.%u",
450 ADDRESS_FMT_VAL(address),
453 "GATEWAY=%u.%u.%u.%u",
454 ADDRESS_FMT_VAL(gateway),
457 log_link_struct(link, LOG_INFO,
458 "MESSAGE=%-*s: DHCPv4 address %u.%u.%u.%u/%u",
461 ADDRESS_FMT_VAL(address),
463 "ADDRESS=%u.%u.%u.%u",
464 ADDRESS_FMT_VAL(address),
469 link->dhcp_lease = lease;
471 if (link->network->dhcp_mtu) {
474 r = sd_dhcp_lease_get_mtu(lease, &mtu);
476 r = link_set_mtu(link, mtu);
478 log_link_error(link, "Failed to set MTU "
483 if (link->network->dhcp_hostname) {
484 const char *hostname;
486 r = sd_dhcp_lease_get_hostname(lease, &hostname);
488 r = link_set_hostname(link, hostname);
491 "Failed to set transient hostname to '%s'",
496 if (!link->network->dhcp_critical) {
497 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease,
500 log_link_warning(link,
501 "DHCP error: no lifetime: %s",
507 r = dhcp4_update_address(link, &address, &netmask, lifetime);
509 log_link_warning(link, "could not update IP address: %s",
511 link_enter_failed(link);
517 static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
518 Link *link = userdata;
522 assert(link->network);
523 assert(link->manager);
525 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
529 case DHCP_EVENT_EXPIRED:
530 case DHCP_EVENT_STOP:
531 case DHCP_EVENT_IP_CHANGE:
532 if (link->network->dhcp_critical) {
534 "DHCPv4 connection considered system critical, ignoring request to reconfigure it.");
538 if (link->dhcp_lease) {
539 r = dhcp_lease_lost(link);
541 link_enter_failed(link);
546 if (event == DHCP_EVENT_IP_CHANGE) {
547 r = dhcp_lease_acquired(client, link);
549 link_enter_failed(link);
555 case DHCP_EVENT_RENEW:
556 r = dhcp_lease_renew(client, link);
558 link_enter_failed(link);
562 case DHCP_EVENT_IP_ACQUIRE:
563 r = dhcp_lease_acquired(client, link);
565 link_enter_failed(link);
571 log_link_warning(link,
572 "DHCP error: client failed: %s",
575 log_link_warning(link,
576 "DHCP unknown event: %d",
584 int dhcp4_configure(Link *link) {
588 assert(link->network);
589 assert(IN_SET(link->network->dhcp, DHCP_SUPPORT_BOTH, DHCP_SUPPORT_V4));
591 r = sd_dhcp_client_new(&link->dhcp_client);
595 r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
599 r = sd_dhcp_client_set_mac(link->dhcp_client,
600 (const uint8_t *) &link->mac,
601 sizeof (link->mac), ARPHRD_ETHER);
605 r = sd_dhcp_client_set_index(link->dhcp_client, link->ifindex);
609 r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
613 r = sd_dhcp_client_set_request_broadcast(link->dhcp_client,
614 link->network->dhcp_broadcast);
619 r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
624 if (link->network->dhcp_mtu) {
625 r = sd_dhcp_client_set_request_option(link->dhcp_client,
626 DHCP_OPTION_INTERFACE_MTU);
631 if (link->network->dhcp_routes) {
632 r = sd_dhcp_client_set_request_option(link->dhcp_client,
633 DHCP_OPTION_STATIC_ROUTE);
636 r = sd_dhcp_client_set_request_option(link->dhcp_client,
637 DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
642 if (link->network->dhcp_sendhost) {
643 _cleanup_free_ char *hostname = NULL;
645 hostname = gethostname_malloc();
649 if (!is_localhost(hostname)) {
650 r = sd_dhcp_client_set_hostname(link->dhcp_client,
657 if (link->network->dhcp_vendor_class_identifier) {
658 r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
659 link->network->dhcp_vendor_class_identifier);