diff options
-rw-r--r-- | net/test/iproute.py | 26 | ||||
-rwxr-xr-x | net/test/run_net_test.sh | 2 | ||||
-rwxr-xr-x | net/test/xfrm.py | 74 | ||||
-rwxr-xr-x | net/test/xfrm_test.py | 6 | ||||
-rwxr-xr-x | net/test/xfrm_tunnel_test.py | 263 |
5 files changed, 275 insertions, 96 deletions
diff --git a/net/test/iproute.py b/net/test/iproute.py index 9cfafc6..77c41b2 100644 --- a/net/test/iproute.py +++ b/net/test/iproute.py @@ -208,13 +208,17 @@ IFLA_PAD = 42 IFLA_XDP = 43 IFLA_EVENT = 44 -# linux/include/uapi/if_link.h +# include/uapi/linux/if_link.h IFLA_INFO_UNSPEC = 0 IFLA_INFO_KIND = 1 IFLA_INFO_DATA = 2 IFLA_INFO_XSTATS = 3 -# linux/if_tunnel.h +IFLA_XFRM_UNSPEC = 0 +IFLA_XFRM_LINK = 1 +IFLA_XFRM_IF_ID = 2 + +# include/uapi/linux/if_tunnel.h IFLA_VTI_UNSPEC = 0 IFLA_VTI_LINK = 1 IFLA_VTI_IKEY = 2 @@ -680,7 +684,7 @@ class IPRoute(netlink.NetlinkSocket): attrs = self._ParseAttributes(RTM_NEWLINK, IfinfoMsg, attrs) return attrs["IFLA_STATS64"] - def GetVtiInfoData(self, dev_name): + def GetIfinfoData(self, dev_name): """Returns an IFLA_INFO_DATA dict object for the specified interface.""" _, attrs = self.GetIfinfo(dev_name) attrs = self._ParseAttributes(RTM_NEWLINK, IfinfoMsg, attrs) @@ -743,6 +747,22 @@ class IPRoute(netlink.NetlinkSocket): flags |= netlink.NLM_F_EXCL return self._SendNlRequest(RTM_NEWLINK, ifinfo, flags) + def CreateXfrmInterface(self, dev_name, xfrm_if_id, underlying_ifindex): + """Creates an XFRM interface with the specified parameters.""" + # The netlink attribute structure is essentially identical to the one + # for VTI above (q.v). + ifdata = self._NlAttrU32(IFLA_XFRM_LINK, underlying_ifindex) + ifdata += self._NlAttrU32(IFLA_XFRM_IF_ID, xfrm_if_id) + + linkinfo = self._NlAttrStr(IFLA_INFO_KIND, "xfrm") + linkinfo += self._NlAttr(IFLA_INFO_DATA, ifdata) + + msg = IfinfoMsg().Pack() + msg += self._NlAttrStr(IFLA_IFNAME, dev_name) + msg += self._NlAttr(IFLA_LINKINFO, linkinfo) + + return self._SendNlRequest(RTM_NEWLINK, msg) + if __name__ == "__main__": iproute = IPRoute() diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index 8c256b4..c7c18d3 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -30,7 +30,7 @@ OPTIONS="$OPTIONS TRANSPORT INET_XFRM_MODE_TUNNEL INET6_AH INET6_ESP" OPTIONS="$OPTIONS INET6_XFRM_MODE_TRANSPORT INET6_XFRM_MODE_TUNNEL" OPTIONS="$OPTIONS CRYPTO_SHA256 CRYPTO_SHA512 CRYPTO_AES_X86_64 CRYPTO_NULL" OPTIONS="$OPTIONS CRYPTO_GCM CRYPTO_ECHAINIV NET_IPVTI IPV6_VTI" -OPTIONS="$OPTIONS SOCK_CGROUP_DATA CGROUP_BPF" +OPTIONS="$OPTIONS SOCK_CGROUP_DATA CGROUP_BPF CONFIG_XFRM_INTERFACE" # For 4.14 kernels, where UBD and HOSTFS are not set OPTIONS="$OPTIONS CONFIG_BLK_DEV_UBD CONFIG_HOSTFS" diff --git a/net/test/xfrm.py b/net/test/xfrm.py index 2fb466a..56b4774 100755 --- a/net/test/xfrm.py +++ b/net/test/xfrm.py @@ -85,6 +85,8 @@ XFRMA_ADDRESS_FILTER = 26 XFRMA_PAD = 27 XFRMA_OFFLOAD_DEV = 28 XFRMA_OUTPUT_MARK = 29 +XFRMA_INPUT_MARK = 30 +XFRMA_IF_ID = 31 # Other netlink constants. See include/uapi/linux/xfrm.h. @@ -369,21 +371,25 @@ class Xfrm(netlink.NetlinkSocket): data = struct.unpack("=I", nla_data)[0] elif name == "XFRMA_TMPL": data = cstruct.Read(nla_data, XfrmUserTmpl)[0] + elif name == "XFRMA_IF_ID": + data = struct.unpack("=I", nla_data)[0] else: data = nla_data return name, data - def _UpdatePolicyInfo(self, msg, policy, tmpl, mark): + def _UpdatePolicyInfo(self, msg, policy, tmpl, mark, xfrm_if_id): """Send a policy to the Security Policy Database""" nlattrs = [] if tmpl is not None: nlattrs.append((XFRMA_TMPL, tmpl)) if mark is not None: nlattrs.append((XFRMA_MARK, mark)) + if xfrm_if_id is not None: + nlattrs.append((XFRMA_IF_ID, struct.pack("=I", xfrm_if_id))) self.SendXfrmNlRequest(msg, policy, nlattrs) - def AddPolicyInfo(self, policy, tmpl, mark): + def AddPolicyInfo(self, policy, tmpl, mark, xfrm_if_id=None): """Add a new policy to the Security Policy Database If the policy exists, then return an error (EEXIST). @@ -392,10 +398,11 @@ class Xfrm(netlink.NetlinkSocket): policy: an unpacked XfrmUserpolicyInfo tmpl: an unpacked XfrmUserTmpl mark: an unpacked XfrmMark + xfrm_if_id: the XFRM interface ID as an integer, or None """ - self._UpdatePolicyInfo(XFRM_MSG_NEWPOLICY, policy, tmpl, mark) + self._UpdatePolicyInfo(XFRM_MSG_NEWPOLICY, policy, tmpl, mark, xfrm_if_id) - def UpdatePolicyInfo(self, policy, tmpl, mark): + def UpdatePolicyInfo(self, policy, tmpl, mark, xfrm_if_id): """Update an existing policy in the Security Policy Database If the policy does not exist, then create it; otherwise, update the @@ -405,10 +412,11 @@ class Xfrm(netlink.NetlinkSocket): policy: an unpacked XfrmUserpolicyInfo tmpl: an unpacked XfrmUserTmpl to update mark: an unpacked XfrmMark to match the existing policy or None + xfrm_if_id: an XFRM interface ID or None """ - self._UpdatePolicyInfo(XFRM_MSG_UPDPOLICY, policy, tmpl, mark) + self._UpdatePolicyInfo(XFRM_MSG_UPDPOLICY, policy, tmpl, mark, xfrm_if_id) - def DeletePolicyInfo(self, selector, direction, mark): + def DeletePolicyInfo(self, selector, direction, mark, xfrm_if_id=None): """Delete a policy from the Security Policy Database Args: @@ -419,6 +427,8 @@ class Xfrm(netlink.NetlinkSocket): nlattrs = [] if mark is not None: nlattrs.append((XFRMA_MARK, mark)) + if xfrm_if_id is not None: + nlattrs.append((XFRMA_IF_ID, struct.pack("=I", xfrm_if_id))) self.SendXfrmNlRequest(XFRM_MSG_DELPOLICY, XfrmUserpolicyId(sel=selector, dir=direction), nlattrs) @@ -440,11 +450,35 @@ class Xfrm(netlink.NetlinkSocket): if nlattrs is None: nlattrs = [] for attr_type, attr_msg in nlattrs: - msg += self._NlAttr(attr_type, attr_msg.Pack()) + # TODO: find a better way to deal with the fact that many XFRM messages + # use nlattrs that aren't cstructs. + # + # This code allows callers to pass in either something that has a Pack() + # method or a packed netlink attr, but not other types of attributes. + # Alternatives include: + # + # 1. Require callers to marshal netlink attributes themselves and call + # _SendNlRequest directly. Delete this method. + # 2. Rename this function to _SendXfrmNlRequestCstructOnly (or other name + # that makes it clear that this only takes cstructs). Switch callers + # that need non-cstruct elements to calling _SendNlRequest directly. + # 3. Make this function somehow automatically detect what to do for + # all types of XFRM attributes today and in the future. This may be + # feasible because all XFRM attributes today occupy the same number + # space, but what about nested attributes? It is unlikley feasible via + # things like "if isinstance(attr_msg, str): ...", because that would + # not be able to determine the right size or byte order for non-struct + # types such as int. + # 4. Define fictitious cstructs which have no correspondence to actual + # kernel structs such as the following to represent a raw integer. + # XfrmAttrOutputMark = cstruct.Struct("=I", mark) + if hasattr(attr_msg, "Pack"): + attr_msg = attr_msg.Pack() + msg += self._NlAttr(attr_type, attr_msg) return self._SendNlRequest(msg_type, msg, flags) def AddSaInfo(self, src, dst, spi, mode, reqid, encryption, auth_trunc, aead, - encap, mark, output_mark, is_update=False): + encap, mark, output_mark, is_update=False, xfrm_if_id=None): """Adds an IPsec security association. Args: @@ -463,6 +497,7 @@ class Xfrm(netlink.NetlinkSocket): output_mark: An integer, the output mark. 0 means unset. is_update: If true, update an existing SA otherwise create a new SA. For compatibility reasons, this value defaults to False. + xfrm_if_id: The XFRM interface ID, or None. """ proto = IPPROTO_ESP xfrm_id = XfrmId((PaddedAddress(dst), spi, proto)) @@ -488,6 +523,8 @@ class Xfrm(netlink.NetlinkSocket): nlattrs += self._NlAttr(XFRMA_ENCAP, encap.Pack()) if output_mark is not None: nlattrs += self._NlAttrU32(XFRMA_OUTPUT_MARK, output_mark) + if xfrm_if_id is not None: + nlattrs += self._NlAttrU32(XFRMA_IF_ID, xfrm_if_id) # The kernel ignores these on input, so make them empty. cur = XfrmLifetimeCur() @@ -519,7 +556,7 @@ class Xfrm(netlink.NetlinkSocket): nl_msg_type = XFRM_MSG_UPDSA if is_update else XFRM_MSG_NEWSA self._SendNlRequest(nl_msg_type, msg, flags) - def DeleteSaInfo(self, dst, spi, proto, mark=None): + def DeleteSaInfo(self, dst, spi, proto, mark=None, xfrm_if_id=None): """Delete an SA from the SAD Args: @@ -530,12 +567,13 @@ class Xfrm(netlink.NetlinkSocket): mark: A mark match specifier, such as returned by ExactMatchMark(), or None for an SA without a Mark attribute. """ - # TODO: deletes take a mark as well. family = AF_INET6 if ":" in dst else AF_INET usersa_id = XfrmUsersaId((PaddedAddress(dst), spi, family, proto)) nlattrs = [] if mark is not None: nlattrs.append((XFRMA_MARK, mark)) + if xfrm_if_id is not None: + nlattrs.append((XFRMA_IF_ID, struct.pack("=I", xfrm_if_id))) self.SendXfrmNlRequest(XFRM_MSG_DELSA, usersa_id, nlattrs) def AllocSpi(self, dst, proto, min_spi, max_spi): @@ -592,7 +630,7 @@ class Xfrm(netlink.NetlinkSocket): self._SendNlRequest(XFRM_MSG_FLUSHSA, usersa_flush.Pack(), flags) def CreateTunnel(self, direction, selector, src, dst, spi, encryption, - auth_trunc, mark, output_mark): + auth_trunc, mark, output_mark, xfrm_if_id): """Create an XFRM Tunnel Consisting of a Policy and an SA. Create a unidirectional XFRM tunnel, which entails one Policy and one @@ -613,6 +651,7 @@ class Xfrm(netlink.NetlinkSocket): for matching the security policy. None means unspecified. output_mark: The mark used to select the underlying network for packets outbound from xfrm. None means unspecified. + xfrm_if_id: The ID of the XFRM interface to use or None. """ outer_family = net_test.GetAddressFamily(net_test.GetAddressVersion(dst)) @@ -620,7 +659,7 @@ class Xfrm(netlink.NetlinkSocket): # UPDSA does not update marks at this time. Actual use case will have no # mark set. Test this use case. self.AddSaInfo(src, dst, spi, XFRM_MODE_TUNNEL, 0, encryption, auth_trunc, - None, None, None, output_mark) + None, None, None, output_mark, xfrm_if_id=xfrm_if_id) if selector is None: selectors = [EmptySelector(AF_INET), EmptySelector(AF_INET6)] @@ -630,16 +669,19 @@ class Xfrm(netlink.NetlinkSocket): for selector in selectors: policy = UserPolicy(direction, selector) tmpl = UserTemplate(outer_family, spi, 0, (src, dst)) - self.AddPolicyInfo(policy, tmpl, mark) + self.AddPolicyInfo(policy, tmpl, mark, xfrm_if_id=xfrm_if_id) + + def DeleteTunnel(self, direction, selector, dst, spi, mark, xfrm_if_id): + if mark is not None: + mark = ExactMatchMark(mark) - def DeleteTunnel(self, direction, selector, dst, spi, mark): - self.DeleteSaInfo(dst, spi, IPPROTO_ESP, ExactMatchMark(mark)) + self.DeleteSaInfo(dst, spi, IPPROTO_ESP, mark, xfrm_if_id) if selector is None: selectors = [EmptySelector(AF_INET), EmptySelector(AF_INET6)] else: selectors = [selector] for selector in selectors: - self.DeletePolicyInfo(selector, direction, ExactMatchMark(mark)) + self.DeletePolicyInfo(selector, direction, mark, xfrm_if_id) if __name__ == "__main__": diff --git a/net/test/xfrm_test.py b/net/test/xfrm_test.py index 93c66f4..52a1596 100755 --- a/net/test/xfrm_test.py +++ b/net/test/xfrm_test.py @@ -631,14 +631,14 @@ class XfrmFunctionalTest(xfrm_base.XfrmLazyTest): self.assertEquals(attributes['XFRMA_TMPL'], tmpl) # Create a new policy using update. - self.xfrm.UpdatePolicyInfo(policy, tmpl1, mark) + self.xfrm.UpdatePolicyInfo(policy, tmpl1, mark, None) # NEWPOLICY will not update the existing policy. This checks both that # UPDPOLICY created a policy and that NEWPOLICY will not perform updates. _CheckTemplateMatch(tmpl1) with self.assertRaisesErrno(EEXIST): - self.xfrm.AddPolicyInfo(policy, tmpl2, mark) + self.xfrm.AddPolicyInfo(policy, tmpl2, mark, None) # Update the policy using UPDPOLICY. - self.xfrm.UpdatePolicyInfo(policy, tmpl2, mark) + self.xfrm.UpdatePolicyInfo(policy, tmpl2, mark, None) # There should only be one policy after update, and it should have the # updated template. _CheckTemplateMatch(tmpl2) diff --git a/net/test/xfrm_tunnel_test.py b/net/test/xfrm_tunnel_test.py index ac340d9..0c355b8 100755 --- a/net/test/xfrm_tunnel_test.py +++ b/net/test/xfrm_tunnel_test.py @@ -31,6 +31,28 @@ import packets import xfrm import xfrm_base +_LOOPBACK_IFINDEX = 1 +_TEST_XFRM_IFNAME = "ipsec42" +_TEST_XFRM_IF_ID = 42 + +# Does the kernel support xfrmi interfaces? +def HaveXfrmInterfaces(): + try: + i = iproute.IPRoute() + i.CreateXfrmInterface(_TEST_XFRM_IFNAME, _TEST_XFRM_IF_ID, + _LOOPBACK_IFINDEX) + i.DeleteLink(_TEST_XFRM_IFNAME) + try: + i.GetIfIndex(_TEST_XFRM_IFNAME) + assert "Deleted interface %s still exists!" % _TEST_XFRM_IFNAME + except IOError: + pass + return True + except IOError: + return False + +HAVE_XFRM_INTERFACES = HaveXfrmInterfaces() + # Parameters to Set up VTI as a special network _BASE_VTI_NETID = {4: 40, 6: 60} _BASE_VTI_OKEY = 2000000100 @@ -79,7 +101,7 @@ class XfrmTunnelTest(xfrm_base.XfrmLazyTest): local_outer, remote_outer, _TEST_OUT_SPI, xfrm_base._ALGO_CBC_AES_256, xfrm_base._ALGO_HMAC_SHA1, - None, underlying_netid) + None, underlying_netid, None) write_sock = socket(net_test.GetAddressFamily(inner_version), SOCK_DGRAM, 0) # Select an interface, which provides the source address of the inner @@ -106,13 +128,16 @@ class XfrmTunnelTest(xfrm_base.XfrmLazyTest): @unittest.skipUnless(net_test.LINUX_VERSION >= (3, 18, 0), "VTI Unsupported") class XfrmAddDeleteVtiTest(xfrm_base.XfrmBaseTest): - def verifyVtiInfoData(self, vti_info_data, version, local_addr, remote_addr, ikey, okey): + def _VerifyVtiInfoData(self, vti_info_data, version, local_addr, remote_addr, + ikey, okey): self.assertEquals(vti_info_data["IFLA_VTI_IKEY"], ikey) self.assertEquals(vti_info_data["IFLA_VTI_OKEY"], okey) family = AF_INET if version == 4 else AF_INET6 - self.assertEquals(inet_ntop(family, vti_info_data["IFLA_VTI_LOCAL"]), local_addr) - self.assertEquals(inet_ntop(family, vti_info_data["IFLA_VTI_REMOTE"]), remote_addr) + self.assertEquals(inet_ntop(family, vti_info_data["IFLA_VTI_LOCAL"]), + local_addr) + self.assertEquals(inet_ntop(family, vti_info_data["IFLA_VTI_REMOTE"]), + remote_addr) def testAddVti(self): """Test the creation of a Virtual Tunnel Interface.""" @@ -125,8 +150,9 @@ class XfrmAddDeleteVtiTest(xfrm_base.XfrmBaseTest): remote_addr=_GetRemoteOuterAddress(version), o_key=_TEST_OKEY, i_key=_TEST_IKEY) - self.verifyVtiInfoData(self.iproute.GetVtiInfoData(_VTI_IFNAME), - version, local_addr, _GetRemoteOuterAddress(version), + self._VerifyVtiInfoData(self.iproute.GetIfinfoData(_VTI_IFNAME), + version, local_addr, + _GetRemoteOuterAddress(version), _TEST_IKEY, _TEST_OKEY) new_remote_addr = {4: net_test.IPV4_ADDR2, 6: net_test.IPV6_ADDR2} @@ -140,7 +166,7 @@ class XfrmAddDeleteVtiTest(xfrm_base.XfrmBaseTest): i_key=new_ikey, is_update=True) - self.verifyVtiInfoData(self.iproute.GetVtiInfoData(_VTI_IFNAME), + self._VerifyVtiInfoData(self.iproute.GetIfinfoData(_VTI_IFNAME), version, local_addr, new_remote_addr[version], new_ikey, new_okey) @@ -166,7 +192,7 @@ class XfrmAddDeleteVtiTest(xfrm_base.XfrmBaseTest): class VtiInterface(object): - def __init__(self, iface, netid, underlying_netid, local, remote): + def __init__(self, iface, netid, underlying_netid, _, local, remote): self.iface = iface self.netid = netid self.underlying_netid = underlying_netid @@ -188,7 +214,7 @@ class VtiInterface(object): self.TeardownInterface() def SetupInterface(self): - self.iproute.CreateVirtualTunnelInterface( + return self.iproute.CreateVirtualTunnelInterface( self.iface, self.local, self.remote, self.ikey, self.okey) def TeardownInterface(self): @@ -202,22 +228,88 @@ class VtiInterface(object): self.out_spi, xfrm_base._ALGO_CBC_AES_256, xfrm_base._ALGO_HMAC_SHA1, xfrm.ExactMatchMark(self.okey), - self.underlying_netid) + self.underlying_netid, None) self.xfrm.CreateTunnel(xfrm.XFRM_POLICY_IN, None, self.remote, self.local, self.in_spi, xfrm_base._ALGO_CBC_AES_256, xfrm_base._ALGO_HMAC_SHA1, - xfrm.ExactMatchMark(self.ikey), None) + xfrm.ExactMatchMark(self.ikey), None, None) def TeardownXfrm(self): self.xfrm.DeleteTunnel(xfrm.XFRM_POLICY_OUT, None, self.remote, - self.out_spi, self.okey) + self.out_spi, self.okey, None) self.xfrm.DeleteTunnel(xfrm.XFRM_POLICY_IN, None, self.local, - self.in_spi, self.ikey) + self.in_spi, self.ikey, None) -@unittest.skipUnless(net_test.LINUX_VERSION >= (3, 18, 0), "VTI Unsupported") -class XfrmVtiTest(xfrm_base.XfrmBaseTest): +@unittest.skipUnless(HAVE_XFRM_INTERFACES, "XFRM interfaces unsupported") +class XfrmAddDeleteXfrmInterfaceTest(xfrm_base.XfrmBaseTest): + """Test the creation of an XFRM Interface.""" + + def testAddXfrmInterface(self): + self.iproute.CreateXfrmInterface(_TEST_XFRM_IFNAME, _TEST_XFRM_IF_ID, + _LOOPBACK_IFINDEX) + if_index = self.iproute.GetIfIndex(_TEST_XFRM_IFNAME) + net_test.SetInterfaceUp(_TEST_XFRM_IFNAME) + + # Validate that the netlink interface matches the ioctl interface. + self.assertEquals(net_test.GetInterfaceIndex(_TEST_XFRM_IFNAME), if_index) + self.iproute.DeleteLink(_TEST_XFRM_IFNAME) + with self.assertRaises(IOError): + self.iproute.GetIfIndex(_TEST_XFRM_IFNAME) + + +class XfrmInterface(object): + + def __init__(self, iface, netid, underlying_netid, ifindex, local, remote): + self.iface = iface + self.netid = netid + self.underlying_netid = underlying_netid + self.ifindex = ifindex + self.local, self.remote = local, remote + self.rx = self.tx = 0 + self.xfrm_if_id = netid + self.out_spi = self.in_spi = random.randint(0, 0x7fffffff) + self.xfrm_if_id = self.netid + + self.iproute = iproute.IPRoute() + self.xfrm = xfrm.Xfrm() + + self.SetupInterface() + self.SetupXfrm() + self.addrs = {} + + def Teardown(self): + self.TeardownXfrm() + self.TeardownInterface() + + def SetupInterface(self): + """Create an XFRM interface.""" + return self.iproute.CreateXfrmInterface(self.iface, self.netid, self.ifindex) + + def TeardownInterface(self): + self.iproute.DeleteLink(self.iface) + + def SetupXfrm(self): + self.xfrm.CreateTunnel(xfrm.XFRM_POLICY_OUT, None, self.local, self.remote, + self.out_spi, xfrm_base._ALGO_CBC_AES_256, + xfrm_base._ALGO_HMAC_SHA1, None, + self.underlying_netid, self.xfrm_if_id) + self.xfrm.CreateTunnel(xfrm.XFRM_POLICY_IN, None, self.remote, self.local, + self.in_spi, xfrm_base._ALGO_CBC_AES_256, + xfrm_base._ALGO_HMAC_SHA1, + None, None, self.xfrm_if_id) + + + def TeardownXfrm(self): + self.xfrm.DeleteTunnel(xfrm.XFRM_POLICY_OUT, None, self.remote, + self.out_spi, None, self.xfrm_if_id) + self.xfrm.DeleteTunnel(xfrm.XFRM_POLICY_IN, None, self.local, + self.in_spi, None, self.xfrm_if_id) + + + +class XfrmTunnelBase(xfrm_base.XfrmBaseTest): @classmethod def setUpClass(cls): @@ -227,7 +319,7 @@ class XfrmVtiTest(xfrm_base.XfrmBaseTest): cls.SetInboundMarks(True) cls.SetMarkReflectSysctls(1) - cls.vtis = {} + cls.tunnels = {} for i, underlying_netid in enumerate(cls.tuns): for version in 4, 6: netid = _BASE_VTI_NETID[version] + i @@ -237,19 +329,23 @@ class XfrmVtiTest(xfrm_base.XfrmBaseTest): remote = net_test.IPV4_ADDR2 if (i % 2) else net_test.IPV4_ADDR else: remote = net_test.IPV6_ADDR2 if (i % 2) else net_test.IPV6_ADDR - vti = VtiInterface(iface, netid, underlying_netid, local, remote) + ifindex = cls.ifindices[underlying_netid] + + tunnel = cls.INTERFACE_CLASS(iface, netid, underlying_netid, ifindex, + local, remote) + cls._SetInboundMarking(netid, iface, True) - cls._SetupVtiNetwork(vti, True) - cls.vtis[netid] = vti + cls._SetupTunnelNetwork(tunnel, True) + cls.tunnels[netid] = tunnel @classmethod def tearDownClass(cls): # The sysctls are restored by MultinetworkBaseTest.tearDownClass. cls.SetInboundMarks(False) - for vti in cls.vtis.values(): - cls._SetInboundMarking(vti.netid, vti.iface, False) - cls._SetupVtiNetwork(vti, False) - vti.Teardown() + for tunnel in cls.tunnels.values(): + cls._SetInboundMarking(tunnel.netid, tunnel.iface, False) + cls._SetupTunnelNetwork(tunnel, False) + tunnel.Teardown() xfrm_base.XfrmBaseTest.tearDownClass() def setUp(self): @@ -275,16 +371,16 @@ class XfrmVtiTest(xfrm_base.XfrmBaseTest): net_test.AddressLengthBits(version), ifindex) @classmethod - def _SetupVtiNetwork(cls, vti, is_add): - """Setup rules and routes for a VTI Network. + def _SetupTunnelNetwork(cls, tunnel, is_add): + """Setup rules and routes for a tunnel Network. Takes an interface and depending on the boolean value of is_add, either adds or removes the rules - and routes for a VTI to behave like an Android - Network for purposes of testing. + and routes for a tunnel interface to behave like an + Android Network for purposes of testing. Args: - vti: A VtiInterface, the VTI to set up. + tunnel: A VtiInterface or XfrmInterface, the tunnel to set up. is_add: Boolean that causes this method to perform setup if True or teardown if False """ @@ -292,32 +388,33 @@ class XfrmVtiTest(xfrm_base.XfrmBaseTest): # Disable router solicitations to avoid occasional spurious packets # arriving on the underlying network; there are two possible behaviors # when that occurred: either only the RA packet is read, and when it - # is echoed back to the VTI, it causes the test to fail by not receiving - # the UDP_PAYLOAD; or, two packets may arrive on the underlying - # network which fails the assertion that only one ESP packet is received. + # is echoed back to the tunnel, it causes the test to fail by not + # receiving # the UDP_PAYLOAD; or, two packets may arrive on the + # underlying # network which fails the assertion that only one ESP packet + # is received. cls.SetSysctl( - "/proc/sys/net/ipv6/conf/%s/router_solicitations" % vti.iface, 0) - net_test.SetInterfaceUp(vti.iface) + "/proc/sys/net/ipv6/conf/%s/router_solicitations" % tunnel.iface, 0) + net_test.SetInterfaceUp(tunnel.iface) for version in [4, 6]: - ifindex = net_test.GetInterfaceIndex(vti.iface) - table = vti.netid + ifindex = net_test.GetInterfaceIndex(tunnel.iface) + table = tunnel.netid # Set up routing rules. - start, end = cls.UidRangeForNetid(vti.netid) + start, end = cls.UidRangeForNetid(tunnel.netid) cls.iproute.UidRangeRule(version, is_add, start, end, table, cls.PRIORITY_UID) - cls.iproute.OifRule(version, is_add, vti.iface, table, cls.PRIORITY_OIF) - cls.iproute.FwmarkRule(version, is_add, vti.netid, cls.NETID_FWMASK, + cls.iproute.OifRule(version, is_add, tunnel.iface, table, cls.PRIORITY_OIF) + cls.iproute.FwmarkRule(version, is_add, tunnel.netid, cls.NETID_FWMASK, table, cls.PRIORITY_FWMARK) # Configure IP addresses. if version == 4: - addr = cls._MyIPv4Address(vti.netid) + addr = cls._MyIPv4Address(tunnel.netid) else: - addr = cls.OnlinkPrefix(6, vti.netid) + "1" + addr = cls.OnlinkPrefix(6, tunnel.netid) + "1" prefixlen = net_test.AddressLengthBits(version) - vti.addrs[version] = addr + tunnel.addrs[version] = addr if is_add: cls.iproute.AddAddress(addr, prefixlen, ifindex) cls.iproute.AddRoute(version, table, "default", 0, None, ifindex) @@ -325,22 +422,24 @@ class XfrmVtiTest(xfrm_base.XfrmBaseTest): cls.iproute.DelRoute(version, table, "default", 0, None, ifindex) cls.iproute.DelAddress(addr, prefixlen, ifindex) - def assertReceivedPacket(self, vti): - vti.rx += 1 - self.assertEquals((vti.rx, vti.tx), self.iproute.GetRxTxPackets(vti.iface)) + def assertReceivedPacket(self, tunnel): + tunnel.rx += 1 + self.assertEquals((tunnel.rx, tunnel.tx), + self.iproute.GetRxTxPackets(tunnel.iface)) - def assertSentPacket(self, vti): - vti.tx += 1 - self.assertEquals((vti.rx, vti.tx), self.iproute.GetRxTxPackets(vti.iface)) + def assertSentPacket(self, tunnel): + tunnel.tx += 1 + self.assertEquals((tunnel.rx, tunnel.tx), + self.iproute.GetRxTxPackets(tunnel.iface)) # TODO: Should we completely re-write this using null encryption and null # authentication? We could then assemble and disassemble packets for each # direction individually. This approach would improve debuggability, avoid the # complexity of the twister, and allow the test to more-closely validate # deployable configurations. - def _CheckVtiInputOutput(self, vti, inner_version): - local_outer = vti.local - remote_outer = vti.remote + def _CheckTunnelInputOutput(self, tunnel, inner_version): + local_outer = tunnel.local + remote_outer = tunnel.remote # Create a socket to receive packets. read_sock = socket( @@ -351,43 +450,43 @@ class XfrmVtiTest(xfrm_base.XfrmBaseTest): # Guard against the eventuality of the receive failing. csocket.SetSocketTimeout(read_sock, 100) - # Send a packet out via the vti-backed network, bound for the port number + # Send a packet out via the tunnel-backed network, bound for the port number # of the input socket. write_sock = socket( net_test.GetAddressFamily(inner_version), SOCK_DGRAM, 0) - self.SelectInterface(write_sock, vti.netid, "mark") + self.SelectInterface(write_sock, tunnel.netid, "mark") write_sock.sendto(net_test.UDP_PAYLOAD, (_GetRemoteInnerAddress(inner_version), port)) # Read a tunneled IP packet on the underlying (outbound) network # verifying that it is an ESP packet. - self.assertSentPacket(vti) - pkt = self._ExpectEspPacketOn(vti.underlying_netid, vti.out_spi, vti.tx, None, + self.assertSentPacket(tunnel) + pkt = self._ExpectEspPacketOn(tunnel.underlying_netid, tunnel.out_spi, tunnel.tx, None, local_outer, remote_outer) # Perform an address switcheroo so that the inner address of the remote - # end of the tunnel is now the address on the local VTI interface; this - # way, the twisted inner packet finds a destination via the VTI once + # end of the tunnel is now the address on the local tunnel interface; this + # way, the twisted inner packet finds a destination via the tunnel once # decrypted. remote = _GetRemoteInnerAddress(inner_version) - local = vti.addrs[inner_version] - self._SwapInterfaceAddress(vti.iface, new_addr=remote, old_addr=local) + local = tunnel.addrs[inner_version] + self._SwapInterfaceAddress(tunnel.iface, new_addr=remote, old_addr=local) try: # Swap the packet's IP headers and write it back to the # underlying network. pkt = TunTwister.TwistPacket(pkt) - self.ReceivePacketOn(vti.underlying_netid, pkt) - self.assertReceivedPacket(vti) + self.ReceivePacketOn(tunnel.underlying_netid, pkt) + self.assertReceivedPacket(tunnel) # Receive the decrypted packet on the dest port number. read_packet = read_sock.recv(4096) self.assertEquals(read_packet, net_test.UDP_PAYLOAD) finally: # Unwind the switcheroo - self._SwapInterfaceAddress(vti.iface, new_addr=local, old_addr=remote) + self._SwapInterfaceAddress(tunnel.iface, new_addr=local, old_addr=remote) # Now attempt to provoke an ICMP error. # TODO: deduplicate with multinetwork_test.py. - version = net_test.GetAddressVersion(vti.remote) + version = net_test.GetAddressVersion(tunnel.remote) dst_prefix, intermediate = { 4: ("172.19.", "172.16.9.12"), 6: ("2001:db8::", "2001:db8::1") @@ -395,29 +494,47 @@ class XfrmVtiTest(xfrm_base.XfrmBaseTest): write_sock.sendto(net_test.UDP_PAYLOAD, (_GetRemoteInnerAddress(inner_version), port)) - self.assertSentPacket(vti) - pkt = self._ExpectEspPacketOn(vti.underlying_netid, vti.out_spi, vti.tx, None, + self.assertSentPacket(tunnel) + pkt = self._ExpectEspPacketOn(tunnel.underlying_netid, tunnel.out_spi, tunnel.tx, None, local_outer, remote_outer) - myaddr = self.MyAddress(version, vti.underlying_netid) + myaddr = self.MyAddress(version, tunnel.underlying_netid) _, toobig = packets.ICMPPacketTooBig(version, intermediate, myaddr, pkt) - self.ReceivePacketOn(vti.underlying_netid, toobig) + self.ReceivePacketOn(tunnel.underlying_netid, toobig) # Check that the packet too big reduced the MTU. - routes = self.iproute.GetRoutes(vti.remote, 0, vti.underlying_netid, None) + routes = self.iproute.GetRoutes(tunnel.remote, 0, tunnel.underlying_netid, None) self.assertEquals(1, len(routes)) rtmsg, attributes = routes[0] self.assertEquals(iproute.RTN_UNICAST, rtmsg.type) self.assertEquals(packets.PTB_MTU, attributes["RTA_METRICS"]["RTAX_MTU"]) # Clear PMTU information so that future tests don't have to worry about it. - self.InvalidateDstCache(version, vti.underlying_netid) + self.InvalidateDstCache(version, tunnel.underlying_netid) - def testVtiInputOutput(self): + def CheckTunnelInputOutput(self): """Test packet input and output over a Virtual Tunnel Interface.""" - for i in xrange(3 * len(self.vtis.values())): - vti = random.choice(self.vtis.values()) - self._CheckVtiInputOutput(vti, 4) - self._CheckVtiInputOutput(vti, 6) + for i in xrange(3 * len(self.tunnels.values())): + tunnel = random.choice(self.tunnels.values()) + self._CheckTunnelInputOutput(tunnel, 4) + self._CheckTunnelInputOutput(tunnel, 6) + + +@unittest.skipUnless(net_test.LINUX_VERSION >= (3, 18, 0), "VTI Unsupported") +class XfrmVtiTest(XfrmTunnelBase): + + INTERFACE_CLASS = VtiInterface + + def testVtiInputOutput(self): + self.CheckTunnelInputOutput() + + +@unittest.skipUnless(HAVE_XFRM_INTERFACES, "XFRM interfaces unsupported") +class XfrmInterfaceTest(XfrmTunnelBase): + + INTERFACE_CLASS = XfrmInterface + + def testXfrmiInputOutput(self): + self.CheckTunnelInputOutput() if __name__ == "__main__": |