From b8b0c1a065395e42749b0e1aaef82e5f457468d8 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 29 Oct 2024 01:23:54 +0900 Subject: [PATCH 1/2] network: update tunnel or vxlan interface if the local address is changed If a tunnel or vxlan is configured with Local=dhcp4 or so, then the local address needs to be changed when it is changed. Fixes #24854. --- src/network/netdev/netdev.c | 11 +++++++++++ src/network/netdev/netdev.h | 6 ++++++ src/network/netdev/tunnel.c | 18 ++++++++++++++++++ src/network/netdev/vxlan.c | 9 +++++++++ src/network/networkd-dhcp4.c | 4 ++++ src/network/networkd-dhcp6.c | 4 ++++ src/network/networkd-ipv4ll.c | 4 ++++ src/network/networkd-link.c | 16 ++++++++++++++-- src/network/networkd-link.h | 3 +++ src/network/networkd-ndisc.c | 2 ++ 10 files changed, 75 insertions(+), 2 deletions(-) diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index 1e046397d50..85760c741d0 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -413,6 +413,17 @@ int netdev_enter_ready(NetDev *netdev) { return 0; } +bool netdev_needs_reconfigure(NetDev *netdev, NetDevLocalAddressType type) { + assert(netdev); + assert(type < _NETDEV_LOCAL_ADDRESS_TYPE_MAX); + + if (type < 0) + return true; + + return NETDEV_VTABLE(netdev)->needs_reconfigure && + NETDEV_VTABLE(netdev)->needs_reconfigure(netdev, type); +} + /* callback for netdev's created without a backing Link */ static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) { int r; diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h index 106f47acada..765496d0447 100644 --- a/src/network/netdev/netdev.h +++ b/src/network/netdev/netdev.h @@ -8,6 +8,7 @@ #include "hash-funcs.h" #include "list.h" #include "log-link.h" +#include "netdev-util.h" #include "networkd-link.h" #include "time-util.h" @@ -186,6 +187,10 @@ typedef struct NetDevVTable { /* provides if MTU can be set. If this is not set, assumed to be yes. */ bool (*can_set_mtu)(NetDev *netdev, uint32_t mtu); + /* provides if the netdev needs to be reconfigured when a specified type of address on the underlying + * interface is updated. */ + bool (*needs_reconfigure)(NetDev *netdev, NetDevLocalAddressType type); + /* expected iftype, e.g. ARPHRD_ETHER. */ uint16_t iftype; @@ -237,6 +242,7 @@ int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *newlink); int netdev_generate_hw_addr(NetDev *netdev, Link *link, const char *name, const struct hw_addr_data *hw_addr, struct hw_addr_data *ret); +bool netdev_needs_reconfigure(NetDev *netdev, NetDevLocalAddressType type); int link_request_stacked_netdev(Link *link, NetDev *netdev); const char* netdev_kind_to_string(NetDevKind d) _const_; diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c index 26f7f207da1..0339c49c8fa 100644 --- a/src/network/netdev/tunnel.c +++ b/src/network/netdev/tunnel.c @@ -708,6 +708,14 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) { return 0; } +static bool tunnel_needs_reconfigure(NetDev *netdev, NetDevLocalAddressType type) { + assert(type >= 0 && type < _NETDEV_LOCAL_ADDRESS_TYPE_MAX); + + Tunnel *t = ASSERT_PTR(TUNNEL(netdev)); + + return t->local_type == type; +} + static int unset_local(Tunnel *t) { assert(t); @@ -1119,6 +1127,7 @@ const NetDevVTable ipip_vtable = { .create_type = NETDEV_CREATE_STACKED, .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, + .needs_reconfigure = tunnel_needs_reconfigure, .iftype = ARPHRD_TUNNEL, }; @@ -1130,6 +1139,7 @@ const NetDevVTable sit_vtable = { .create_type = NETDEV_CREATE_STACKED, .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, + .needs_reconfigure = tunnel_needs_reconfigure, .iftype = ARPHRD_SIT, }; @@ -1141,6 +1151,7 @@ const NetDevVTable vti_vtable = { .create_type = NETDEV_CREATE_STACKED, .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, + .needs_reconfigure = tunnel_needs_reconfigure, .iftype = ARPHRD_TUNNEL, }; @@ -1152,6 +1163,7 @@ const NetDevVTable vti6_vtable = { .create_type = NETDEV_CREATE_STACKED, .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, + .needs_reconfigure = tunnel_needs_reconfigure, .iftype = ARPHRD_TUNNEL6, }; @@ -1163,6 +1175,7 @@ const NetDevVTable gre_vtable = { .create_type = NETDEV_CREATE_STACKED, .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, + .needs_reconfigure = tunnel_needs_reconfigure, .iftype = ARPHRD_IPGRE, }; @@ -1174,6 +1187,7 @@ const NetDevVTable gretap_vtable = { .create_type = NETDEV_CREATE_STACKED, .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, + .needs_reconfigure = tunnel_needs_reconfigure, .iftype = ARPHRD_ETHER, .generate_mac = true, }; @@ -1186,6 +1200,7 @@ const NetDevVTable ip6gre_vtable = { .create_type = NETDEV_CREATE_STACKED, .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, + .needs_reconfigure = tunnel_needs_reconfigure, .iftype = ARPHRD_IP6GRE, }; @@ -1197,6 +1212,7 @@ const NetDevVTable ip6gretap_vtable = { .create_type = NETDEV_CREATE_STACKED, .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, + .needs_reconfigure = tunnel_needs_reconfigure, .iftype = ARPHRD_ETHER, .generate_mac = true, }; @@ -1209,6 +1225,7 @@ const NetDevVTable ip6tnl_vtable = { .create_type = NETDEV_CREATE_STACKED, .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, + .needs_reconfigure = tunnel_needs_reconfigure, .iftype = ARPHRD_TUNNEL6, }; @@ -1220,6 +1237,7 @@ const NetDevVTable erspan_vtable = { .create_type = NETDEV_CREATE_STACKED, .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, + .needs_reconfigure = tunnel_needs_reconfigure, .iftype = ARPHRD_ETHER, .generate_mac = true, }; diff --git a/src/network/netdev/vxlan.c b/src/network/netdev/vxlan.c index 2c4aaecbf0c..9f22794d348 100644 --- a/src/network/netdev/vxlan.c +++ b/src/network/netdev/vxlan.c @@ -415,6 +415,14 @@ static int netdev_vxlan_verify(NetDev *netdev, const char *filename) { return 0; } +static bool vxlan_needs_reconfigure(NetDev *netdev, NetDevLocalAddressType type) { + assert(type >= 0 && type < _NETDEV_LOCAL_ADDRESS_TYPE_MAX); + + VxLan *v = VXLAN(netdev); + + return v->local_type == type; +} + static int netdev_vxlan_is_ready_to_create(NetDev *netdev, Link *link) { VxLan *v = VXLAN(netdev); @@ -445,6 +453,7 @@ const NetDevVTable vxlan_vtable = { .is_ready_to_create = netdev_vxlan_is_ready_to_create, .config_verify = netdev_vxlan_verify, .can_set_mtu = vxlan_can_set_mtu, + .needs_reconfigure = vxlan_needs_reconfigure, .iftype = ARPHRD_ETHER, .generate_mac = true, }; diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 2dd29bca948..bda3a561d94 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -326,6 +326,10 @@ int dhcp4_check_ready(Link *link) { if (r < 0) return r; + r = link_request_stacked_netdevs(link, NETDEV_LOCAL_ADDRESS_DHCP4); + if (r < 0) + return r; + r = sd_ipv4ll_stop(link->ipv4ll); if (r < 0) return log_link_warning_errno(link, r, "Failed to drop IPv4 link-local address: %m"); diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index b49f51f6843..1eb5138d5e0 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -122,6 +122,10 @@ int dhcp6_check_ready(Link *link) { if (r < 0) return r; + r = link_request_stacked_netdevs(link, NETDEV_LOCAL_ADDRESS_DHCP6); + if (r < 0) + return r; + link_check_ready(link); return 0; } diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 299aaedd070..ea960593bbc 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -109,6 +109,10 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { log_link_debug(link, "IPv4 link-local claim "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); + r = link_request_stacked_netdevs(link, NETDEV_LOCAL_ADDRESS_IPV4LL); + if (r < 0) + return r; + return link_request_address(link, address, NULL, ipv4ll_address_handler, NULL); } diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 4aa24a27e0b..be8826b49c5 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -640,15 +640,23 @@ static int link_request_static_configs(Link *link) { return 0; } -static int link_request_stacked_netdevs(Link *link) { +int link_request_stacked_netdevs(Link *link, NetDevLocalAddressType type) { NetDev *netdev; int r; assert(link); + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + return 0; + + assert(link->network); + link->stacked_netdevs_created = false; HASHMAP_FOREACH(netdev, link->network->stacked_netdevs) { + if (!netdev_needs_reconfigure(netdev, type)) + continue; + r = link_request_stacked_netdev(link, netdev); if (r < 0) return r; @@ -776,6 +784,10 @@ int link_ipv6ll_gained(Link *link) { if (r < 0) return r; + r = link_request_stacked_netdevs(link, NETDEV_LOCAL_ADDRESS_IPV6LL); + if (r < 0) + return r; + link_check_ready(link); return 0; } @@ -1188,7 +1200,7 @@ static int link_configure(Link *link) { if (r < 0) return r; - r = link_request_stacked_netdevs(link); + r = link_request_stacked_netdevs(link, _NETDEV_LOCAL_ADDRESS_TYPE_INVALID); if (r < 0) return r; diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 8ac7c355632..86aba9556bd 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -19,6 +19,7 @@ #include "ether-addr-util.h" #include "log-link.h" +#include "netdev.h" #include "netif-util.h" #include "network-util.h" #include "networkd-bridge-vlan.h" @@ -257,6 +258,8 @@ void link_free_engines(Link *link); const char* link_state_to_string(LinkState s) _const_; LinkState link_state_from_string(const char *s) _pure_; +int link_request_stacked_netdevs(Link *link, NetDevLocalAddressType type); + int link_reconfigure_impl(Link *link, bool force); int link_reconfigure(Link *link, bool force); int link_reconfigure_on_bus_method_reload(Link *link, sd_bus_message *message); diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 46f39547254..0773e9e8ca8 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -2141,6 +2141,8 @@ static int ndisc_drop_outdated(Link *link, const struct in6_addr *router, usec_t updated = true; } + RET_GATHER(ret, link_request_stacked_netdevs(link, NETDEV_LOCAL_ADDRESS_SLAAC)); + if (updated) link_dirty(link); From 80f38c1f65257887687291e12450bad32782773e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 29 Oct 2024 02:18:05 +0900 Subject: [PATCH 2/2] test-network: add test case for tunnel Local=dhcp4 For issue #24854. --- .../conf/25-dhcp-client-ipv4-only.network | 1 + test/test-network/conf/25-sit-dhcp4.netdev | 8 ++++++++ test/test-network/conf/25-sit-dhcp4.network | 4 ++++ test/test-network/systemd-networkd-tests.py | 15 +++++++++++++-- 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 test/test-network/conf/25-sit-dhcp4.netdev create mode 100644 test/test-network/conf/25-sit-dhcp4.network diff --git a/test/test-network/conf/25-dhcp-client-ipv4-only.network b/test/test-network/conf/25-dhcp-client-ipv4-only.network index 5e83bd24dc1..7d79ee3485e 100644 --- a/test/test-network/conf/25-dhcp-client-ipv4-only.network +++ b/test/test-network/conf/25-dhcp-client-ipv4-only.network @@ -6,6 +6,7 @@ Name=veth99 DHCP=ipv4 IPv6AcceptRA=no Address=192.168.5.250/24 +Tunnel=sit-dhcp4 [DHCPv4] RequestAddress=192.168.5.110 diff --git a/test/test-network/conf/25-sit-dhcp4.netdev b/test/test-network/conf/25-sit-dhcp4.netdev new file mode 100644 index 00000000000..7f89504ceef --- /dev/null +++ b/test/test-network/conf/25-sit-dhcp4.netdev @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[NetDev] +Name=sit-dhcp4 +Kind=sit + +[Tunnel] +Local=dhcp4 +Remote=any diff --git a/test/test-network/conf/25-sit-dhcp4.network b/test/test-network/conf/25-sit-dhcp4.network new file mode 100644 index 00000000000..bcf330579c9 --- /dev/null +++ b/test/test-network/conf/25-sit-dhcp4.network @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=sit-dhcp4 +Type=sit diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 82339583bed..61a4933d652 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -6829,7 +6829,8 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): @expectedFailureIfKernelReturnsInvalidFlags() def test_dhcp_client_ipv4_only(self): - copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network') + copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network', + '25-sit-dhcp4.netdev', '25-sit-dhcp4.network') self.setup_nftset('addr4', 'ipv4_addr') self.setup_nftset('network4', 'ipv4_addr', 'flags interval;') @@ -6842,7 +6843,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): '--dhcp-option=option:domain-search,example.com', '--dhcp-alternate-port=67,5555', ipv4_range='192.168.5.110,192.168.5.119') - self.wait_online('veth99:routable', 'veth-peer:routable') + self.wait_online('veth99:routable', 'veth-peer:routable', 'sit-dhcp4:carrier') self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4') print('## ip address show dev veth99 scope global') @@ -6915,6 +6916,11 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider'])) self.assertEqual('192.168.5.1', a) + print('## tunnel') + output = check_output('ip -d link show sit-dhcp4') + print(output) + self.assertRegex(output, fr'sit (ip6ip )?remote any local {address1} dev veth99') + print('## dnsmasq log') output = read_dnsmasq_log_file() print(output) @@ -7010,6 +7016,11 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider'])) self.assertEqual('192.168.5.1', a) + print('## tunnel') + output = check_output('ip -d link show sit-dhcp4') + print(output) + self.assertRegex(output, fr'sit (ip6ip )?remote any local {address2} dev veth99') + print('## dnsmasq log') output = read_dnsmasq_log_file() print(output)