network: add MPLSRouting= setting to enable MPLS routing (#35495)
Closing #35487.
This commit is contained in:
@@ -1148,6 +1148,24 @@ DuplicateAddressDetection=none</programlisting></para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>MPLSRouting=</varname></term>
|
||||
<listitem>
|
||||
<para>Control whether Multi-protocol Label Switching (MPLS) routing is enabled on this interface.
|
||||
This configures <filename>/proc/sys/net/mpls/conf/<replaceable>INTERFACE</replaceable>/input</filename>.
|
||||
Takes a boolean. Defaults to unset, and the kernel's default will be used.</para>
|
||||
|
||||
<para>Note, <command>systemd-networkd</command> does <emphasis>not</emphasis> load any required
|
||||
kernel modules for MPLS. To enable the feature, <filename>mpls_router</filename> kernel module must
|
||||
be loaded before <filename>systemd-networkd.service</filename> is started. Consider adding the
|
||||
kernel module to
|
||||
<citerefentry><refentrytitle>modules-load.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
||||
</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>KeepMaster=</varname></term>
|
||||
<listitem>
|
||||
|
||||
+13
-6
@@ -115,22 +115,29 @@ int sysctl_writef(const char *property, const char *format, ...) {
|
||||
return sysctl_write(property, v);
|
||||
}
|
||||
|
||||
static const char* af_to_sysctl_dir(int af) {
|
||||
if (af == AF_MPLS)
|
||||
return "mpls";
|
||||
|
||||
return af_to_ipv4_ipv6(af);
|
||||
}
|
||||
|
||||
int sysctl_write_ip_property(int af, const char *ifname, const char *property, const char *value, Hashmap **shadow) {
|
||||
const char *p;
|
||||
|
||||
assert(property);
|
||||
assert(value);
|
||||
|
||||
if (!IN_SET(af, AF_INET, AF_INET6))
|
||||
if (!IN_SET(af, AF_INET, AF_INET6, AF_MPLS))
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
if (ifname) {
|
||||
if (!ifname_valid_full(ifname, IFNAME_VALID_SPECIAL))
|
||||
return -EINVAL;
|
||||
|
||||
p = strjoina("net/", af_to_ipv4_ipv6(af), "/conf/", ifname, "/", property);
|
||||
p = strjoina("net/", af_to_sysctl_dir(af), "/conf/", ifname, "/", property);
|
||||
} else
|
||||
p = strjoina("net/", af_to_ipv4_ipv6(af), "/", property);
|
||||
p = strjoina("net/", af_to_sysctl_dir(af), "/", property);
|
||||
|
||||
return sysctl_write_full(p, value, shadow);
|
||||
}
|
||||
@@ -181,16 +188,16 @@ int sysctl_read_ip_property(int af, const char *ifname, const char *property, ch
|
||||
|
||||
assert(property);
|
||||
|
||||
if (!IN_SET(af, AF_INET, AF_INET6))
|
||||
if (!IN_SET(af, AF_INET, AF_INET6, AF_MPLS))
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
if (ifname) {
|
||||
if (!ifname_valid_full(ifname, IFNAME_VALID_SPECIAL))
|
||||
return -EINVAL;
|
||||
|
||||
p = strjoina("net/", af_to_ipv4_ipv6(af), "/conf/", ifname, "/", property);
|
||||
p = strjoina("net/", af_to_sysctl_dir(af), "/conf/", ifname, "/", property);
|
||||
} else
|
||||
p = strjoina("net/", af_to_ipv4_ipv6(af), "/", property);
|
||||
p = strjoina("net/", af_to_sysctl_dir(af), "/", property);
|
||||
|
||||
return sysctl_read(p, ret);
|
||||
}
|
||||
|
||||
@@ -151,6 +151,7 @@ Network.ProxyARP, config_parse_tristate,
|
||||
Network.IPv6ProxyNDPAddress, config_parse_ipv6_proxy_ndp_address, 0, 0
|
||||
Network.IPv4ReversePathFilter, config_parse_ip_reverse_path_filter, 0, offsetof(Network, ipv4_rp_filter)
|
||||
Network.MulticastIGMPVersion, config_parse_ipv4_force_igmp_version, 0, offsetof(Network, ipv4_force_igmp_version)
|
||||
Network.MPLSRouting, config_parse_tristate, 0, offsetof(Network, mpls_input)
|
||||
Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier)
|
||||
Network.ConfigureWithoutCarrier, config_parse_bool, 0, offsetof(Network, configure_without_carrier)
|
||||
Network.IgnoreCarrierLoss, config_parse_ignore_carrier_loss, 0, 0
|
||||
|
||||
@@ -482,6 +482,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
|
||||
.proxy_arp_pvlan = -1,
|
||||
.ipv4_rp_filter = _IP_REVERSE_PATH_FILTER_INVALID,
|
||||
.ipv4_force_igmp_version = _IPV4_FORCE_IGMP_VERSION_INVALID,
|
||||
.mpls_input = -1,
|
||||
|
||||
.ndisc = -1,
|
||||
.ndisc_use_redirect = true,
|
||||
|
||||
@@ -343,6 +343,7 @@ struct Network {
|
||||
IPv4ForceIgmpVersion ipv4_force_igmp_version;
|
||||
int ipv6_proxy_ndp;
|
||||
Set *ipv6_proxy_ndp_addresses;
|
||||
int mpls_input;
|
||||
|
||||
/* NDisc support */
|
||||
int ndisc;
|
||||
|
||||
@@ -243,7 +243,7 @@ static bool link_is_configured_for_family(Link *link, int family) {
|
||||
/* CAN devices do not support IP layer. Most of the functions below are never called for CAN devices,
|
||||
* but link_set_ipv6_mtu() may be called after setting interface MTU, and warn about the failure. For
|
||||
* safety, let's unconditionally check if the interface is not a CAN device. */
|
||||
if (IN_SET(family, AF_INET, AF_INET6) && link->iftype == ARPHRD_CAN)
|
||||
if (IN_SET(family, AF_INET, AF_INET6, AF_MPLS) && link->iftype == ARPHRD_CAN)
|
||||
return false;
|
||||
|
||||
if (family == AF_INET6 && !socket_ipv6_is_supported())
|
||||
@@ -671,6 +671,19 @@ static int link_set_ipv4_promote_secondaries(Link *link) {
|
||||
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "promote_secondaries", true, manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
static int link_set_mpls_input(Link *link) {
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
|
||||
if (!link_is_configured_for_family(link, AF_MPLS))
|
||||
return 0;
|
||||
|
||||
if (link->network->mpls_input < 0)
|
||||
return 0;
|
||||
|
||||
return sysctl_write_ip_property_boolean(AF_MPLS, link->ifname, "input", link->network->mpls_input > 0, manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
int link_set_sysctl(Link *link) {
|
||||
int r;
|
||||
|
||||
@@ -743,6 +756,10 @@ int link_set_sysctl(Link *link) {
|
||||
if (r < 0)
|
||||
log_link_warning_errno(link, r, "Cannot enable promote_secondaries for interface, ignoring: %m");
|
||||
|
||||
r = link_set_mpls_input(link);
|
||||
if (r < 0)
|
||||
log_link_warning_errno(link, r, "Cannot set MPLS input, ignoring: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Match]
|
||||
Name=dummy98
|
||||
|
||||
[Network]
|
||||
IPv6AcceptRA=no
|
||||
MPLSRouting=yes
|
||||
@@ -732,6 +732,9 @@ def read_ipv6_neigh_sysctl_attr(link, attribute):
|
||||
def read_ipv4_sysctl_attr(link, attribute):
|
||||
return read_ip_sysctl_attr(link, attribute, 'ipv4')
|
||||
|
||||
def read_mpls_sysctl_attr(link, attribute):
|
||||
return read_ip_sysctl_attr(link, attribute, 'mpls')
|
||||
|
||||
def stop_by_pid_file(pid_file):
|
||||
if not os.path.exists(pid_file):
|
||||
return
|
||||
@@ -1117,6 +1120,9 @@ class Utilities():
|
||||
def check_ipv6_neigh_sysctl_attr(self, link, attribute, expected):
|
||||
self.assertEqual(read_ipv6_neigh_sysctl_attr(link, attribute), expected)
|
||||
|
||||
def check_mpls_sysctl_attr(self, link, attribute, expected):
|
||||
self.assertEqual(read_mpls_sysctl_attr(link, attribute), expected)
|
||||
|
||||
def wait_links(self, *links, trial=40):
|
||||
for _ in range(trial):
|
||||
if link_exists(*links):
|
||||
@@ -4568,6 +4574,15 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
|
||||
print(output)
|
||||
self.assertRegex(output, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
|
||||
|
||||
@expectedFailureIfModuleIsNotAvailable('mpls_router')
|
||||
def test_sysctl_mpls(self):
|
||||
check_output('modprobe mpls_router')
|
||||
copy_network_unit('25-sysctl-mpls.network', '12-dummy.netdev')
|
||||
start_networkd()
|
||||
self.wait_online('dummy98:degraded')
|
||||
|
||||
self.check_mpls_sysctl_attr('dummy98', 'input', '1')
|
||||
|
||||
def test_bind_carrier(self):
|
||||
copy_network_unit('25-bind-carrier.network', '11-dummy.netdev')
|
||||
start_networkd()
|
||||
|
||||
Reference in New Issue
Block a user