diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 512d657138f..5307a508d3f 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -1283,9 +1283,8 @@ IPv6Token=prefixstable:2002:da8:1::
Gateway=
Takes the gateway address or the special values _dhcp4 and
- _dhcp6. If _dhcp4 or _dhcp6 is
- set, then the gateway address provided by DHCP (or in the IPv6 case, provided by IPv6 RA)
- is used.
+ _ipv6ra. If _dhcp4 or _ipv6ra is
+ set, then the gateway address provided by DHCPv4 or IPv6 RA is used.
diff --git a/src/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c
index 46d27f8f544..a41bd348dc4 100644
--- a/src/network/networkd-dhcp-common.c
+++ b/src/network/networkd-dhcp-common.c
@@ -26,18 +26,38 @@ bool link_dhcp_enabled(Link *link, int family) {
if (link->flags & IFF_LOOPBACK)
return false;
- if (!link->network)
- return false;
-
- if (link->network->bond)
- return false;
-
if (link->iftype == ARPHRD_CAN)
return false;
+ if (!link->network)
+ return false;
+
return link->network->dhcp & (family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6);
}
+void network_adjust_dhcp(Network *network) {
+ assert(network);
+ assert(network->dhcp >= 0);
+
+ if (network->dhcp == ADDRESS_FAMILY_NO)
+ return;
+
+ /* Bonding slave does not support addressing. */
+ if (network->bond) {
+ log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.",
+ network->filename);
+ network->dhcp = ADDRESS_FAMILY_NO;
+ return;
+ }
+
+ if (!FLAGS_SET(network->link_local, ADDRESS_FAMILY_IPV6) &&
+ FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV6)) {
+ log_warning("%s: DHCPv6 client is enabled but IPv6 link local addressing is disabled. "
+ "Disabling DHCPv6 client.", network->filename);
+ SET_FLAG(network->dhcp, ADDRESS_FAMILY_IPV6, false);
+ }
+}
+
DUID* link_get_duid(Link *link) {
if (link->network->duid.type != _DUID_TYPE_INVALID)
return &link->network->duid;
diff --git a/src/network/networkd-dhcp-common.h b/src/network/networkd-dhcp-common.h
index e3982a7ef16..fc84450e5eb 100644
--- a/src/network/networkd-dhcp-common.h
+++ b/src/network/networkd-dhcp-common.h
@@ -9,6 +9,7 @@
typedef struct Link Link;
typedef struct Manager Manager;
+typedef struct Network Network;
typedef enum DHCPUseDomains {
DHCP_USE_DOMAINS_NO,
@@ -46,6 +47,8 @@ static inline bool link_dhcp6_enabled(Link *link) {
return link_dhcp_enabled(link, AF_INET6);
}
+void network_adjust_dhcp(Network *network);
+
DUID* link_get_duid(Link *link);
int link_configure_duid(Link *link);
int manager_request_product_uuid(Manager *m, Link *link);
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 2920e615f3c..1396829bdd4 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -388,14 +388,21 @@ static int link_set_dhcp_routes(Link *link) {
return log_link_error_errno(link, r, "Could not set router: %m");
HASHMAP_FOREACH(rt, link->network->routes_by_section) {
- if (!rt->gateway_from_dhcp)
+ if (!rt->gateway_from_dhcp_or_ra)
continue;
- if (rt->family != AF_INET)
+ if (rt->gw_family != AF_INET)
continue;
- rt->gw_family = AF_INET;
rt->gw.in = router[0];
+ if (!rt->protocol_set)
+ rt->protocol = RTPROT_DHCP;
+ if (!rt->priority_set)
+ rt->priority = link->network->dhcp_route_metric;
+ if (!rt->table_set)
+ rt->table = table;
+ if (rt->mtu == 0)
+ rt->mtu = link->network->dhcp_route_mtu;
r = dhcp_route_configure(rt, link);
if (r < 0)
diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c
index c67dd7d88ed..6fde8fe2392 100644
--- a/src/network/networkd-ndisc.c
+++ b/src/network/networkd-ndisc.c
@@ -14,7 +14,6 @@
#include "networkd-dhcp6.h"
#include "networkd-manager.h"
#include "networkd-ndisc.h"
-#include "networkd-sysctl.h"
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
@@ -52,19 +51,23 @@ bool link_ipv6_accept_ra_enabled(Link *link) {
if (!link_ipv6ll_enabled(link))
return false;
- /* If unset use system default (enabled if local forwarding is disabled.
- * disabled if local forwarding is enabled).
- * If set, ignore or enforce RA independent of local forwarding state.
- */
- if (link->network->ipv6_accept_ra < 0)
+ assert(link->network->ipv6_accept_ra >= 0);
+ return link->network->ipv6_accept_ra;
+}
+
+void network_adjust_ipv6_accept_ra(Network *network) {
+ assert(network);
+
+ if (!FLAGS_SET(network->link_local, ADDRESS_FAMILY_IPV6)) {
+ if (network->ipv6_accept_ra > 0)
+ log_warning("%s: IPv6AcceptRA= is enabled but IPv6 link local addressing is disabled or not supported. "
+ "Disabling IPv6AcceptRA=.", network->filename);
+ network->ipv6_accept_ra = false;
+ }
+
+ if (network->ipv6_accept_ra < 0)
/* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */
- return !link_ip_forward_enabled(link, AF_INET6);
- else if (link->network->ipv6_accept_ra > 0)
- /* accept RA even if ip_forward is enabled */
- return true;
- else
- /* ignore RA */
- return false;
+ network->ipv6_accept_ra = !FLAGS_SET(network->ip_forward, ADDRESS_FAMILY_IPV6);
}
static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool force);
@@ -461,7 +464,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
union in_addr_union gateway;
uint16_t lifetime;
unsigned preference;
- uint32_t mtu;
+ uint32_t table, mtu;
usec_t time_now;
int r;
@@ -504,12 +507,14 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
else if (r < 0)
return log_link_error_errno(link, r, "Failed to get default router MTU from RA: %m");
+ table = link_get_ipv6_accept_ra_route_table(link);
+
r = route_new(&route);
if (r < 0)
return log_oom();
route->family = AF_INET6;
- route->table = link_get_ipv6_accept_ra_route_table(link);
+ route->table = table;
route->priority = link->network->dhcp6_route_metric;
route->protocol = RTPROT_RA;
route->pref = preference;
@@ -524,14 +529,24 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
Route *route_gw;
HASHMAP_FOREACH(route_gw, link->network->routes_by_section) {
- if (!route_gw->gateway_from_dhcp)
+ if (!route_gw->gateway_from_dhcp_or_ra)
continue;
- if (route_gw->family != AF_INET6)
+ if (route_gw->gw_family != AF_INET6)
continue;
route_gw->gw = gateway;
- route_gw->gw_family = AF_INET6;
+ if (!route_gw->table_set)
+ route_gw->table = table;
+ if (!route_gw->priority_set)
+ route_gw->priority = link->network->dhcp6_route_metric;
+ if (!route_gw->protocol_set)
+ route_gw->protocol = RTPROT_RA;
+ if (!route_gw->pref_set)
+ route->pref = preference;
+ route_gw->lifetime = time_now + lifetime * USEC_PER_SEC;
+ if (route_gw->mtu == 0)
+ route_gw->mtu = mtu;
r = ndisc_route_configure(route_gw, link, rt);
if (r < 0)
diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h
index fd7bb0e97fc..4e9a704995f 100644
--- a/src/network/networkd-ndisc.h
+++ b/src/network/networkd-ndisc.h
@@ -71,6 +71,8 @@ static inline char* NDISC_DNSSL_DOMAIN(const NDiscDNSSL *n) {
bool link_ipv6_accept_ra_enabled(Link *link);
+void network_adjust_ipv6_accept_ra(Network *network);
+
int ndisc_configure(Link *link);
void ndisc_vacuum(Link *link);
void ndisc_flush(Link *link);
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 9707c97653b..6bfc4cb9ebe 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -16,9 +16,11 @@
#include "network-internal.h"
#include "networkd-address-label.h"
#include "networkd-address.h"
+#include "networkd-dhcp-common.h"
#include "networkd-fdb.h"
#include "networkd-manager.h"
#include "networkd-mdb.h"
+#include "networkd-ndisc.h"
#include "networkd-neighbor.h"
#include "networkd-network.h"
#include "networkd-nexthop.h"
@@ -188,21 +190,11 @@ int network_verify(Network *network) {
if (network->bond) {
/* Bonding slave does not support addressing. */
- if (network->ipv6_accept_ra > 0) {
- log_warning("%s: Cannot enable IPv6AcceptRA= when Bond= is specified, disabling IPv6AcceptRA=.",
- network->filename);
- network->ipv6_accept_ra = 0;
- }
if (network->link_local >= 0 && network->link_local != ADDRESS_FAMILY_NO) {
log_warning("%s: Cannot enable LinkLocalAddressing= when Bond= is specified, disabling LinkLocalAddressing=.",
network->filename);
network->link_local = ADDRESS_FAMILY_NO;
}
- if (network->dhcp != ADDRESS_FAMILY_NO) {
- log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.",
- network->filename);
- network->dhcp = ADDRESS_FAMILY_NO;
- }
if (network->dhcp_server) {
log_warning("%s: Cannot enable DHCPServer= when Bond= is specified, disabling DHCPServer=.",
network->filename);
@@ -223,18 +215,6 @@ int network_verify(Network *network) {
network->link_local = network->bridge ? ADDRESS_FAMILY_NO : ADDRESS_FAMILY_IPV6;
if (!FLAGS_SET(network->link_local, ADDRESS_FAMILY_IPV6)) {
- if (network->ipv6_accept_ra > 0) {
- log_warning("%s: IPv6AcceptRA= is enabled by the .network file but IPv6 link local addressing is disabled. "
- "Disabling IPv6AcceptRA=.", network->filename);
- network->ipv6_accept_ra = false;
- }
-
- if (FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV6)) {
- log_warning("%s: DHCPv6 client is enabled by the .network file but IPv6 link local addressing is disabled. "
- "Disabling DHCPv6 client.", network->filename);
- SET_FLAG(network->dhcp, ADDRESS_FAMILY_IPV6, false);
- }
-
if (network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE) {
log_warning("%s: IPv6PrefixDelegation= is enabled but IPv6 link local addressing is disabled. "
"Disabling IPv6PrefixDelegation=.", network->filename);
@@ -249,13 +229,13 @@ int network_verify(Network *network) {
SET_FLAG(network->link_local, ADDRESS_FAMILY_FALLBACK_IPV4, false);
}
- if (network->ipv6_accept_ra < 0 && network->bridge)
- network->ipv6_accept_ra = false;
-
/* IPMasquerade=yes implies IPForward=yes */
if (network->ip_masquerade)
network->ip_forward |= ADDRESS_FAMILY_IPV4;
+ network_adjust_ipv6_accept_ra(network);
+ network_adjust_dhcp(network);
+
if (network->mtu > 0 && network->dhcp_use_mtu) {
log_warning("%s: MTUBytes= in [Link] section and UseMTU= in [DHCP] section are set. "
"Disabling UseMTU=.", network->filename);
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index 1ed82d9adce..707660df3c0 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -1192,7 +1192,7 @@ int link_set_routes(Link *link) {
/* First add the routes that enable us to talk to gateways, then add in the others that need a gateway. */
for (phase = 0; phase < _PHASE_MAX; phase++)
HASHMAP_FOREACH(rt, link->network->routes_by_section) {
- if (rt->gateway_from_dhcp)
+ if (rt->gateway_from_dhcp_or_ra)
continue;
if ((in_addr_is_null(rt->gw_family, &rt->gw) && ordered_set_isempty(rt->multipath_routes)) != (phase == PHASE_NON_GATEWAY))
@@ -1723,7 +1723,7 @@ int config_parse_gateway(
}
if (isempty(rvalue)) {
- n->gateway_from_dhcp = false;
+ n->gateway_from_dhcp_or_ra = false;
n->gw_family = AF_UNSPEC;
n->gw = IN_ADDR_NULL;
TAKE_PTR(n);
@@ -1731,21 +1731,21 @@ int config_parse_gateway(
}
if (streq(rvalue, "_dhcp")) {
- n->gateway_from_dhcp = true;
+ n->gateway_from_dhcp_or_ra = true;
TAKE_PTR(n);
return 0;
}
if (streq(rvalue, "_dhcp4")) {
n->gw_family = AF_INET;
- n->gateway_from_dhcp = true;
+ n->gateway_from_dhcp_or_ra = true;
TAKE_PTR(n);
return 0;
}
- if (streq(rvalue, "_dhcp6")) {
+ if (streq(rvalue, "_ipv6ra")) {
n->gw_family = AF_INET6;
- n->gateway_from_dhcp = true;
+ n->gateway_from_dhcp_or_ra = true;
TAKE_PTR(n);
return 0;
}
@@ -1758,7 +1758,7 @@ int config_parse_gateway(
return 0;
}
- n->gateway_from_dhcp = false;
+ n->gateway_from_dhcp_or_ra = false;
TAKE_PTR(n);
return 0;
}
@@ -1902,6 +1902,7 @@ int config_parse_route_priority(
return 0;
}
+ n->priority_set = true;
TAKE_PTR(n);
return 0;
}
@@ -2086,6 +2087,7 @@ int config_parse_ipv6_route_preference(
return 0;
}
+ n->pref_set = true;
TAKE_PTR(n);
return 0;
}
@@ -2380,25 +2382,55 @@ static int route_section_verify(Route *route, Network *network) {
if (section_is_invalid(route->section))
return -EINVAL;
+ if (route->gateway_from_dhcp_or_ra) {
+ if (route->gw_family == AF_UNSPEC) {
+ /* When deprecated Gateway=_dhcp is set, then assume gateway family based on other settings. */
+ switch (route->family) {
+ case AF_UNSPEC:
+ log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
+ "Please use \"_dhcp4\" or \"_ipv6ra\" instead. Assuming \"_dhcp4\".",
+ route->section->filename, route->section->line);
+ route->family = AF_INET;
+ break;
+ case AF_INET:
+ case AF_INET6:
+ log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
+ "Assuming \"%s\" based on Destination=, Source=, or PreferredSource= setting.",
+ route->section->filename, route->section->line, route->family == AF_INET ? "_dhcp4" : "_ipv6ra");
+ break;
+ default:
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: Invalid route family. Ignoring [Route] section from line %u.",
+ route->section->filename, route->section->line);
+ }
+ route->gw_family = route->family;
+ }
+
+ if (route->gw_family == AF_INET && !FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV4))
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: Gateway=\"_dhcp4\" is specified but DHCPv4 client is disabled. "
+ "Ignoring [Route] section from line %u.",
+ route->section->filename, route->section->line);
+
+ if (route->gw_family == AF_INET6 && !network->ipv6_accept_ra)
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: Gateway=\"_ipv6ra\" is specified but IPv6AcceptRA= is disabled. "
+ "Ignoring [Route] section from line %u.",
+ route->section->filename, route->section->line);
+ }
+
+ /* When only Gateway= is specified, assume the route family based on the Gateway address. */
if (route->family == AF_UNSPEC)
route->family = route->gw_family;
if (route->family == AF_UNSPEC) {
assert(route->section);
- if (route->gateway_from_dhcp) {
- log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
- "Please use \"_dhcp4\" or \"_dhcp6\" instead. Assuming \"_dhcp4\".",
- route->section->filename, route->section->line);
-
- route->family = AF_INET;
- route->gw_family = AF_INET;
- } else
- return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
- "%s: Route section without Gateway=, Destination=, Source=, "
- "or PreferredSource= field configured. "
- "Ignoring [Route] section from line %u.",
- route->section->filename, route->section->line);
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: Route section without Gateway=, Destination=, Source=, "
+ "or PreferredSource= field configured. "
+ "Ignoring [Route] section from line %u.",
+ route->section->filename, route->section->line);
}
if (route->family == AF_INET6 && route->gw_family == AF_INET)
diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h
index 8207b67a2df..e896719e13b 100644
--- a/src/network/networkd-route.h
+++ b/src/network/networkd-route.h
@@ -32,20 +32,24 @@ typedef struct Route {
unsigned char dst_prefixlen;
unsigned char src_prefixlen;
unsigned char scope;
- bool scope_set;
unsigned char protocol; /* RTPROT_* */
unsigned char type; /* RTN_* */
unsigned char tos;
uint32_t priority; /* note that ip(8) calls this 'metric' */
uint32_t table;
- bool table_set;
uint32_t mtu;
uint32_t initcwnd;
uint32_t initrwnd;
unsigned char pref;
unsigned flags;
int gateway_onlink;
- bool gateway_from_dhcp;
+
+ bool scope_set:1;
+ bool table_set:1;
+ bool priority_set:1;
+ bool protocol_set:1;
+ bool pref_set:1;
+ bool gateway_from_dhcp_or_ra:1;
union in_addr_union gw;
union in_addr_union dst;
diff --git a/src/network/networkd-sysctl.c b/src/network/networkd-sysctl.c
index c8facbf8316..de4ed95981e 100644
--- a/src/network/networkd-sysctl.c
+++ b/src/network/networkd-sysctl.c
@@ -38,7 +38,7 @@ static int link_set_proxy_arp(Link *link) {
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", link->network->proxy_arp > 0);
}
-bool link_ip_forward_enabled(Link *link, int family) {
+static bool link_ip_forward_enabled(Link *link, int family) {
assert(link);
assert(IN_SET(family, AF_INET, AF_INET6));
diff --git a/src/network/networkd-sysctl.h b/src/network/networkd-sysctl.h
index a409d8f54f2..552b55226ac 100644
--- a/src/network/networkd-sysctl.h
+++ b/src/network/networkd-sysctl.h
@@ -16,7 +16,6 @@ typedef enum IPv6PrivacyExtensions {
_IPV6_PRIVACY_EXTENSIONS_INVALID = -1,
} IPv6PrivacyExtensions;
-bool link_ip_forward_enabled(Link *link, int family);
int link_set_sysctl(Link *link);
int link_set_ipv6_mtu(Link *link);
diff --git a/test/test-network/conf/dhcp-client-gateway-ipv4.network b/test/test-network/conf/dhcp-client-gateway-ipv4.network
index 7e6f6b33e01..de43c16d118 100644
--- a/test/test-network/conf/dhcp-client-gateway-ipv4.network
+++ b/test/test-network/conf/dhcp-client-gateway-ipv4.network
@@ -6,5 +6,5 @@ DHCP=ipv4
IPv6AcceptRA=no
[Route]
-Gateway=_dhcp
+Gateway=_dhcp4
Destination=10.0.0.0/8
diff --git a/test/test-network/conf/dhcp-client-gateway-ipv6.network b/test/test-network/conf/dhcp-client-gateway-ipv6.network
index ce708e7da9a..604a953b581 100644
--- a/test/test-network/conf/dhcp-client-gateway-ipv6.network
+++ b/test/test-network/conf/dhcp-client-gateway-ipv6.network
@@ -5,5 +5,5 @@ Name=veth99
DHCP=ipv6
[Route]
-Gateway=_dhcp
+Gateway=_ipv6ra
Destination=2001:1234:5:9fff:ff:ff:ff:ff/128
diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py
index 3189302655a..752b2fee435 100755
--- a/test/test-network/systemd-networkd-tests.py
+++ b/test/test-network/systemd-networkd-tests.py
@@ -3899,7 +3899,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
output = check_output('ip route list dev veth99 10.0.0.0/8')
print(output)
- self.assertRegex(output, '10.0.0.0/8 via 192.168.5.1 proto static')
+ self.assertRegex(output, '10.0.0.0/8 via 192.168.5.1 proto dhcp')
def test_dhcp_client_gateway_ipv6(self):
copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',