networkd-dhcp4.c revision 4b7b5abb785c03cb880e3a8a7a724d7de6e9cf3b
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering This file is part of systemd.
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering Copyright 2013-2014 Tom Gundersen <teg@jklm.no>
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering systemd is free software; you can redistribute it and/or modify it
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering under the terms of the GNU Lesser General Public License as published by
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering (at your option) any later version.
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering systemd is distributed in the hope that it will be useful, but
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering Lesser General Public License for more details.
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering You should have received a copy of the GNU Lesser General Public License
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poetteringstatic int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering _cleanup_link_unref_ Link *link = userdata;
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering if (r < 0 && r != -EEXIST) {
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering log_link_error(link, "could not set DHCPv4 route: %s",
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poetteringstatic int link_set_dhcp_routes(Link *link) {
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering if (r < 0 && r != -ENOENT) {
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering "DHCP error: could not get gateway: %s",
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering _cleanup_route_free_ Route *route = NULL;
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering _cleanup_route_free_ Route *route_gw = NULL;
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering "DHCP error: could not get address: %s",
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = route_new_dynamic(&route, RTPROT_DHCP);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering "Could not allocate route: %s",
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering r = route_new_dynamic(&route_gw, RTPROT_DHCP);
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering "Could not allocate route: %s",
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering /* The dhcp netmask may mask out the gateway. Add an explicit
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering * route for the gw host so that we can route no matter the
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering * netmask or existing kernel route tables. */
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering route_gw->metrics = link->network->dhcp_route_metric;
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering r = route_configure(route_gw, link, &dhcp4_route_handler);
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering "could not set host route: %s",
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering route->metrics = link->network->dhcp_route_metric;
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering r = route_configure(route, link, &dhcp4_route_handler);
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering "could not set routes: %s",
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering "DHCP error: could not get routes: %s",
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering for (i = 0; i < n; i++) {
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering _cleanup_route_free_ Route *route = NULL;
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering r = route_new_dynamic(&route, RTPROT_DHCP);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering log_link_error(link, "Could not allocate route: %s",
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering route->in_addr.in = static_routes[i].gw_addr;
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering route->dst_addr.in = static_routes[i].dst_addr;
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering route->dst_prefixlen = static_routes[i].dst_prefixlen;
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering route->metrics = link->network->dhcp_route_metric;
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = route_configure(route, link, &dhcp4_route_handler);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering "could not set host route: %s",
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering _cleanup_address_free_ Address *address = NULL;
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering log_link_warning(link, "DHCP lease lost");
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes);
df3fb561b2df486a495a5f0bcc83168bd1860533Lennart Poettering for (i = 0; i < n; i++) {
df3fb561b2df486a495a5f0bcc83168bd1860533Lennart Poettering _cleanup_route_free_ Route *route = NULL;
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = route_new_dynamic(&route, RTPROT_UNSPEC);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering route->dst_prefixlen = routes[i].dst_prefixlen;
df3fb561b2df486a495a5f0bcc83168bd1860533Lennart Poettering r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering _cleanup_route_free_ Route *route_gw = NULL;
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering _cleanup_route_free_ Route *route = NULL;
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering r = route_new_dynamic(&route_gw, RTPROT_UNSPEC);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = route_new_dynamic(&route, RTPROT_UNSPEC);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering prefixlen = in_addr_netmask_to_prefixlen(&netmask);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering address_drop(address, link, &link_address_drop_handler);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering if (r >= 0 && link->original_mtu != mtu) {
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering r = link_set_mtu(link, link->original_mtu);
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering "DHCP error: could not reset MTU");
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering r = sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering if (r >= 0 || hostname) {
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering "Failed to set transient hostname to '%s': %m",
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poetteringstatic int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering _cleanup_link_unref_ Link *link = userdata;
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering if (r < 0 && r != -EEXIST) {
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering log_link_error(link, "could not set DHCPv4 address: %s",
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering } else if (r >= 0)
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering link_rtnl_process_address(rtnl, m, link->manager);
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poetteringstatic int dhcp4_update_address(Link *link,
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering _cleanup_address_free_ Address *addr = NULL;
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering prefixlen = in_addr_netmask_to_prefixlen(netmask);
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering addr->in_addr.in.s_addr = address->s_addr;
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering /* use update rather than configure so that we will update the
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering * lifetime of an existing address if it has already been configured */
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering r = address_update(addr, link, &dhcp4_address_handler);
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poetteringstatic int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
df3fb561b2df486a495a5f0bcc83168bd1860533Lennart Poettering uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
df3fb561b2df486a495a5f0bcc83168bd1860533Lennart Poettering r = sd_dhcp_client_get_lease(client, &lease);
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering log_link_warning(link, "DHCP error: no lease %s",
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering link->dhcp_lease = sd_dhcp_lease_ref(lease);
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering r = sd_dhcp_lease_get_address(lease, &address);
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering log_link_warning(link, "DHCP error: no address: %s",
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering r = sd_dhcp_lease_get_netmask(lease, &netmask);
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering log_link_warning(link, "DHCP error: no netmask: %s",
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering r = sd_dhcp_lease_get_lifetime(link->dhcp_lease,
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering "DHCP error: no lifetime: %s",
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = dhcp4_update_address(link, &address, &netmask, lifetime);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering log_link_warning(link, "could not update IP address: %s",
03cc0fd1431b82e59c11ae12a274c1f2df23169dLennart Poetteringstatic int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
03cc0fd1431b82e59c11ae12a274c1f2df23169dLennart Poettering uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_client_get_lease(client, &lease);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering return log_link_error_errno(link, r, "DHCP error: no lease: %m");
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_lease_get_address(lease, &address);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering return log_link_error_errno(link, r, "DHCP error: no address: %m");
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_lease_get_netmask(lease, &netmask);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering return log_link_error_errno(link, r, "DHCP error: no netmask: %m");
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering prefixlen = in_addr_netmask_to_prefixlen(&netmask);
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering r = sd_dhcp_lease_get_router(lease, &gateway);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering if (r < 0 && r != -ENOENT)
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering return log_link_error_errno(link, r, "DHCP error: could not get gateway: %m");
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(gateway),
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u",
6d0c65ffb4f82e8c6dceb453919b3db54343fc27Lennart Poettering "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering link->dhcp_lease = sd_dhcp_lease_ref(lease);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering log_link_error_errno(link, r, "Failed to set MTU to %" PRIu16 ": %m", mtu);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_lease_get_hostname(lease, &hostname);
9085f64a6694f2928c79fcce365edb1dca6937d4Lennart Poettering if (r >= 0 || hostname) {
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = dhcp4_update_address(link, &address, &netmask, lifetime);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering log_link_warning_errno(link, r, "Could not update IP address: %m");
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poetteringstatic void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering "DHCPv4 connection considered system critical, ignoring request to reconfigure it.");
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering "DHCP error: client failed: %s",
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering "DHCP unknown event: %d",
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering assert(link->network->dhcp & ADDRESS_FAMILY_IPV4);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_client_new(&link->dhcp_client);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_client_set_mac(link->dhcp_client,
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_client_set_index(link->dhcp_client, link->ifindex);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_client_set_request_broadcast(link->dhcp_client,
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_client_set_request_option(link->dhcp_client,
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_client_set_request_option(link->dhcp_client,
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_client_set_request_option(link->dhcp_client,
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering /* Always acquire the timezone and NTP*/
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NTP_SERVER);
ee8c45689526ca973407cbb77bce7b96a062c40bLennart Poettering r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NEW_TZDB_TIMEZONE);
if (!hostname)
return -ENOMEM;
case DHCP_CLIENT_ID_DUID:
case DHCP_CLIENT_ID_MAC: