network: introduce per-interface IP forwarding settings

This deprecates IPForward= setting, which unconditionally controled
the global setting, even though it is a setting in .network file.

Instead, this introduces new IPv4Forwarding= and IPv6Forwarding=
settings both in .network and networkd.conf.
If these settings are specified in a .network file, then the
per-interface forwarding setting will be configured.
If specified in networkd.conf, then the global IP forwarding setting will
be configured.

Closes #30648.
This commit is contained in:
Yu Watanabe
2024-02-22 19:24:12 +00:00
committed by Luca Boccassi
parent 755fdfffa0
commit 3976c43092
24 changed files with 197 additions and 125 deletions
+2
View File
@@ -27,6 +27,8 @@ Network.ManageForeignRoutingPolicyRules, config_parse_bool,
Network.ManageForeignRoutes, config_parse_bool, 0, offsetof(Manager, manage_foreign_routes)
Network.ManageForeignNextHops, config_parse_bool, 0, offsetof(Manager, manage_foreign_nexthops)
Network.RouteTable, config_parse_route_table_names, 0, 0
Network.IPv4Forwarding, config_parse_tristate, 0, offsetof(Manager, ip_forwarding[0])
Network.IPv6Forwarding, config_parse_tristate, 0, offsetof(Manager, ip_forwarding[1])
Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, 0, offsetof(Manager, ipv6_privacy_extensions)
DHCPv4.DUIDType, config_parse_duid_type, 0, offsetof(Manager, dhcp_duid)
DHCPv4.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, dhcp_duid)
+3 -3
View File
@@ -8,6 +8,7 @@
#include "networkd-link.h"
#include "networkd-lldp-tx.h"
#include "networkd-manager.h"
#include "networkd-sysctl.h"
#include "parse-util.h"
#include "string-table.h"
#include "string-util.h"
@@ -69,9 +70,8 @@ int link_lldp_tx_configure(Link *link) {
SD_LLDP_SYSTEM_CAPABILITIES_STATION |
SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE |
SD_LLDP_SYSTEM_CAPABILITIES_ROUTER,
(link->network->ip_forward != ADDRESS_FAMILY_NO) ?
SD_LLDP_SYSTEM_CAPABILITIES_ROUTER :
SD_LLDP_SYSTEM_CAPABILITIES_STATION);
(link_get_ip_forwarding(link, AF_INET) > 0 || link_get_ip_forwarding(link, AF_INET6) > 0) ?
SD_LLDP_SYSTEM_CAPABILITIES_ROUTER : SD_LLDP_SYSTEM_CAPABILITIES_STATION);
if (r < 0)
return r;
+3
View File
@@ -577,6 +577,7 @@ int manager_new(Manager **ret, bool test_mode) {
.dhcp_duid.type = DUID_TYPE_EN,
.dhcp6_duid.type = DUID_TYPE_EN,
.duid_product_uuid.type = DUID_TYPE_UUID,
.ip_forwarding = { -1, -1, },
};
*ret = TAKE_PTR(m);
@@ -659,6 +660,8 @@ int manager_start(Manager *m) {
assert(m);
manager_set_sysctl(m);
r = manager_start_speed_meter(m);
if (r < 0)
return log_error_errno(r, "Failed to initialize speed meter: %m");
+3
View File
@@ -107,6 +107,9 @@ struct Manager {
Hashmap *tuntap_fds_by_name;
unsigned reloading;
/* sysctl */
int ip_forwarding[2];
};
int manager_new(Manager **ret, bool test_mode);
+11 -6
View File
@@ -20,6 +20,7 @@
#include "networkd-queue.h"
#include "networkd-route.h"
#include "networkd-state-file.h"
#include "networkd-sysctl.h"
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
@@ -52,8 +53,16 @@ bool link_ipv6_accept_ra_enabled(Link *link) {
if (!link_may_have_ipv6ll(link, /* check_multicast = */ true))
return false;
assert(link->network->ipv6_accept_ra >= 0);
return link->network->ipv6_accept_ra;
if (link->network->ipv6_accept_ra >= 0)
return link->network->ipv6_accept_ra;
/* Accept RAs if IPv6 forwarding is disabled, and ignore RAs if IPv6 forwarding is enabled. */
int t = link_get_ip_forwarding(link, AF_INET6);
if (t >= 0)
return !t;
/* Otherwise, defaults to true. */
return true;
}
void network_adjust_ipv6_accept_ra(Network *network) {
@@ -66,10 +75,6 @@ void network_adjust_ipv6_accept_ra(Network *network) {
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 */
network->ipv6_accept_ra = !FLAGS_SET(network->ip_forward, ADDRESS_FAMILY_IPV6);
/* When RouterAllowList=, PrefixAllowList= or RouteAllowList= are specified, then
* RouterDenyList=, PrefixDenyList= or RouteDenyList= are ignored, respectively. */
if (!set_isempty(network->ndisc_allow_listed_router))
+3 -1
View File
@@ -124,7 +124,9 @@ Network.DNSOverTLS, config_parse_dns_over_tls_mode,
Network.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Network, dnssec_mode)
Network.DNSSECNegativeTrustAnchors, config_parse_dnssec_negative_trust_anchors, 0, offsetof(Network, dnssec_negative_trust_anchors)
Network.NTP, config_parse_ntp, 0, offsetof(Network, ntp)
Network.IPForward, config_parse_address_family_with_kernel, 0, offsetof(Network, ip_forward)
Network.IPForward, config_parse_ip_forward_deprecated, 0, 0
Network.IPv4Forwarding, config_parse_tristate, 0, offsetof(Network, ip_forwarding[0])
Network.IPv6Forwarding, config_parse_tristate, 0, offsetof(Network, ip_forwarding[1])
Network.IPMasquerade, config_parse_ip_masquerade, 0, offsetof(Network, ip_masquerade)
Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, 0, offsetof(Network, ipv6_privacy_extensions)
Network.IPv6AcceptRA, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra)
+1 -3
View File
@@ -225,9 +225,6 @@ int network_verify(Network *network) {
network->ipv6ll_address_gen_mode < 0)
network->ipv6ll_address_gen_mode = IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY;
/* IPMasquerade implies IPForward */
network->ip_forward |= network->ip_masquerade;
network_adjust_ipv6_proxy_ndp(network);
network_adjust_ipv6_accept_ra(network);
network_adjust_dhcp(network);
@@ -465,6 +462,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.link_local = _ADDRESS_FAMILY_INVALID,
.ipv6ll_address_gen_mode = _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID,
.ip_forwarding = { -1, -1, },
.ipv4_accept_local = -1,
.ipv4_route_localnet = -1,
.ipv6_privacy_extensions = _IPV6_PRIVACY_EXTENSIONS_INVALID,
+1 -1
View File
@@ -320,7 +320,7 @@ struct Network {
int ipoib_umcast;
/* sysctl settings */
AddressFamily ip_forward;
int ip_forwarding[2];
int ipv4_accept_local;
int ipv4_route_localnet;
int ipv6_dad_transmits;
+1 -1
View File
@@ -840,7 +840,7 @@ int route_section_verify_nexthops(Route *route) {
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
if (route->nexthop.family == AF_INET6 && !route->network->ipv6_accept_ra)
if (route->nexthop.family == AF_INET6 && route->network->ipv6_accept_ra == 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: Gateway=\"_ipv6ra\" is specified but IPv6AcceptRA= is disabled. "
"Ignoring [Route] section from line %u.",
+94 -41
View File
@@ -4,6 +4,7 @@
#include <linux/if.h>
#include <linux/if_arp.h>
#include "af-list.h"
#include "missing_network.h"
#include "networkd-link.h"
#include "networkd-manager.h"
@@ -13,6 +14,40 @@
#include "string-table.h"
#include "sysctl-util.h"
static void manager_set_ip_forwarding(Manager *manager, int family) {
int r, t;
assert(manager);
assert(IN_SET(family, AF_INET, AF_INET6));
if (family == AF_INET6 && !socket_ipv6_is_supported())
return;
t = manager->ip_forwarding[family == AF_INET6];
if (t < 0)
return; /* keep */
/* First, set the default value. */
r = sysctl_write_ip_property_boolean(family, "default", "forwarding", t);
if (r < 0)
log_warning_errno(r, "Failed to %s the default %s forwarding: %m",
enable_disable(t), af_to_ipv4_ipv6(family));
/* Then, set the value to all interfaces. */
r = sysctl_write_ip_property_boolean(family, "all", "forwarding", t);
if (r < 0)
log_warning_errno(r, "Failed to %s %s forwarding for all interfaces: %m",
enable_disable(t), af_to_ipv4_ipv6(family));
}
void manager_set_sysctl(Manager *manager) {
assert(manager);
assert(!manager->test_mode);
manager_set_ip_forwarding(manager, AF_INET);
manager_set_ip_forwarding(manager, AF_INET6);
}
static bool link_is_configured_for_family(Link *link, int family) {
assert(link);
@@ -70,48 +105,50 @@ static int link_set_proxy_arp_pvlan(Link *link) {
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp_pvlan", link->network->proxy_arp_pvlan > 0);
}
static bool link_ip_forward_enabled(Link *link, int family) {
int link_get_ip_forwarding(Link *link, int family) {
assert(link);
assert(link->manager);
assert(link->network);
assert(IN_SET(family, AF_INET, AF_INET6));
/* If it is explicitly specified, then honor the setting. */
int t = link->network->ip_forwarding[family == AF_INET6];
if (t >= 0)
return t;
/* If IPMasquerade= is enabled, also enable IP forwarding. */
if (family == AF_INET && FLAGS_SET(link->network->ip_masquerade, ADDRESS_FAMILY_IPV4))
return true;
if (family == AF_INET6 && FLAGS_SET(link->network->ip_masquerade, ADDRESS_FAMILY_IPV6))
return true;
/* If IPv6SendRA= is enabled, also enable IPv6 forwarding. */
if (family == AF_INET6 && link_radv_enabled(link))
return true;
/* Otherwise, use the global setting. */
return link->manager->ip_forwarding[family == AF_INET6];
}
static int link_set_ip_forwarding(Link *link, int family) {
int r, t;
assert(link);
assert(IN_SET(family, AF_INET, AF_INET6));
if (!link_is_configured_for_family(link, family))
return false;
return link->network->ip_forward & (family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6);
}
static int link_set_ipv4_forward(Link *link) {
assert(link);
if (!link_ip_forward_enabled(link, AF_INET))
return 0;
/* We propagate the forwarding flag from one interface to the
* global setting one way. This means: as long as at least one
* interface was configured at any time that had IP forwarding
* enabled the setting will stay on for good. We do this
* primarily to keep IPv4 and IPv6 packet forwarding behaviour
* somewhat in sync (see below). */
t = link_get_ip_forwarding(link, family);
if (t < 0)
return 0; /* keep */
return sysctl_write_ip_property(AF_INET, NULL, "ip_forward", "1");
}
r = sysctl_write_ip_property_boolean(family, link->ifname, "forwarding", t);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to %s %s forwarding, ignoring: %m",
enable_disable(t), af_to_ipv4_ipv6(family));
static int link_set_ipv6_forward(Link *link) {
assert(link);
if (!link_ip_forward_enabled(link, AF_INET6))
return 0;
/* On Linux, the IPv6 stack does not know a per-interface
* packet forwarding setting: either packet forwarding is on
* for all, or off for all. We hence don't bother with a
* per-interface setting, but simply propagate the interface
* flag, if it is set, to the global flag, one-way. Note that
* while IPv4 would allow a per-interface flag, we expose the
* same behaviour there and also propagate the setting from
* one to all, to keep things simple (see above). */
return sysctl_write_ip_property(AF_INET6, "all", "forwarding", "1");
return 0;
}
static int link_set_ipv4_rp_filter(Link *link) {
@@ -291,13 +328,8 @@ int link_set_sysctl(Link *link) {
if (r < 0)
log_link_warning_errno(link, r, "Cannot configure proxy ARP private VLAN for interface, ignoring: %m");
r = link_set_ipv4_forward(link);
if (r < 0)
log_link_warning_errno(link, r, "Cannot turn on IPv4 packet forwarding, ignoring: %m");
r = link_set_ipv6_forward(link);
if (r < 0)
log_link_warning_errno(link, r, "Cannot configure IPv6 packet forwarding, ignoring: %m");
(void) link_set_ip_forwarding(link, AF_INET);
(void) link_set_ip_forwarding(link, AF_INET6);
r = link_set_ipv6_privacy_extensions(link);
if (r < 0)
@@ -371,3 +403,24 @@ static const char* const ip_reverse_path_filter_table[_IP_REVERSE_PATH_FILTER_MA
DEFINE_STRING_TABLE_LOOKUP(ip_reverse_path_filter, IPReversePathFilter);
DEFINE_CONFIG_PARSE_ENUM(config_parse_ip_reverse_path_filter, ip_reverse_path_filter, IPReversePathFilter,
"Failed to parse IP reverse path filter option");
int config_parse_ip_forward_deprecated(
const char* unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
assert(filename);
log_syntax(unit, LOG_WARNING, filename, line, 0,
"IPForward= setting is deprecated. "
"Please use IPv4Forwarding= and/or IPv6Forwarding= in networkd.conf for global setting, "
"and the same settings in .network files for per-interface setting.");
return 0;
}
+5
View File
@@ -6,6 +6,7 @@
#include "conf-parser.h"
typedef struct Link Link;
typedef struct Manager Manager;
typedef enum IPv6PrivacyExtensions {
/* These values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values. Do not reorder! */
@@ -26,6 +27,9 @@ typedef enum IPReversePathFilter {
_IP_REVERSE_PATH_FILTER_INVALID = -EINVAL,
} IPReversePathFilter;
void manager_set_sysctl(Manager *manager);
int link_get_ip_forwarding(Link *link, int family);
int link_set_sysctl(Link *link);
int link_set_ipv6_mtu(Link *link);
@@ -37,3 +41,4 @@ IPReversePathFilter ip_reverse_path_filter_from_string(const char *s) _pure_;
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_privacy_extensions);
CONFIG_PARSER_PROTOTYPE(config_parse_ip_reverse_path_filter);
CONFIG_PARSER_PROTOTYPE(config_parse_ip_forward_deprecated);
-42
View File
@@ -116,48 +116,6 @@ DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_deprecated_address_family, AddressFa
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(ip_masquerade_address_family, AddressFamily);
DEFINE_STRING_TABLE_LOOKUP(dhcp_lease_server_type, sd_dhcp_lease_server_type_t);
int config_parse_address_family_with_kernel(
const char* unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
AddressFamily *fwd = data, s;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
/* This function is mostly obsolete now. It simply redirects
* "kernel" to "no". In older networkd versions we used to
* distinguish IPForward=off from IPForward=kernel, where the
* former would explicitly turn off forwarding while the
* latter would simply not touch the setting. But that logic
* is gone, hence silently accept the old setting, but turn it
* to "no". */
s = address_family_from_string(rvalue);
if (s < 0) {
if (streq(rvalue, "kernel"))
s = ADDRESS_FAMILY_NO;
else {
log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse IPForward= option, ignoring: %s", rvalue);
return 0;
}
}
*fwd = s;
return 0;
}
int config_parse_ip_masquerade(
const char *unit,
const char *filename,
-1
View File
@@ -52,7 +52,6 @@ static inline uint32_t usec_to_sec(usec_t usec, usec_t now_usec) {
}
CONFIG_PARSER_PROTOTYPE(config_parse_link_local_address_family);
CONFIG_PARSER_PROTOTYPE(config_parse_address_family_with_kernel);
CONFIG_PARSER_PROTOTYPE(config_parse_ip_masquerade);
CONFIG_PARSER_PROTOTYPE(config_parse_mud_url);