+/* SPDX-License-Identifier: LGPL-2.1+ */
/***
This file is part of systemd.
#include "alloc-util.h"
#include "dhcp-lease-internal.h"
#include "hostname-util.h"
+#include "netdev/vrf.h"
#include "network-internal.h"
#include "networkd-link.h"
#include "networkd-manager.h"
return 1;
}
+static int route_scope_from_address(const Route *route, const struct in_addr *self_addr) {
+ assert(route);
+ assert(self_addr);
+
+ if (in_addr_is_localhost(AF_INET, &route->dst) ||
+ (self_addr->s_addr && route->dst.in.s_addr == self_addr->s_addr))
+ return RT_SCOPE_HOST;
+ else if (in4_addr_is_null(&route->gw.in))
+ return RT_SCOPE_LINK;
+ else
+ return RT_SCOPE_UNIVERSE;
+}
+
static int link_set_dhcp_routes(Link *link) {
- struct in_addr gateway;
+ struct in_addr gateway, address;
_cleanup_free_ sd_dhcp_route **static_routes = NULL;
int r, n, i;
+ uint32_t table;
assert(link);
- assert(link->dhcp_lease);
- assert(link->network);
+
+ 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;
- 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");
+ /* 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;
- if (r >= 0) {
- struct in_addr address;
+ 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++) {
_cleanup_route_free_ Route *route = NULL;
- _cleanup_route_free_ Route *route_gw = NULL;
- r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
+ r = route_new(&route);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP error: could not get address: %m");
+ 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 (r >= 0 && link->dhcp4_messages <= 0) {
+ _cleanup_route_free_ Route *route = NULL;
+ _cleanup_route_free_ Route *route_gw = NULL;
r = route_new(&route);
if (r < 0)
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;
+ route_gw->table = table;
r = route_configure(route_gw, link, dhcp4_route_handler);
if (r < 0)
route->gw.in = gateway;
route->prefsrc.in = address;
route->priority = link->network->dhcp_route_metric;
- route->table = link->network->dhcp_route_table;
+ route->table = table;
r = route_configure(route, link, dhcp4_route_handler);
if (r < 0) {
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;
-
- 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;
}
if (r >= 0) {
r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
if (r >= 0)
- prefixlen = in_addr_netmask_to_prefixlen(&netmask);
+ prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
address->family = AF_INET;
address->in_addr.in = addr;
if (hostname) {
/* If a hostname was set due to the lease, then unset it now. */
- r = link_set_hostname(link, NULL);
+ r = manager_set_hostname(link->manager, NULL);
if (r < 0)
log_link_warning_errno(link, r, "Failed to reset transient hostname: %m");
}
assert(netmask);
assert(lifetime);
- prefixlen = in_addr_netmask_to_prefixlen(netmask);
+ prefixlen = in4_addr_netmask_to_prefixlen(netmask);
r = address_new(&addr);
if (r < 0)
if (r < 0)
return log_link_error_errno(link, r, "DHCP error: No netmask: %m");
- prefixlen = in_addr_netmask_to_prefixlen(&netmask);
+ prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
r = sd_dhcp_lease_get_router(lease, &gateway);
if (r < 0 && r != -ENODATA)
(void) sd_dhcp_lease_get_hostname(lease, &hostname);
if (hostname) {
- r = link_set_hostname(link, hostname);
+ r = manager_set_hostname(link->manager, hostname);
if (r < 0)
log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname);
}
(void) sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz);
if (tz) {
- r = link_set_timezone(link, tz);
+ r = manager_set_timezone(link->manager, tz);
if (r < 0)
log_link_error_errno(link, r, "Failed to set timezone to '%s': %m", tz);
}
assert(link->network->dhcp & ADDRESS_FAMILY_IPV4);
if (!link->dhcp_client) {
- r = sd_dhcp_client_new(&link->dhcp_client);
+ r = sd_dhcp_client_new(&link->dhcp_client, link->network->dhcp_anonymize);
if (r < 0)
return r;
}
return r;
}
- if (link->network->dhcp_use_routes) {
+ /* NOTE: even if this variable is called "use", it also "sends" PRL
+ * options, maybe there should be a different configuration variable
+ * to send or not route options?. */
+ /* NOTE: when using Anonymize=yes, routes PRL options are sent
+ * by default, so they don't need to be added here. */
+ if (link->network->dhcp_use_routes && !link->network->dhcp_anonymize) {
r = sd_dhcp_client_set_request_option(link->dhcp_client,
SD_DHCP_OPTION_STATIC_ROUTE);
if (r < 0)
return r;
}
- /* Always acquire the timezone and NTP */
- r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NTP_SERVER);
- if (r < 0)
- return r;
+ if (link->network->dhcp_use_ntp) {
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NTP_SERVER);
+ if (r < 0)
+ return r;
+ }
- r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE);
- if (r < 0)
- return r;
+ if (link->network->dhcp_use_timezone) {
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE);
+ if (r < 0)
+ return r;
+ }
r = dhcp4_set_hostname(link);
if (r < 0)