diff --git a/src/network/networkd-ipv6-proxy-ndp.c b/src/network/networkd-ipv6-proxy-ndp.c index 2a45dd94895..98d42055478 100644 --- a/src/network/networkd-ipv6-proxy-ndp.c +++ b/src/network/networkd-ipv6-proxy-ndp.c @@ -10,8 +10,9 @@ #include "networkd-link.h" #include "networkd-manager.h" #include "networkd-network.h" -#include "string-util.h" #include "socket-util.h" +#include "string-util.h" +#include "sysctl-util.h" static bool ipv6_proxy_ndp_is_needed(Link *link) { assert(link); @@ -32,8 +33,8 @@ static bool ipv6_proxy_ndp_is_needed(Link *link) { } static int ipv6_proxy_ndp_set(Link *link) { - const char *p = NULL; - int r, v; + bool v; + int r; assert(link); @@ -41,9 +42,8 @@ static int ipv6_proxy_ndp_set(Link *link) { return 0; v = ipv6_proxy_ndp_is_needed(link); - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/proxy_ndp"); - r = write_string_file(p, one_zero(v), WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v); if (r < 0) log_link_warning_errno(link, r, "Cannot configure proxy NDP for interface: %m"); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 2aea0b839c1..8bca0428891 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -29,6 +29,7 @@ #include "stdio-util.h" #include "string-table.h" #include "strv.h" +#include "sysctl-util.h" #include "tmpfile-util.h" #include "util.h" #include "virt.h" @@ -73,6 +74,9 @@ static bool link_dhcp6_enabled(Link *link) { if (link->network->bond) return false; + if (manager_sysctl_ipv6_enabled(link->manager) == 0) + return false; + return link->network->dhcp & ADDRESS_FAMILY_IPV6; } @@ -142,6 +146,9 @@ static bool link_ipv6ll_enabled(Link *link) { if (link->network->bond) return false; + if (manager_sysctl_ipv6_enabled(link->manager) == 0) + return false; + return link->network->link_local & ADDRESS_FAMILY_IPV6; } @@ -154,6 +161,9 @@ static bool link_ipv6_enabled(Link *link) { if (link->network->bridge || link->network->bond) return false; + if (manager_sysctl_ipv6_enabled(link->manager) == 0) + return false; + /* DHCPv6 client will not be started if no IPv6 link-local address is configured. */ return link_ipv6ll_enabled(link) || network_has_static_ipv6_addresses(link->network); } @@ -233,6 +243,9 @@ static bool link_ipv6_forward_enabled(Link *link) { if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID) return false; + if (manager_sysctl_ipv6_enabled(link->manager) == 0) + return false; + return link->network->ip_forward & ADDRESS_FAMILY_IPV6; } @@ -297,7 +310,6 @@ static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) { } static int link_enable_ipv6(Link *link) { - const char *p = NULL; bool disabled; int r; @@ -306,9 +318,7 @@ static int link_enable_ipv6(Link *link) { disabled = !link_ipv6_enabled(link); - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/disable_ipv6"); - - r = write_string_file(p, one_zero(disabled), WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", disabled); if (r < 0) log_link_warning_errno(link, r, "Cannot %s IPv6 for interface %s: %m", enable_disable(!disabled), link->ifname); @@ -1330,15 +1340,12 @@ static int link_set_bridge_vlan(Link *link) { } static int link_set_proxy_arp(Link *link) { - const char *p = NULL; int r; if (!link_proxy_arp_enabled(link)) return 0; - p = strjoina("/proc/sys/net/ipv4/conf/", link->ifname, "/proxy_arp"); - - r = write_string_file(p, one_zero(link->network->proxy_arp), WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", link->network->proxy_arp > 0); if (r < 0) log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface: %m"); @@ -1912,20 +1919,12 @@ static int link_configure_addrgen_mode(Link *link) { if (!link_ipv6ll_enabled(link)) ipv6ll_mode = IN6_ADDR_GEN_MODE_NONE; - else { - const char *p = NULL; - _cleanup_free_ char *stable_secret = NULL; - - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/stable_secret"); - + else if (sysctl_read_ip_property(AF_INET6, link->ifname, "stable_secret", NULL) < 0) /* The file may not exist. And event if it exists, when stable_secret is unset, - * then reading the file fails and EIO is returned. */ - r = read_one_line_file(p, &stable_secret); - if (r < 0) - ipv6ll_mode = IN6_ADDR_GEN_MODE_EUI64; - else - ipv6ll_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY; - } + * reading the file fails with EIO. */ + ipv6ll_mode = IN6_ADDR_GEN_MODE_EUI64; + else + ipv6ll_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY; r = sd_netlink_message_append_u8(req, IFLA_INET6_ADDR_GEN_MODE, ipv6ll_mode); if (r < 0) @@ -2653,7 +2652,7 @@ static int link_set_ipv4_forward(Link *link) { * primarily to keep IPv4 and IPv6 packet forwarding behaviour * somewhat in sync (see below). */ - r = write_string_file("/proc/sys/net/ipv4/ip_forward", "1", WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property(AF_INET, NULL, "ip_forward", "1"); if (r < 0) log_link_warning_errno(link, r, "Cannot turn on IPv4 packet forwarding, ignoring: %m"); @@ -2675,7 +2674,7 @@ static int link_set_ipv6_forward(Link *link) { * same behaviour there and also propagate the setting from * one to all, to keep things simple (see above). */ - r = write_string_file("/proc/sys/net/ipv6/conf/all/forwarding", "1", WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property(AF_INET6, "all", "forwarding", "1"); if (r < 0) log_link_warning_errno(link, r, "Cannot configure IPv6 packet forwarding, ignoring: %m"); @@ -2683,19 +2682,14 @@ static int link_set_ipv6_forward(Link *link) { } static int link_set_ipv6_privacy_extensions(Link *link) { - char buf[DECIMAL_STR_MAX(unsigned) + 1]; IPv6PrivacyExtensions s; - const char *p = NULL; int r; s = link_ipv6_privacy_extensions(link); if (s < 0) return 0; - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/use_tempaddr"); - xsprintf(buf, "%u", (unsigned) link->network->ipv6_privacy_extensions); - - r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property_int(AF_INET6, link->ifname, "use_tempaddr", (int) link->network->ipv6_privacy_extensions); if (r < 0) log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extension for interface: %m"); @@ -2703,7 +2697,6 @@ static int link_set_ipv6_privacy_extensions(Link *link) { } static int link_set_ipv6_accept_ra(Link *link) { - const char *p = NULL; int r; /* Make this a NOP if IPv6 is not available */ @@ -2716,10 +2709,7 @@ static int link_set_ipv6_accept_ra(Link *link) { if (!link->network) return 0; - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/accept_ra"); - - /* We handle router advertisements ourselves, tell the kernel to GTFO */ - r = write_string_file(p, "0", WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0"); if (r < 0) log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface: %m"); @@ -2727,8 +2717,6 @@ static int link_set_ipv6_accept_ra(Link *link) { } static int link_set_ipv6_dad_transmits(Link *link) { - char buf[DECIMAL_STR_MAX(int) + 1]; - const char *p = NULL; int r; /* Make this a NOP if IPv6 is not available */ @@ -2744,10 +2732,7 @@ static int link_set_ipv6_dad_transmits(Link *link) { if (link->network->ipv6_dad_transmits < 0) return 0; - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/dad_transmits"); - xsprintf(buf, "%i", link->network->ipv6_dad_transmits); - - r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property_int(AF_INET6, link->ifname, "dad_transmits", link->network->ipv6_dad_transmits); if (r < 0) log_link_warning_errno(link, r, "Cannot set IPv6 dad transmits for interface: %m"); @@ -2755,8 +2740,6 @@ static int link_set_ipv6_dad_transmits(Link *link) { } static int link_set_ipv6_hop_limit(Link *link) { - char buf[DECIMAL_STR_MAX(int) + 1]; - const char *p = NULL; int r; /* Make this a NOP if IPv6 is not available */ @@ -2772,10 +2755,7 @@ static int link_set_ipv6_hop_limit(Link *link) { if (link->network->ipv6_hop_limit < 0) return 0; - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/hop_limit"); - xsprintf(buf, "%i", link->network->ipv6_hop_limit); - - r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property_int(AF_INET6, link->ifname, "hop_limit", link->network->ipv6_hop_limit); if (r < 0) log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface: %m"); @@ -2783,8 +2763,6 @@ static int link_set_ipv6_hop_limit(Link *link) { } static int link_set_ipv6_mtu(Link *link) { - char buf[DECIMAL_STR_MAX(unsigned) + 1]; - const char *p = NULL; int r; /* Make this a NOP if IPv6 is not available */ @@ -2797,11 +2775,7 @@ static int link_set_ipv6_mtu(Link *link) { if (link->network->ipv6_mtu == 0) return 0; - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/mtu"); - - xsprintf(buf, "%" PRIu32, link->network->ipv6_mtu); - - r = write_string_file(p, buf, WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", link->network->ipv6_mtu); if (r < 0) log_link_warning_errno(link, r, "Cannot set IPv6 MTU for interface: %m"); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index b7e15a56992..edb5801b692 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -24,6 +24,7 @@ #include "path-util.h" #include "set.h" #include "strv.h" +#include "sysctl-util.h" #include "tmpfile-util.h" #include "virt.h" @@ -1379,6 +1380,8 @@ int manager_new(Manager **ret) { if (!m->state_file) return -ENOMEM; + m->sysctl_ipv6_enabled = -1; + r = sd_event_default(&m->event); if (r < 0) return r; @@ -1877,3 +1880,18 @@ int manager_request_product_uuid(Manager *m, Link *link) { return 0; } + +int manager_sysctl_ipv6_enabled(Manager *manager) { + _cleanup_free_ char *value = NULL; + int r; + + if (manager->sysctl_ipv6_enabled >= 0) + return manager->sysctl_ipv6_enabled; + + r = sysctl_read_ip_property(AF_INET6, "all", "disable_ipv6", &value); + if (r < 0) + return log_warning_errno(r, "Failed to read net.ipv6.conf.all.disable_ipv6 sysctl property: %m"); + + manager->sysctl_ipv6_enabled = value[0] == '0'; + return manager->sysctl_ipv6_enabled; +} diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 33f80bf19f5..35ab6bedb17 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -56,6 +56,8 @@ struct Manager { Set *rules; Set *rules_foreign; Set *rules_saved; + + int sysctl_ipv6_enabled; }; extern const sd_bus_vtable manager_vtable[]; @@ -93,4 +95,6 @@ Link *manager_dhcp6_prefix_get(Manager *m, struct in6_addr *addr); int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link); int manager_dhcp6_prefix_remove_all(Manager *m, Link *link); +int manager_sysctl_ipv6_enabled(Manager *manager); + DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); diff --git a/src/shared/sysctl-util.c b/src/shared/sysctl-util.c index 480e6c38a11..9be4055b376 100644 --- a/src/shared/sysctl-util.c +++ b/src/shared/sysctl-util.c @@ -60,6 +60,22 @@ int sysctl_write(const char *property, const char *value) { return 0; } +int sysctl_write_ip_property(int af, const char *ifname, const char *property, const char *value) { + const char *p; + + assert(IN_SET(af, AF_INET, AF_INET6)); + assert(property); + assert(value); + + p = strjoina("/proc/sys/net/ipv", af == AF_INET ? "4" : "6", + ifname ? "/conf/" : "", strempty(ifname), + property[0] == '/' ? "" : "/", property); + + log_debug("Setting '%s' to '%s'", p, value); + + return write_string_file(p, value, WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER); +} + int sysctl_read(const char *property, char **content) { char *p; @@ -69,3 +85,25 @@ int sysctl_read(const char *property, char **content) { p = strjoina("/proc/sys/", property); return read_full_file(p, content, NULL); } + +int sysctl_read_ip_property(int af, const char *ifname, const char *property, char **ret) { + _cleanup_free_ char *value = NULL; + const char *p; + int r; + + assert(IN_SET(af, AF_INET, AF_INET6)); + assert(property); + + p = strjoina("/proc/sys/net/ipv", af == AF_INET ? "4" : "6", + ifname ? "/conf/" : "", strempty(ifname), + property[0] == '/' ? "" : "/", property); + + r = read_one_line_file(p, &value); + if (r < 0) + return r; + + if (ret) + *ret = TAKE_PTR(value); + + return r; +} diff --git a/src/shared/sysctl-util.h b/src/shared/sysctl-util.h index fd7c78b2b8d..d50c6e43816 100644 --- a/src/shared/sysctl-util.h +++ b/src/shared/sysctl-util.h @@ -1,7 +1,29 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include +#include + +#include "macro.h" +#include "stdio-util.h" +#include "util.h" + char *sysctl_normalize(char *s); int sysctl_read(const char *property, char **value); int sysctl_write(const char *property, const char *value); +int sysctl_read_ip_property(int af, const char *ifname, const char *property, char **ret); +int sysctl_write_ip_property(int af, const char *ifname, const char *property, const char *value); +static inline int sysctl_write_ip_property_boolean(int af, const char *ifname, const char *property, bool value) { + return sysctl_write_ip_property(af, ifname, property, one_zero(value)); +} + +#define DEFINE_SYSCTL_WRITE_IP_PROPERTY(name, type, format) \ + static inline int sysctl_write_ip_property_##name(int af, const char *ifname, const char *property, type value) { \ + char buf[DECIMAL_STR_MAX(type)]; \ + xsprintf(buf, format, value); \ + return sysctl_write_ip_property(af, ifname, property, buf); \ + } + +DEFINE_SYSCTL_WRITE_IP_PROPERTY(int, int, "%i"); +DEFINE_SYSCTL_WRITE_IP_PROPERTY(uint32, uint32_t, "%" PRIu32); diff --git a/test/test-network/conf/25-sysctl-disable-ipv6.network b/test/test-network/conf/25-sysctl-disable-ipv6.network new file mode 100644 index 00000000000..e52078e0d4c --- /dev/null +++ b/test/test-network/conf/25-sysctl-disable-ipv6.network @@ -0,0 +1,6 @@ +[Match] +Name=dummy98 + +[Network] +IPv6AcceptRA=no +Address=10.2.3.4/16 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 797ba7c4b26..dae370c0175 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -117,6 +117,7 @@ class Utilities(): return f.readline().strip() def copy_unit_to_networkd_unit_path(self, *units): + print() for unit in units: shutil.copy(os.path.join(networkd_ci_path, unit), network_unit_file_path) if (os.path.exists(os.path.join(networkd_ci_path, unit + '.d'))): @@ -172,7 +173,6 @@ class Utilities(): else: subprocess.check_call('systemctl restart systemd-networkd', shell=True) time.sleep(5) - print() class NetworkdNetDevTests(unittest.TestCase, Utilities): @@ -598,6 +598,7 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): '25-route-section.network', '25-route-tcp-window-settings.network', '25-route-type.network', + '25-sysctl-disable-ipv6.network', '25-sysctl.network', 'configure-without-carrier.network', 'routing-policy-rule.network', @@ -960,6 +961,45 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): self.assertEqual(self.read_ipv4_sysctl_attr('dummy98', 'forwarding'),'1') self.assertEqual(self.read_ipv4_sysctl_attr('dummy98', 'proxy_arp'), '1') + def test_sysctl_disable_ipv6(self): + self.copy_unit_to_networkd_unit_path('25-sysctl-disable-ipv6.network', '12-dummy.netdev') + + print('## Disable ipv6') + self.assertEqual(subprocess.call(['sysctl', 'net.ipv6.conf.all.disable_ipv6=1']), 0) + self.assertEqual(subprocess.call(['sysctl', 'net.ipv6.conf.default.disable_ipv6=1']), 0) + + self.start_networkd() + + self.assertTrue(self.link_exits('dummy98')) + + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dummy98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98') + output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dummy98']).rstrip().decode('utf-8') + print(output) + self.assertEqual(output, '') + output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8') + self.assertRegex(output, 'State: routable \(configured\)') + + self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0) + + print('## Enable ipv6') + self.assertEqual(subprocess.call(['sysctl', 'net.ipv6.conf.all.disable_ipv6=0']), 0) + self.assertEqual(subprocess.call(['sysctl', 'net.ipv6.conf.default.disable_ipv6=0']), 0) + + self.start_networkd() + + self.assertTrue(self.link_exits('dummy98')) + + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dummy98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98') + output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dummy98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'inet6 .* scope link') + output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8') + self.assertRegex(output, 'State: routable \(configured\)') + def test_bind_carrier(self): self.copy_unit_to_networkd_unit_path('25-bind-carrier.network', '11-dummy.netdev') self.start_networkd()