diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2022-12-19 21:31:27 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2022-12-19 21:31:27 +0000 |
commit | 0130b59f12a774eaf26f984499cf119b3783a162 (patch) | |
tree | 3e8634678d5910cf823b9fe7839553596df35dc3 | |
parent | e2e6f03c82b478d8fb30a1717801a8978886a42f (diff) | |
parent | 5137c678453b7bf36f324af937bf73631bfd0f0d (diff) |
Merge "Improve kernel unit tests for XFRM_MIGRATE"
-rwxr-xr-x | net/test/xfrm.py | 7 | ||||
-rwxr-xr-x | net/test/xfrm_tunnel_test.py | 174 |
2 files changed, 121 insertions, 60 deletions
diff --git a/net/test/xfrm.py b/net/test/xfrm.py index abd487a..3d003b6 100755 --- a/net/test/xfrm.py +++ b/net/test/xfrm.py @@ -753,9 +753,12 @@ class Xfrm(netlink.NetlinkSocket): net_test.GetAddressFamily(net_test.GetAddressVersion(new_saddr)))) nlattrs.append((XFRMA_MIGRATE, xfrmMigrate)) + if xfrm_if_id is not None: + nlattrs.append((XFRMA_IF_ID, struct.pack("=I", xfrm_if_id))) + for selector in selectors: - self.SendXfrmNlRequest(XFRM_MSG_MIGRATE, - XfrmUserpolicyId(sel=selector, dir=direction), nlattrs) + self.SendXfrmNlRequest(XFRM_MSG_MIGRATE, + XfrmUserpolicyId(sel=selector, dir=direction), nlattrs) # UPDSA is called exclusively to update the set_mark=new_output_mark. self.AddSaInfo(new_saddr, new_daddr, spi, XFRM_MODE_TUNNEL, 0, encryption, diff --git a/net/test/xfrm_tunnel_test.py b/net/test/xfrm_tunnel_test.py index cc5139d..4efb46a 100755 --- a/net/test/xfrm_tunnel_test.py +++ b/net/test/xfrm_tunnel_test.py @@ -23,6 +23,7 @@ import itertools import struct import unittest +from net_test import LINUX_VERSION from scapy import all as scapy from tun_twister import TunTwister import csocket @@ -39,9 +40,10 @@ _TEST_XFRM_IFNAME = "ipsec42" _TEST_XFRM_IF_ID = 42 _TEST_SPI = 0x1234 -# Does the kernel support xfrmi interfaces? +# Does the kernel support CONFIG_XFRM_INTERFACE? def HaveXfrmInterfaces(): - if net_test.LINUX_VERSION >= (4, 19, 0): + # 4.19+ must have CONFIG_XFRM_INTERFACE enabled + if LINUX_VERSION >= (4, 19, 0): return True try: @@ -60,9 +62,30 @@ def HaveXfrmInterfaces(): HAVE_XFRM_INTERFACES = HaveXfrmInterfaces() -# Does the kernel support CONFIG_XFRM_MIGRATE? +# Two kernel fixes have been added in 5.17 to allow XFRM_MIGRATE to work correctly +# when (1) there are multiple tunnels with the same selectors; and (2) addresses +# are updated to a different IP family. These two fixes were pulled into upstream +# LTS releases 4.14.273, 4.19.236, 5.4.186, 5.10.107 and 5.15.30, from whence they +# flowed into the Android Common Kernel (via standard LTS merges). +# As such we require 4.14.273+, 4.19.236+, 5.4.186+, 5.10.107+, 5.15.30+ or 5.17+ +# to have these fixes. +def HasXfrmMigrateFixes(): + return ( + ((LINUX_VERSION >= (4, 14, 273)) and (LINUX_VERSION < (4, 19, 0))) or + ((LINUX_VERSION >= (4, 19, 236)) and (LINUX_VERSION < (5, 4, 0))) or + ((LINUX_VERSION >= (5, 4, 186)) and (LINUX_VERSION < (5, 10, 0))) or + ((LINUX_VERSION >= (5, 10, 107)) and (LINUX_VERSION < (5, 15, 0))) or + (LINUX_VERSION >= (5, 15, 30)) + ) + + +# Does the kernel support CONFIG_XFRM_MIGRATE and include the kernel fixes? def SupportsXfrmMigrate(): - if net_test.LINUX_VERSION >= (5, 10, 0): + if not HasXfrmMigrateFixes(): + return False + + # 5.10+ must have CONFIG_XFRM_MIGRATE enabled + if LINUX_VERSION >= (5, 10, 0): return True # XFRM_MIGRATE depends on xfrmi interfaces @@ -168,7 +191,7 @@ def InjectTests(): InjectParameterizedTests(XfrmTunnelTest) InjectParameterizedTests(XfrmInterfaceTest) InjectParameterizedTests(XfrmVtiTest) - InjectParameterizedTests(XfrmInterfaceMigrateTest) + InjectParameterizedMigrateTests(XfrmInterfaceMigrateTest) def InjectParameterizedTests(cls): @@ -180,6 +203,15 @@ def InjectParameterizedTests(cls): util.InjectParameterizedTest(cls, param_list, NameGenerator) +def InjectParameterizedMigrateTests(cls): + VERSIONS = (4, 6) + param_list = itertools.product(VERSIONS, VERSIONS, VERSIONS) + + def NameGenerator(*args): + return "IPv%d_in_IPv%d_to_outer_IPv%d" % tuple(args) + + util.InjectParameterizedTest(cls, param_list, NameGenerator) + class XfrmTunnelTest(xfrm_base.XfrmLazyTest): @@ -557,17 +589,12 @@ class XfrmInterface(IpSecBaseInterface): self.local = new_local self.remote = new_remote + self.version = net_test.GetAddressVersion(new_local) self.underlying_netid = new_underlying_netid class XfrmTunnelBase(xfrm_base.XfrmBaseTest): - # Subclass that does not allow multiple tunnels (e.g. XfrmInterfaceMigrateTest) - # should override this method. - @classmethod - def allowMultipleTunnels(cls): - return True - @classmethod def setUpClass(cls): xfrm_base.XfrmBaseTest.setUpClass() @@ -581,9 +608,6 @@ class XfrmTunnelBase(xfrm_base.XfrmBaseTest): cls.tunnelsV4 = {} cls.tunnelsV6 = {} - if not cls.allowMultipleTunnels(): - return - for i, underlying_netid in enumerate(cls.tuns): for version in 4, 6: netid = _BASE_TUNNEL_NETID[version] + _TUNNEL_NETID_OFFSET + i @@ -865,26 +889,29 @@ class XfrmTunnelBase(xfrm_base.XfrmBaseTest): self._CheckTunnelEncryption(tunnel, inner_version, local_inner, remote_inner) + def _RebuildTunnel(self, tunnel, use_null_crypt): + # Some tests require that the out_seq_num and in_seq_num are the same + # (Specifically encrypted tests), rebuild SAs to ensure seq_num is 1 + # + # Until we get better scapy support, the only way we can build an + # encrypted packet is to send it out, and read the packet from the wire. + # We then generally use this as the "inbound" encrypted packet, injecting + # it into the interface for which it is expected on. + # + # As such, this is required to ensure that encrypted packets (which we + # currently have no way to easily modify) are not considered replay + # attacks by the inbound SA. (eg: received 3 packets, seq_num_in = 3, + # sent only 1, # seq_num_out = 1, inbound SA would consider this a replay + # attack) + tunnel.TeardownXfrm() + tunnel.SetupXfrm(use_null_crypt) + def _TestTunnel(self, inner_version, outer_version, func, use_null_crypt): """Bootstrap method to setup and run tests for the given parameters.""" tunnel = self.randomTunnel(outer_version) try: - # Some tests require that the out_seq_num and in_seq_num are the same - # (Specifically encrypted tests), rebuild SAs to ensure seq_num is 1 - # - # Until we get better scapy support, the only way we can build an - # encrypted packet is to send it out, and read the packet from the wire. - # We then generally use this as the "inbound" encrypted packet, injecting - # it into the interface for which it is expected on. - # - # As such, this is required to ensure that encrypted packets (which we - # currently have no way to easily modify) are not considered replay - # attacks by the inbound SA. (eg: received 3 packets, seq_num_in = 3, - # sent only 1, # seq_num_out = 1, inbound SA would consider this a replay - # attack) - tunnel.TeardownXfrm() - tunnel.SetupXfrm(use_null_crypt) + self._RebuildTunnel(tunnel, use_null_crypt) local_inner = tunnel.addrs[inner_version] remote_inner = _GetRemoteInnerAddress(inner_version) @@ -1010,15 +1037,23 @@ class XfrmInterfaceTest(XfrmTunnelBase): def ParamTestXfrmIntfRekey(self, inner_version, outer_version): self._TestTunnelRekey(inner_version, outer_version) -@unittest.skipUnless(SUPPORTS_XFRM_MIGRATE, "XFRM migration unsupported") +############################################################################## +# +# Test for presence of CONFIG_XFRM_MIGRATE and kernel patches +# +# xfrm: Check if_id in xfrm_migrate +# Upstream commit: c1aca3080e382886e2e58e809787441984a2f89b +# +# xfrm: Fix xfrm migrate issues when address family changes +# Upstream commit: e03c3bba351f99ad932e8f06baa9da1afc418e02 +# +# Those two upstream 5.17 fixes above were pulled in to LTS in kernel versions +# 4.14.273, 4.19.236, 5.4.186, 5.10.107, 5.15.30. +# +@unittest.skipUnless(SUPPORTS_XFRM_MIGRATE, + "XFRM migration unsupported or fixes not included") class XfrmInterfaceMigrateTest(XfrmTunnelBase): - # TODO: b/172497215 There is a kernel issue that XFRM_MIGRATE cannot work correctly - # when there are multiple tunnels with the same selectors. Thus before this issue - # is fixed, #allowMultipleTunnels must be overridden to avoid setting up multiple - # tunnels. This need to be removed after the kernel issue is fixed. - @classmethod - def allowMultipleTunnels(cls): - return False + INTERFACE_CLASS = XfrmInterface def setUpTunnel(self, outer_version, use_null_crypt): underlying_netid = self.RandomNetid() @@ -1041,9 +1076,17 @@ class XfrmInterfaceMigrateTest(XfrmTunnelBase): self._SetupTunnelNetwork(tunnel, False) tunnel.Teardown() - def _TestTunnel(self, inner_version, outer_version, func, use_null_crypt): + def _TestTunnel(self, inner_version, outer_version, new_outer_version, func, + use_null_crypt): + tunnel = self.randomTunnel(outer_version) + + old_underlying_netid = tunnel.underlying_netid + old_local = tunnel.local + old_remote = tunnel.remote + + try: - tunnel = self.setUpTunnel(outer_version, use_null_crypt) + self._RebuildTunnel(tunnel, use_null_crypt) # Verify functionality before migration local_inner = tunnel.addrs[inner_version] @@ -1051,39 +1094,54 @@ class XfrmInterfaceMigrateTest(XfrmTunnelBase): func(tunnel, inner_version, local_inner, remote_inner) # Migrate tunnel - # TODO:b/169170981 Add tests that migrate 4 -> 6 and 6 -> 4 new_underlying_netid = self.RandomNetid(exclude=tunnel.underlying_netid) - new_local = self.MyAddress(outer_version, new_underlying_netid) - new_remote = net_test.IPV4_ADDR2 if outer_version == 4 else net_test.IPV6_ADDR2 + new_version = new_outer_version + new_local = self.MyAddress(new_version, new_underlying_netid) + new_remote = net_test.IPV4_ADDR2 if new_version == 4 else net_test.IPV6_ADDR2 tunnel.Migrate(new_underlying_netid, new_local, new_remote) # Verify functionality after migration func(tunnel, inner_version, local_inner, remote_inner) finally: - self.tearDownTunnel(tunnel) + # Reset the tunnel to the original configuration + tunnel.TeardownXfrm() - def ParamTestMigrateXfrmIntfInput(self, inner_version, outer_version): - self._TestTunnel(inner_version, outer_version, self._CheckTunnelInput, True) + self.local = old_local + self.remote = old_remote + self.underlying_netid = old_underlying_netid + tunnel.SetupXfrm(False) - def ParamTestMigrateXfrmIntfOutput(self, inner_version, outer_version): - self._TestTunnel(inner_version, outer_version, self._CheckTunnelOutput, - True) - def ParamTestMigrateXfrmIntfInOutEncrypted(self, inner_version, outer_version): - self._TestTunnel(inner_version, outer_version, self._CheckTunnelEncryption, - False) + def ParamTestMigrateXfrmIntfInput(self, inner_version, outer_version, + new_outer_version): + self._TestTunnel(inner_version, outer_version, new_outer_version, + self._CheckTunnelInput, True) - def ParamTestMigrateXfrmIntfIcmp(self, inner_version, outer_version): - self._TestTunnel(inner_version, outer_version, self._CheckTunnelIcmp, False) + def ParamTestMigrateXfrmIntfOutput(self, inner_version, outer_version, + new_outer_version): + self._TestTunnel(inner_version, outer_version, new_outer_version, + self._CheckTunnelOutput, True) - def ParamTestMigrateXfrmIntfEncryptionWithIcmp(self, inner_version, outer_version): - self._TestTunnel(inner_version, outer_version, + def ParamTestMigrateXfrmIntfInOutEncrypted(self, inner_version, outer_version, + new_outer_version): + self._TestTunnel(inner_version, outer_version, new_outer_version, + self._CheckTunnelEncryption, False) + + def ParamTestMigrateXfrmIntfIcmp(self, inner_version, outer_version, + new_outer_version): + self._TestTunnel(inner_version, outer_version, new_outer_version, + self._CheckTunnelIcmp, False) + + def ParamTestMigrateXfrmIntfEncryptionWithIcmp(self, inner_version, outer_version, + new_outer_version): + self._TestTunnel(inner_version, outer_version, new_outer_version, self._CheckTunnelEncryptionWithIcmp, False) - def ParamTestMigrateXfrmIntfRekey(self, inner_version, outer_version): - self._TestTunnel(inner_version, outer_version, self._CheckTunnelRekey, - True) + def ParamTestMigrateXfrmIntfRekey(self, inner_version, outer_version, + new_outer_version): + self._TestTunnel(inner_version, outer_version, new_outer_version, + self._CheckTunnelRekey, True) if __name__ == "__main__": InjectTests() |