networkd-dhcp4.c revision 6666907869fb3bc7fe6a6025540db5b887c7a78b
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering This file is part of systemd.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Copyright 2013-2014 Tom Gundersen <teg@jklm.no>
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering systemd is free software; you can redistribute it and/or modify it
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering under the terms of the GNU Lesser General Public License as published by
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering (at your option) any later version.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering systemd is distributed in the hope that it will be useful, but
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Lesser General Public License for more details.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering You should have received a copy of the GNU Lesser General Public License
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering _cleanup_link_unref_ Link *link = userdata;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (r < 0 && r != -EEXIST) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_link_error_errno(link, r, "Could not set DHCPv4 route: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int link_set_dhcp_routes(Link *link) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (r < 0 && r != -ENODATA)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering _cleanup_route_free_ Route *route = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering _cleanup_route_free_ Route *route_gw = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return log_link_warning_errno(link, r, "DHCP error: could not get address: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return log_link_error_errno(link, r, "Could not allocate route: %m");
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering return log_link_error_errno(link, r, "Could not allocate route: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* The dhcp netmask may mask out the gateway. Add an explicit
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering * route for the gw host so that we can route no matter the
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * netmask or existing kernel route tables. */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering route_gw->metrics = link->network->dhcp_route_metric;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = route_configure(route_gw, link, &dhcp4_route_handler);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return log_link_warning_errno(link, r, "Could not set host route: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering route->metrics = link->network->dhcp_route_metric;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = route_configure(route, link, &dhcp4_route_handler);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_link_warning_errno(link, r, "Could not set routes: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return log_link_warning_errno(link, n, "DHCP error: could not get routes: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering for (i = 0; i < n; i++) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering _cleanup_route_free_ Route *route = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return log_link_error_errno(link, r, "Could not allocate route: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering route->in_addr.in = static_routes[i].gw_addr;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering route->dst_addr.in = static_routes[i].dst_addr;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering route->dst_prefixlen = static_routes[i].dst_prefixlen;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering route->metrics = link->network->dhcp_route_metric;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = route_configure(route, link, &dhcp4_route_handler);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return log_link_warning_errno(link, r, "Could not set host route: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering _cleanup_address_free_ Address *address = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_link_warning(link, "DHCP lease lost");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering for (i = 0; i < n; i++) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering _cleanup_route_free_ Route *route = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering route->dst_prefixlen = routes[i].dst_prefixlen;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering _cleanup_route_free_ Route *route_gw = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering _cleanup_route_free_ Route *route = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering prefixlen = in_addr_netmask_to_prefixlen(&netmask);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering address_remove(address, link, &link_address_remove_handler);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (r >= 0 && link->original_mtu != mtu) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = link_set_mtu(link, link->original_mtu);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering "DHCP error: could not reset MTU");
e88baee88fad8bc59d33b55a7a2d640ef9e16cd6Zbigniew Jędrzejewski-Szmek if (link->network->dhcp_hostname) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering (void) sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering /* If a hostname was set due to the lease, then unset it now. */
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering log_link_warning_errno(link, r, "Failed to reset transient hostname: %m");
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poetteringstatic int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering _cleanup_link_unref_ Link *link = userdata;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (r < 0 && r != -EEXIST) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering log_link_error_errno(link, r, "Could not set DHCPv4 address: %m");
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering } else if (r >= 0)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering manager_rtnl_process_address(rtnl, m, link->manager);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poetteringstatic int dhcp4_update_address(Link *link,
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering _cleanup_address_free_ Address *addr = NULL;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering prefixlen = in_addr_netmask_to_prefixlen(netmask);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering addr->in_addr.in.s_addr = address->s_addr;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering /* allow reusing an existing address and simply update its lifetime
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering * in case it already exists */
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = address_configure(addr, link, &dhcp4_address_handler, true);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poetteringstatic int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = sd_dhcp_client_get_lease(client, &lease);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return log_link_warning_errno(link, r, "DHCP error: no lease: %m");
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering link->dhcp_lease = sd_dhcp_lease_ref(lease);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = sd_dhcp_lease_get_address(lease, &address);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return log_link_warning_errno(link, r, "DHCP error: no address: %m");
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = sd_dhcp_lease_get_netmask(lease, &netmask);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return log_link_warning_errno(link, r, "DHCP error: no netmask: %m");
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = dhcp4_update_address(link, &address, &netmask, lifetime);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_link_warning_errno(link, r, "Could not update IP address: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = sd_dhcp_client_get_lease(client, &lease);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return log_link_error_errno(link, r, "DHCP error: No lease: %m");
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = sd_dhcp_lease_get_address(lease, &address);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return log_link_error_errno(link, r, "DHCP error: No address: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = sd_dhcp_lease_get_netmask(lease, &netmask);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return log_link_error_errno(link, r, "DHCP error: No netmask: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering prefixlen = in_addr_netmask_to_prefixlen(&netmask);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = sd_dhcp_lease_get_router(lease, &gateway);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (r < 0 && r != -ENODATA)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(gateway),
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u",
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering link->dhcp_lease = sd_dhcp_lease_ref(lease);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_link_error_errno(link, r, "Failed to set MTU to %" PRIu16 ": %m", mtu);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering (void) sd_dhcp_lease_get_hostname(lease, &hostname);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering (void) sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_link_error_errno(link, r, "Failed to set timezone to '%s': %m", tz);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = dhcp4_update_address(link, &address, &netmask, lifetime);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_link_warning_errno(link, r, "Could not update IP address: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering log_link_error(link, "DHCPv4 connection considered system critical, ignoring request to reconfigure it.");
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering if (event == SD_DHCP_CLIENT_EVENT_IP_CHANGE) {
if (event < 0)
if (!hostname)
return -ENOMEM;
case DHCP_CLIENT_ID_DUID:
case DHCP_CLIENT_ID_MAC: