networkd-dhcp4.c revision 0bc70f1d9c5453ba614ec0ed041dc30b9cd52071
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering
12b42c76672a66c2d4ea7212c14f8f1b5a62b78dTom Gundersen/***
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering This file is part of systemd.
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering Copyright 2013-2014 Tom Gundersen <teg@jklm.no>
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering systemd is free software; you can redistribute it and/or modify it
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering under the terms of the GNU Lesser General Public License as published by
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering (at your option) any later version.
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering systemd is distributed in the hope that it will be useful, but
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering Lesser General Public License for more details.
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering You should have received a copy of the GNU Lesser General Public License
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering***/
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering#include <netinet/ether.h>
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering#include <linux/if.h>
dfdebb1b925332352966804303b2516a6506a429Zbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek#include "alloc-util.h"
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek#include "dhcp-lease-internal.h"
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek#include "hostname-util.h"
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek#include "network-internal.h"
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek#include "networkd-link.h"
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmekstatic int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek void *userdata) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek _cleanup_link_unref_ Link *link = userdata;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek int r;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek assert(link);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek assert(link->dhcp4_messages);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek link->dhcp4_messages --;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek r = sd_netlink_message_get_errno(m);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (r < 0 && r != -EEXIST) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek log_link_error_errno(link, r, "Could not set DHCPv4 route: %m");
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek link_enter_failed(link);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek }
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (!link->dhcp4_messages) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek link->dhcp4_configured = true;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek link_check_ready(link);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek }
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return 1;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek}
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmekstatic int link_set_dhcp_routes(Link *link) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek struct in_addr gateway;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek struct sd_dhcp_route *static_routes;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek int r, n, i;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek assert(link);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek assert(link->dhcp_lease);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (r < 0 && r != -ENODATA)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m");
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (r >= 0) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek struct in_addr address;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek _cleanup_route_free_ Route *route = NULL;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek _cleanup_route_free_ Route *route_gw = NULL;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (r < 0)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return log_link_warning_errno(link, r, "DHCP error: could not get address: %m");
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek r = route_new(&route);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (r < 0)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return log_link_error_errno(link, r, "Could not allocate route: %m");
f17fd655144d080279b0c14d973b9db169a88976Evgeny Vereshchagin
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route->protocol = RTPROT_DHCP;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek r = route_new(&route_gw);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (r < 0)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return log_link_error_errno(link, r, "Could not allocate route: %m");
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* The dhcp netmask may mask out the gateway. Add an explicit
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * route for the gw host so that we can route no matter the
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * netmask or existing kernel route tables. */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route_gw->family = AF_INET;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route_gw->dst.in = gateway;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route_gw->dst_prefixlen = 32;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route_gw->prefsrc.in = address;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route_gw->scope = RT_SCOPE_LINK;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route_gw->protocol = RTPROT_DHCP;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route_gw->priority = link->network->dhcp_route_metric;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek r = route_configure(route_gw, link, &dhcp4_route_handler);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (r < 0)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return log_link_warning_errno(link, r, "Could not set host route: %m");
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek link->dhcp4_messages ++;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route->family = AF_INET;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route->gw.in = gateway;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route->prefsrc.in = address;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route->priority = link->network->dhcp_route_metric;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek r = route_configure(route, link, &dhcp4_route_handler);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (r < 0) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek log_link_warning_errno(link, r, "Could not set routes: %m");
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek link_enter_failed(link);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return r;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek }
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek link->dhcp4_messages ++;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek }
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (n == -ENODATA)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return 0;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (n < 0)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return log_link_warning_errno(link, n, "DHCP error: could not get routes: %m");
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek for (i = 0; i < n; i++) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek _cleanup_route_free_ Route *route = NULL;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek r = route_new(&route);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (r < 0)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return log_link_error_errno(link, r, "Could not allocate route: %m");
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route->family = AF_INET;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route->protocol = RTPROT_DHCP;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route->gw.in = static_routes[i].gw_addr;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route->dst.in = static_routes[i].dst_addr;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route->dst_prefixlen = static_routes[i].dst_prefixlen;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route->priority = link->network->dhcp_route_metric;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek r = route_configure(route, link, &dhcp4_route_handler);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (r < 0)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return log_link_warning_errno(link, r, "Could not set host route: %m");
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek link->dhcp4_messages ++;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek }
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return 0;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek}
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmekstatic int dhcp_lease_lost(Link *link) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek _cleanup_address_free_ Address *address = NULL;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek struct in_addr addr;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek struct in_addr netmask;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek struct in_addr gateway;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek unsigned prefixlen = 0;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek int r;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek assert(link);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek assert(link->dhcp_lease);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek log_link_warning(link, "DHCP lease lost");
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (link->network->dhcp_routes) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek struct sd_dhcp_route *routes;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek int n, i;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (n >= 0) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek for (i = 0; i < n; i++) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek _cleanup_route_free_ Route *route = NULL;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek r = route_new(&route);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (r >= 0) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route->family = AF_INET;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route->gw.in = routes[i].gw_addr;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route->dst.in = routes[i].dst_addr;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route->dst_prefixlen = routes[i].dst_prefixlen;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route_remove(route, link,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek &link_route_remove_handler);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek }
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek }
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek }
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek }
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek r = address_new(&address);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (r >= 0) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (r >= 0) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek _cleanup_route_free_ Route *route_gw = NULL;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek _cleanup_route_free_ Route *route = NULL;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek r = route_new(&route_gw);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (r >= 0) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route_gw->family = AF_INET;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route_gw->dst.in = gateway;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route_gw->dst_prefixlen = 32;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route_gw->scope = RT_SCOPE_LINK;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek route_remove(route_gw, link,
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering &link_route_remove_handler);
522d4a495af3a615526fccdf038d2d68f41a73c8Lennart Poettering }
r = route_new(&route);
if (r >= 0) {
route->family = AF_INET;
route->gw.in = gateway;
route_remove(route, link,
&link_route_remove_handler);
}
}
r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr);
if (r >= 0) {
r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
if (r >= 0)
prefixlen = in_addr_netmask_to_prefixlen(&netmask);
address->family = AF_INET;
address->in_addr.in = addr;
address->prefixlen = prefixlen;
address_remove(address, link, &link_address_remove_handler);
}
}
if (link->network->dhcp_mtu) {
uint16_t mtu;
r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu);
if (r >= 0 && link->original_mtu != mtu) {
r = link_set_mtu(link, link->original_mtu);
if (r < 0) {
log_link_warning(link,
"DHCP error: could not reset MTU");
link_enter_failed(link);
return r;
}
}
}
if (link->network->dhcp_hostname) {
const char *hostname = NULL;
if (link->network->hostname)
hostname = link->network->hostname;
else
(void) sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
if (hostname) {
/* If a hostname was set due to the lease, then unset it now. */
r = link_set_hostname(link, NULL);
if (r < 0)
log_link_warning_errno(link, r, "Failed to reset transient hostname: %m");
}
}
link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
link->dhcp4_configured = false;
return 0;
}
static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
void *userdata) {
_cleanup_link_unref_ Link *link = userdata;
int r;
assert(link);
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
log_link_error_errno(link, r, "Could not set DHCPv4 address: %m");
link_enter_failed(link);
} else if (r >= 0)
manager_rtnl_process_address(rtnl, m, link->manager);
link_set_dhcp_routes(link);
return 1;
}
static int dhcp4_update_address(Link *link,
struct in_addr *address,
struct in_addr *netmask,
uint32_t lifetime) {
_cleanup_address_free_ Address *addr = NULL;
unsigned prefixlen;
int r;
assert(address);
assert(netmask);
assert(lifetime);
prefixlen = in_addr_netmask_to_prefixlen(netmask);
r = address_new(&addr);
if (r < 0)
return r;
addr->family = AF_INET;
addr->in_addr.in.s_addr = address->s_addr;
addr->cinfo.ifa_prefered = lifetime;
addr->cinfo.ifa_valid = lifetime;
addr->prefixlen = prefixlen;
addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
/* allow reusing an existing address and simply update its lifetime
* in case it already exists */
r = address_configure(addr, link, &dhcp4_address_handler, true);
if (r < 0)
return r;
return 0;
}
static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
sd_dhcp_lease *lease;
struct in_addr address;
struct in_addr netmask;
uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
int r;
assert(link);
assert(client);
assert(link->network);
r = sd_dhcp_client_get_lease(client, &lease);
if (r < 0)
return log_link_warning_errno(link, r, "DHCP error: no lease: %m");
sd_dhcp_lease_unref(link->dhcp_lease);
link->dhcp4_configured = false;
link->dhcp_lease = sd_dhcp_lease_ref(lease);
r = sd_dhcp_lease_get_address(lease, &address);
if (r < 0)
return log_link_warning_errno(link, r, "DHCP error: no address: %m");
r = sd_dhcp_lease_get_netmask(lease, &netmask);
if (r < 0)
return log_link_warning_errno(link, r, "DHCP error: no netmask: %m");
if (!link->network->dhcp_critical) {
r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
if (r < 0)
return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
}
r = dhcp4_update_address(link, &address, &netmask, lifetime);
if (r < 0) {
log_link_warning_errno(link, r, "Could not update IP address: %m");
link_enter_failed(link);
return r;
}
return 0;
}
static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
sd_dhcp_lease *lease;
struct in_addr address;
struct in_addr netmask;
struct in_addr gateway;
unsigned prefixlen;
uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
int r;
assert(client);
assert(link);
r = sd_dhcp_client_get_lease(client, &lease);
if (r < 0)
return log_link_error_errno(link, r, "DHCP error: No lease: %m");
r = sd_dhcp_lease_get_address(lease, &address);
if (r < 0)
return log_link_error_errno(link, r, "DHCP error: No address: %m");
r = sd_dhcp_lease_get_netmask(lease, &netmask);
if (r < 0)
return log_link_error_errno(link, r, "DHCP error: No netmask: %m");
prefixlen = in_addr_netmask_to_prefixlen(&netmask);
r = sd_dhcp_lease_get_router(lease, &gateway);
if (r < 0 && r != -ENODATA)
return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m");
if (r >= 0)
log_struct(LOG_INFO,
LOG_LINK_INTERFACE(link),
LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
ADDRESS_FMT_VAL(address),
prefixlen,
ADDRESS_FMT_VAL(gateway)),
"ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
"PREFIXLEN=%u", prefixlen,
"GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(gateway),
NULL);
else
log_struct(LOG_INFO,
LOG_LINK_INTERFACE(link),
LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u",
ADDRESS_FMT_VAL(address),
prefixlen),
"ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
"PREFIXLEN=%u", prefixlen,
NULL);
link->dhcp_lease = sd_dhcp_lease_ref(lease);
if (link->network->dhcp_mtu) {
uint16_t mtu;
r = sd_dhcp_lease_get_mtu(lease, &mtu);
if (r >= 0) {
r = link_set_mtu(link, mtu);
if (r < 0)
log_link_error_errno(link, r, "Failed to set MTU to %" PRIu16 ": %m", mtu);
}
}
if (link->network->dhcp_hostname) {
const char *hostname = NULL;
if (link->network->hostname)
hostname = link->network->hostname;
else
(void) sd_dhcp_lease_get_hostname(lease, &hostname);
if (hostname) {
r = link_set_hostname(link, hostname);
if (r < 0)
log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname);
}
}
if (link->network->dhcp_timezone) {
const char *tz = NULL;
(void) sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz);
if (tz) {
r = link_set_timezone(link, tz);
if (r < 0)
log_link_error_errno(link, r, "Failed to set timezone to '%s': %m", tz);
}
}
if (!link->network->dhcp_critical) {
r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
if (r < 0) {
log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
return r;
}
}
r = dhcp4_update_address(link, &address, &netmask, lifetime);
if (r < 0) {
log_link_warning_errno(link, r, "Could not update IP address: %m");
link_enter_failed(link);
return r;
}
return 0;
}
static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
Link *link = userdata;
int r = 0;
assert(link);
assert(link->network);
assert(link->manager);
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return;
switch (event) {
case SD_DHCP_CLIENT_EVENT_EXPIRED:
case SD_DHCP_CLIENT_EVENT_STOP:
case SD_DHCP_CLIENT_EVENT_IP_CHANGE:
if (link->network->dhcp_critical) {
log_link_error(link, "DHCPv4 connection considered system critical, ignoring request to reconfigure it.");
return;
}
if (link->dhcp_lease) {
r = dhcp_lease_lost(link);
if (r < 0) {
link_enter_failed(link);
return;
}
}
if (event == SD_DHCP_CLIENT_EVENT_IP_CHANGE) {
r = dhcp_lease_acquired(client, link);
if (r < 0) {
link_enter_failed(link);
return;
}
}
break;
case SD_DHCP_CLIENT_EVENT_RENEW:
r = dhcp_lease_renew(client, link);
if (r < 0) {
link_enter_failed(link);
return;
}
break;
case SD_DHCP_CLIENT_EVENT_IP_ACQUIRE:
r = dhcp_lease_acquired(client, link);
if (r < 0) {
link_enter_failed(link);
return;
}
break;
default:
if (event < 0)
log_link_warning_errno(link, event, "DHCP error: Client failed: %m");
else
log_link_warning(link, "DHCP unknown event: %i", event);
break;
}
return;
}
int dhcp4_configure(Link *link) {
int r;
assert(link);
assert(link->network);
assert(link->network->dhcp & ADDRESS_FAMILY_IPV4);
if (!link->dhcp_client) {
r = sd_dhcp_client_new(&link->dhcp_client);
if (r < 0)
return r;
}
r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
if (r < 0)
return r;
r = sd_dhcp_client_set_mac(link->dhcp_client,
(const uint8_t *) &link->mac,
sizeof (link->mac), ARPHRD_ETHER);
if (r < 0)
return r;
r = sd_dhcp_client_set_index(link->dhcp_client, link->ifindex);
if (r < 0)
return r;
r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
if (r < 0)
return r;
r = sd_dhcp_client_set_request_broadcast(link->dhcp_client,
link->network->dhcp_broadcast);
if (r < 0)
return r;
if (link->mtu) {
r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
if (r < 0)
return r;
}
if (link->network->dhcp_mtu) {
r = sd_dhcp_client_set_request_option(link->dhcp_client,
DHCP_OPTION_INTERFACE_MTU);
if (r < 0)
return r;
}
if (link->network->dhcp_routes) {
r = sd_dhcp_client_set_request_option(link->dhcp_client,
DHCP_OPTION_STATIC_ROUTE);
if (r < 0)
return r;
r = sd_dhcp_client_set_request_option(link->dhcp_client,
DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
if (r < 0)
return r;
}
/* Always acquire the timezone and NTP*/
r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NTP_SERVER);
if (r < 0)
return r;
r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NEW_TZDB_TIMEZONE);
if (r < 0)
return r;
if (link->network->dhcp_sendhost) {
_cleanup_free_ char *hostname = NULL;
const char *hn = NULL;
if (!link->network->hostname) {
hostname = gethostname_malloc();
if (!hostname)
return -ENOMEM;
hn = hostname;
} else
hn = link->network->hostname;
if (!is_localhost(hn)) {
r = sd_dhcp_client_set_hostname(link->dhcp_client, hn);
if (r < 0)
return r;
}
}
if (link->network->dhcp_vendor_class_identifier) {
r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
link->network->dhcp_vendor_class_identifier);
if (r < 0)
return r;
}
switch (link->network->dhcp_client_identifier) {
case DHCP_CLIENT_ID_DUID:
/* Library defaults to this. */
break;
case DHCP_CLIENT_ID_MAC:
r = sd_dhcp_client_set_client_id(link->dhcp_client,
ARPHRD_ETHER,
(const uint8_t *) &link->mac,
sizeof (link->mac));
if (r < 0)
return r;
break;
default:
assert_not_reached("Unknown client identifier type.");
}
return 0;
}