Example #1
0
static int link_set_dhcp_routes(Link *link) {
        struct in_addr gateway, address;
        _cleanup_free_ sd_dhcp_route **static_routes = NULL;
        int r, n, i;

        assert(link);
        assert(link->dhcp_lease);
        assert(link->network);

        if (!link->network->dhcp_use_routes)
                return 0;

        r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
        if (r < 0)
                return log_link_warning_errno(link, r, "DHCP error: could not get address: %m");

        r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
        if (r < 0 && r != -ENODATA)
                return log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m");

        if (r >= 0) {
                _cleanup_route_free_ Route *route = NULL;
                _cleanup_route_free_ Route *route_gw = NULL;

                r = route_new(&route);
                if (r < 0)
                        return log_link_error_errno(link, r, "Could not allocate route: %m");

                route->protocol = RTPROT_DHCP;

                r = route_new(&route_gw);
                if (r < 0)
                        return log_link_error_errno(link, r,  "Could not allocate route: %m");

                /* The dhcp netmask may mask out the gateway. Add an explicit
                 * route for the gw host so that we can route no matter the
                 * netmask or existing kernel route tables. */
                route_gw->family = AF_INET;
                route_gw->dst.in = gateway;
                route_gw->dst_prefixlen = 32;
                route_gw->prefsrc.in = address;
                route_gw->scope = RT_SCOPE_LINK;
                route_gw->protocol = RTPROT_DHCP;
                route_gw->priority = link->network->dhcp_route_metric;
                route_gw->table = link->network->dhcp_route_table;

                r = route_configure(route_gw, link, dhcp4_route_handler);
                if (r < 0)
                        return log_link_warning_errno(link, r, "Could not set host route: %m");

                link->dhcp4_messages++;

                route->family = AF_INET;
                route->gw.in = gateway;
                route->prefsrc.in = address;
                route->priority = link->network->dhcp_route_metric;
                route->table = link->network->dhcp_route_table;

                r = route_configure(route, link, dhcp4_route_handler);
                if (r < 0) {
                        log_link_warning_errno(link, r, "Could not set routes: %m");
                        link_enter_failed(link);
                        return r;
                }

                link->dhcp4_messages++;
        }

        n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
        if (n == -ENODATA)
                return 0;
        if (n < 0)
                return log_link_warning_errno(link, n, "DHCP error: could not get routes: %m");

        for (i = 0; i < n; i++) {
                _cleanup_route_free_ Route *route = NULL;

                r = route_new(&route);
                if (r < 0)
                        return log_link_error_errno(link, r, "Could not allocate route: %m");

                route->family = AF_INET;
                route->protocol = RTPROT_DHCP;
                assert_se(sd_dhcp_route_get_gateway(static_routes[i], &route->gw.in) >= 0);
                assert_se(sd_dhcp_route_get_destination(static_routes[i], &route->dst.in) >= 0);
                assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0);
                route->priority = link->network->dhcp_route_metric;
                route->table = link->network->dhcp_route_table;
                route->scope = route_scope_from_address(route, &address);

                r = route_configure(route, link, dhcp4_route_handler);
                if (r < 0)
                        return log_link_warning_errno(link, r, "Could not set host route: %m");

                link->dhcp4_messages++;
        }

        return 0;
}
Example #2
0
static int dhcp_lease_lost(Link *link) {
        _cleanup_address_free_ Address *address = NULL;
        struct in_addr addr;
        struct in_addr netmask;
        struct in_addr gateway;
        unsigned prefixlen = 0;
        int r;

        assert(link);
        assert(link->dhcp_lease);

        log_link_warning(link, "DHCP lease lost");

        if (link->network->dhcp_use_routes) {
                _cleanup_free_ sd_dhcp_route **routes = NULL;
                int n, i;

                n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes);
                if (n >= 0) {
                        for (i = 0; i < n; i++) {
                                _cleanup_route_free_ Route *route = NULL;

                                r = route_new(&route);
                                if (r >= 0) {
                                        route->family = AF_INET;
                                        assert_se(sd_dhcp_route_get_gateway(routes[i], &route->gw.in) >= 0);
                                        assert_se(sd_dhcp_route_get_destination(routes[i], &route->dst.in) >= 0);
                                        assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen) >= 0);

                                        route_remove(route, link,
                                                     link_route_remove_handler);
                                }
                        }
                }
        }

        r = address_new(&address);
        if (r >= 0) {
                r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
                if (r >= 0) {
                        _cleanup_route_free_ Route *route_gw = NULL;
                        _cleanup_route_free_ Route *route = NULL;

                        r = route_new(&route_gw);
                        if (r >= 0) {
                                route_gw->family = AF_INET;
                                route_gw->dst.in = gateway;
                                route_gw->dst_prefixlen = 32;
                                route_gw->scope = RT_SCOPE_LINK;

                                route_remove(route_gw, link,
                                             link_route_remove_handler);
                        }

                        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_use_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_use_hostname) {
                const char *hostname = NULL;

                if (link->network->dhcp_hostname)
                        hostname = link->network->dhcp_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 = manager_set_hostname(link->manager, 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_dirty(link);
        link->dhcp4_configured = false;

        return 0;
}
Example #3
0
static int link_set_dhcp_routes(Link *link) {
        _cleanup_free_ sd_dhcp_route **static_routes = NULL;
        bool classless_route = false, static_route = false;
        const struct in_addr *router;
        struct in_addr address;
        int r, n, i;
        uint32_t table;

        assert(link);

        if (!link->dhcp_lease) /* link went down while we configured the IP addresses? */
                return 0;

        if (!link->network) /* link went down while we configured the IP addresses? */
                return 0;

        if (!link->network->dhcp_use_routes)
                return 0;

        table = link_get_dhcp_route_table(link);

        r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
        if (r < 0)
                return log_link_warning_errno(link, r, "DHCP error: could not get address: %m");

        n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
        if (n == -ENODATA)
                log_link_debug_errno(link, n, "DHCP: No routes received from DHCP server: %m");
        else if (n < 0)
                log_link_debug_errno(link, n, "DHCP error: could not get routes: %m");

        for (i = 0; i < n; i++) {
                switch (sd_dhcp_route_get_option(static_routes[i])) {
                case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
                        classless_route = true;
                        break;
                case SD_DHCP_OPTION_STATIC_ROUTE:
                        static_route = true;
                        break;
                }
        }

        for (i = 0; i < n; i++) {
                _cleanup_(route_freep) Route *route = NULL;

                /* if the DHCP server returns both a Classless Static Routes option and a Static Routes option,
                   the DHCP client MUST ignore the Static Routes option. */
                if (classless_route &&
                    sd_dhcp_route_get_option(static_routes[i]) != SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE)
                        continue;

                r = route_new(&route);
                if (r < 0)
                        return log_link_error_errno(link, r, "Could not allocate route: %m");

                route->family = AF_INET;
                route->protocol = RTPROT_DHCP;
                assert_se(sd_dhcp_route_get_gateway(static_routes[i], &route->gw.in) >= 0);
                assert_se(sd_dhcp_route_get_destination(static_routes[i], &route->dst.in) >= 0);
                assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0);
                route->priority = link->network->dhcp_route_metric;
                route->table = table;
                route->scope = route_scope_from_address(route, &address);

                r = route_configure(route, link, dhcp4_route_handler);
                if (r < 0)
                        return log_link_warning_errno(link, r, "Could not set host route: %m");

                link->dhcp4_messages++;
        }

        r = sd_dhcp_lease_get_router(link->dhcp_lease, &router);
        if (IN_SET(r, 0, -ENODATA))
                log_link_info(link, "DHCP: No gateway received from DHCP server.");
        else if (r < 0)
                log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m");
        else if (in4_addr_is_null(&router[0]))
                log_link_info(link, "DHCP: Received gateway is null.");

        /* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and
           a Router option, the DHCP client MUST ignore the Router option. */
        if (classless_route && static_route)
                log_link_warning(link, "Classless static routes received from DHCP server: ignoring static-route option and router option");

        if (r > 0 && !classless_route && !in4_addr_is_null(&router[0])) {
                _cleanup_(route_freep) Route *route = NULL, *route_gw = NULL;

                r = route_new(&route_gw);
                if (r < 0)
                        return log_link_error_errno(link, r,  "Could not allocate route: %m");

                /* The dhcp netmask may mask out the gateway. Add an explicit
                 * route for the gw host so that we can route no matter the
                 * netmask or existing kernel route tables. */
                route_gw->family = AF_INET;
                route_gw->dst.in = router[0];
                route_gw->dst_prefixlen = 32;
                route_gw->prefsrc.in = address;
                route_gw->scope = RT_SCOPE_LINK;
                route_gw->protocol = RTPROT_DHCP;
                route_gw->priority = link->network->dhcp_route_metric;
                route_gw->table = table;

                r = route_configure(route_gw, link, dhcp4_route_handler);
                if (r < 0)
                        return log_link_warning_errno(link, r, "Could not set host route: %m");

                link->dhcp4_messages++;

                r = route_new(&route);
                if (r < 0)
                        return log_link_error_errno(link, r, "Could not allocate route: %m");

                route->family = AF_INET;
                route->gw.in = router[0];
                route->prefsrc.in = address;
                route->protocol = RTPROT_DHCP;
                route->priority = link->network->dhcp_route_metric;
                route->table = table;

                r = route_configure(route, link, dhcp4_route_handler);
                if (r < 0) {
                        log_link_warning_errno(link, r, "Could not set routes: %m");
                        link_enter_failed(link);
                        return r;
                }

                link->dhcp4_messages++;
        }

        return 0;
}
static NMIP4Config *
lease_to_ip4_config (const char *iface,
                     int ifindex,
                     sd_dhcp_lease *lease,
                     GHashTable *options,
                     guint32 default_priority,
                     gboolean log_lease,
                     GError **error)
{
	NMIP4Config *ip4_config = NULL;
	struct in_addr tmp_addr;
	const struct in_addr *addr_list;
	char buf[INET_ADDRSTRLEN];
	const char *str;
	guint32 lifetime = 0, i;
	NMPlatformIP4Address address;
	GString *l;
	gs_free sd_dhcp_route **routes = NULL;
	guint16 mtu;
	int r, num;
	guint64 end_time;
	const void *data;
	gsize data_len;
	gboolean metered = FALSE;
	gboolean static_default_gateway = FALSE;

	g_return_val_if_fail (lease != NULL, NULL);

	ip4_config = nm_ip4_config_new (ifindex);

	/* Address */
	sd_dhcp_lease_get_address (lease, &tmp_addr);
	memset (&address, 0, sizeof (address));
	address.address = tmp_addr.s_addr;
	address.peer_address = tmp_addr.s_addr;
	str = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL);
	LOG_LEASE (LOGD_DHCP4, "  address %s", str);
	add_option (options, dhcp4_requests, DHCP_OPTION_IP_ADDRESS, str);

	/* Prefix/netmask */
	sd_dhcp_lease_get_netmask (lease, &tmp_addr);
	address.plen = nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr);
	LOG_LEASE (LOGD_DHCP4, "  plen %d", address.plen);
	add_option (options,
	            dhcp4_requests,
	            SD_DHCP_OPTION_SUBNET_MASK,
	            nm_utils_inet4_ntop (tmp_addr.s_addr, NULL));

	/* Lease time */
	sd_dhcp_lease_get_lifetime (lease, &lifetime);
	address.timestamp = nm_utils_get_monotonic_timestamp_s ();
	address.lifetime = address.preferred = lifetime;
	end_time = (guint64) time (NULL) + lifetime;
	LOG_LEASE (LOGD_DHCP4, "  expires in %" G_GUINT32_FORMAT " seconds", lifetime);
	add_option_u64 (options,
	                dhcp4_requests,
	                SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME,
	                end_time);

	address.addr_source = NM_IP_CONFIG_SOURCE_DHCP;
	nm_ip4_config_add_address (ip4_config, &address);

	/* DNS Servers */
	num = sd_dhcp_lease_get_dns (lease, &addr_list);
	if (num > 0) {
		l = g_string_sized_new (30);
		for (i = 0; i < num; i++) {
			if (addr_list[i].s_addr) {
				nm_ip4_config_add_nameserver (ip4_config, addr_list[i].s_addr);
				str = nm_utils_inet4_ntop (addr_list[i].s_addr, NULL);
				LOG_LEASE (LOGD_DHCP4, "  nameserver '%s'", str);
				g_string_append_printf (l, "%s%s", l->len ? " " : "", str);
			}
		}
		if (l->len)
			add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_NAME_SERVER, l->str);
		g_string_free (l, TRUE);
	}

	/* Domain Name */
	r = sd_dhcp_lease_get_domainname (lease, &str);
	if (r == 0) {
		/* Multiple domains sometimes stuffed into option 15 "Domain Name".
		 * As systemd escapes such characters, split them at \\032. */
		char **domains = g_strsplit (str, "\\032", 0);
		char **s;

		for (s = domains; *s; s++) {
			LOG_LEASE (LOGD_DHCP4, "  domain name '%s'", *s);
			nm_ip4_config_add_domain (ip4_config, *s);
		}
		g_strfreev (domains);
		add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_NAME, str);
	}

	/* Hostname */
	r = sd_dhcp_lease_get_hostname (lease, &str);
	if (r == 0) {
		LOG_LEASE (LOGD_DHCP4, "  hostname '%s'", str);
		add_option (options, dhcp4_requests, SD_DHCP_OPTION_HOST_NAME, str);
	}

	/* Routes */
	num = sd_dhcp_lease_get_routes (lease, &routes);
	if (num > 0) {
		l = g_string_sized_new (30);
		for (i = 0; i < num; i++) {
			NMPlatformIP4Route route = { 0 };
			const char *gw_str;
			guint8 plen;
			struct in_addr a;

			if (sd_dhcp_route_get_destination (routes[i], &a) < 0)
				continue;
			route.network = a.s_addr;

			if (   sd_dhcp_route_get_destination_prefix_length (routes[i], &plen) < 0
			    || plen > 32)
				continue;
			route.plen = plen;

			if (sd_dhcp_route_get_gateway (routes[i], &a) < 0)
				continue;
			route.gateway = a.s_addr;

			if (route.plen) {
				route.rt_source = NM_IP_CONFIG_SOURCE_DHCP;
				route.metric = default_priority;
				nm_ip4_config_add_route (ip4_config, &route);

				str = nm_utils_inet4_ntop (route.network, buf);
				gw_str = nm_utils_inet4_ntop (route.gateway, NULL);
				LOG_LEASE (LOGD_DHCP4, "  static route %s/%d gw %s", str, route.plen, gw_str);

				g_string_append_printf (l, "%s%s/%d %s", l->len ? " " : "", str, route.plen, gw_str);
			} else {
				if (!static_default_gateway) {
					static_default_gateway = TRUE;
					nm_ip4_config_set_gateway (ip4_config, route.gateway);

					str = nm_utils_inet4_ntop (route.gateway, NULL);
					LOG_LEASE (LOGD_DHCP4, "  gateway %s", str);
					add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROUTER, str);
				}
			}
		}
		if (l->len)
			add_option (options, dhcp4_requests, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, l->str);
		g_string_free (l, TRUE);
	}

	/* If the DHCP server returns both a Classless Static Routes option and a
	 * Router option, the DHCP client MUST ignore the Router option [RFC 3442].
	 * Be more lenient and ignore the Router option only if Classless Static
	 * Routes contain a default gateway (as other DHCP backends do).
	 */
	/* Gateway */
	if (!static_default_gateway) {
		r = sd_dhcp_lease_get_router (lease, &tmp_addr);
		if (r == 0) {
			nm_ip4_config_set_gateway (ip4_config, tmp_addr.s_addr);
			str = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL);
			LOG_LEASE (LOGD_DHCP4, "  gateway %s", str);
			add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROUTER, str);
		}
	}

	/* MTU */
	r = sd_dhcp_lease_get_mtu (lease, &mtu);
	if (r == 0 && mtu) {
		nm_ip4_config_set_mtu (ip4_config, mtu, NM_IP_CONFIG_SOURCE_DHCP);
		add_option_u32 (options, dhcp4_requests, SD_DHCP_OPTION_INTERFACE_MTU, mtu);
		LOG_LEASE (LOGD_DHCP4, "  mtu %u", mtu);
	}

	/* NTP servers */
	num = sd_dhcp_lease_get_ntp (lease, &addr_list);
	if (num > 0) {
		l = g_string_sized_new (30);
		for (i = 0; i < num; i++) {
			str = nm_utils_inet4_ntop (addr_list[i].s_addr, buf);
			LOG_LEASE (LOGD_DHCP4, "  ntp server '%s'", str);
			g_string_append_printf (l, "%s%s", l->len ? " " : "", str);
		}
		add_option (options, dhcp4_requests, SD_DHCP_OPTION_NTP_SERVER, l->str);
		g_string_free (l, TRUE);
	}

	r = sd_dhcp_lease_get_vendor_specific (lease, &data, &data_len);
	if (r >= 0)
		metered = !!memmem (data, data_len, "ANDROID_METERED", NM_STRLEN ("ANDROID_METERED"));
	nm_ip4_config_set_metered (ip4_config, metered);

	return ip4_config;
}
Example #5
0
static int link_set_dhcp_routes(Link *link) {
        _cleanup_free_ sd_dhcp_route **static_routes = NULL;
        bool classless_route = false, static_route = false;
        struct in_addr gateway, address;
        int r, n, i;
        uint32_t table;

        assert(link);

        if (!link->dhcp_lease) /* link went down while we configured the IP addresses? */
                return 0;

        if (!link->network) /* link went down while we configured the IP addresses? */
                return 0;

        if (!link->network->dhcp_use_routes)
                return 0;

        /* When the interface is part of an VRF use the VRFs routing table, unless
         * there is a another table specified. */
        table = link->network->dhcp_route_table;
        if (!link->network->dhcp_route_table_set && link->network->vrf != NULL)
                table = VRF(link->network->vrf)->table;

        r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
        if (r < 0)
                return log_link_warning_errno(link, r, "DHCP error: could not get address: %m");

        n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
        if (n < 0)
                log_link_debug_errno(link, n, "DHCP error: could not get routes: %m");

        for (i = 0; i < n; i++) {
                if (static_routes[i]->option == SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE)
                        classless_route = true;

                if (static_routes[i]->option == SD_DHCP_OPTION_STATIC_ROUTE)
                        static_route = true;
        }

        for (i = 0; i < n; i++) {
                _cleanup_route_free_ Route *route = NULL;

                /* if the DHCP server returns both a Classless Static Routes option and a Static Routes option,
                   the DHCP client MUST ignore the Static Routes option. */
                if (classless_route && static_routes[i]->option == SD_DHCP_OPTION_STATIC_ROUTE)
                        continue;

                r = route_new(&route);
                if (r < 0)
                        return log_link_error_errno(link, r, "Could not allocate route: %m");

                route->family = AF_INET;
                route->protocol = RTPROT_DHCP;
                assert_se(sd_dhcp_route_get_gateway(static_routes[i], &route->gw.in) >= 0);
                assert_se(sd_dhcp_route_get_destination(static_routes[i], &route->dst.in) >= 0);
                assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0);
                route->priority = link->network->dhcp_route_metric;
                route->table = table;
                route->scope = route_scope_from_address(route, &address);

                r = route_configure(route, link, dhcp4_route_handler);
                if (r < 0)
                        return log_link_warning_errno(link, r, "Could not set host route: %m");

                link->dhcp4_messages++;
        }

        r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
        if (r == -ENODATA)
                log_link_info_errno(link, r, "DHCP: No routes received from DHCP server: %m");
        else if (r < 0)
                log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m");

        /* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and
           a Router option, the DHCP client MUST ignore the Router option. */
        if (classless_route && static_route)
                log_link_warning(link, "Classless static routes received from DHCP server: ignoring static-route option and router option");

        if (r >= 0 && !classless_route) {
                _cleanup_route_free_ Route *route = NULL;
                _cleanup_route_free_ Route *route_gw = NULL;

                r = route_new(&route);
                if (r < 0)
                        return log_link_error_errno(link, r, "Could not allocate route: %m");

                route->protocol = RTPROT_DHCP;

                r = route_new(&route_gw);
                if (r < 0)
                        return log_link_error_errno(link, r,  "Could not allocate route: %m");

                /* The dhcp netmask may mask out the gateway. Add an explicit
                 * route for the gw host so that we can route no matter the
                 * netmask or existing kernel route tables. */
                route_gw->family = AF_INET;
                route_gw->dst.in = gateway;
                route_gw->dst_prefixlen = 32;
                route_gw->prefsrc.in = address;
                route_gw->scope = RT_SCOPE_LINK;
                route_gw->protocol = RTPROT_DHCP;
                route_gw->priority = link->network->dhcp_route_metric;
                route_gw->table = table;

                r = route_configure(route_gw, link, dhcp4_route_handler);
                if (r < 0)
                        return log_link_warning_errno(link, r, "Could not set host route: %m");

                link->dhcp4_messages++;

                route->family = AF_INET;
                route->gw.in = gateway;
                route->prefsrc.in = address;
                route->priority = link->network->dhcp_route_metric;
                route->table = table;

                r = route_configure(route, link, dhcp4_route_handler);
                if (r < 0) {
                        log_link_warning_errno(link, r, "Could not set routes: %m");
                        link_enter_failed(link);
                        return r;
                }

                link->dhcp4_messages++;
        }

        return 0;
}