From f7699431b8717d77c24b517dfb8ef43065874698 Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Tue, 20 Mar 2018 17:31:14 -0700 Subject: Fix algorithm tests to ensure no lingering sockets Lingering TCP sockets appear to be causing testUpdatePolicy to fail due to policies attached to TCP sockets in TIME_WAIT. This patch sets SO_LINGER on the algorithm tests' sockets, forcing them to shutdown cleanly, and synchronously. Bug: 74071662 Test: Ran on common 3.18, 4.4 with parallel_tests.sh 20 10 Change-Id: Ie9ab9b7672cc2ce8309ed871eeee09ec8128aac9 --- net/test/xfrm_algorithm_test.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/net/test/xfrm_algorithm_test.py b/net/test/xfrm_algorithm_test.py index 90e7ee0..1de6e4b 100755 --- a/net/test/xfrm_algorithm_test.py +++ b/net/test/xfrm_algorithm_test.py @@ -252,6 +252,12 @@ class XfrmAlgorithmTest(xfrm_base.XfrmLazyTest): sock_right.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) self.SelectInterface(sock_right, netid, "mark") + # For UDP, set SO_LINGER to 0, to prevent TCP sockets from hanging around + # in a TIME_WAIT state. + if params["proto"] == SOCK_STREAM: + net_test.DisableFinWait(sock_left) + net_test.DisableFinWait(sock_right) + # Apply the left outbound socket policy. xfrm_base.ApplySocketPolicy(sock_left, family, xfrm.XFRM_POLICY_OUT, spi_right, req_ids[0], None) @@ -320,8 +326,6 @@ class XfrmAlgorithmTest(xfrm_base.XfrmLazyTest): sock_left.send("hello request") data = sock_left.recv(2048) self.assertEquals("hello response", data) - if params["proto"] == SOCK_STREAM: - sock_left.shutdown(SHUT_RD) sock_left.close() server.join() if server_error: -- cgit v1.2.3 From 2ab89c98a1431747fd2c1a2912732c26ba4c1124 Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Sun, 11 Feb 2018 21:39:35 -0800 Subject: Enable algorithm net tests for 3.18 kernels With the backport of 00bc0ef, 3.18 kernels no longer hang on the xfrm algorithm tests. This patch re-enables these tests for all 3.18 kernels. Bug: 63589559 Test: All kernel tests run, passing. Change-Id: I27b7e463a5566b1211ef6248f9239a52ff39d50d --- net/test/xfrm_algorithm_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/net/test/xfrm_algorithm_test.py b/net/test/xfrm_algorithm_test.py index 1de6e4b..6adc461 100755 --- a/net/test/xfrm_algorithm_test.py +++ b/net/test/xfrm_algorithm_test.py @@ -136,7 +136,6 @@ class XfrmAlgorithmTest(xfrm_base.XfrmLazyTest): new_name = new_name.replace("(", "-").replace(")", "") # remove parens setattr(cls, new_name, TestClosure) - @unittest.skipIf(net_test.LINUX_VERSION[:2] == (3, 18), "b/63589559") def ParamTestSocketPolicySimple(self, params): """Test two-way traffic using transport mode and socket policies.""" -- cgit v1.2.3 From c2b94100988f17e4a6eb43cad4e538ccd924e4ff Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 28 Mar 2018 06:35:03 +0900 Subject: Disable qtaguid tests if qtaguid is not present. Test: qtaguid_test correctly skipped on non-Android kernel Change-Id: I78310997342303403916328843765868f5d43aa0 --- net/test/qtaguid_test.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/test/qtaguid_test.py b/net/test/qtaguid_test.py index ad99a57..c121df2 100755 --- a/net/test/qtaguid_test.py +++ b/net/test/qtaguid_test.py @@ -27,7 +27,10 @@ import tcp_test CTRL_PROCPATH = "/proc/net/xt_qtaguid/ctrl" OTHER_UID_GID = 12345 +HAVE_QTAGUID = os.path.exists(CTRL_PROCPATH) + +@unittest.skipUnless(HAVE_QTAGUID, "xt_qtaguid not supported") class QtaguidTest(tcp_test.TcpBaseTest): def RunIptablesCommand(self, args): -- cgit v1.2.3 From 3f132dc23b080bd6c1951606346ade7e1dbadd41 Mon Sep 17 00:00:00 2001 From: manojboopathi Date: Fri, 2 Feb 2018 14:13:48 -0800 Subject: Verify VTI Modification using RTM_NEWLINK This test checks that an existing VTI(6) can be updated using the RTM_NEWLINK without the EXCL flag. Bug: 72532478 Test: aosp/kernel/tests/net/test/run_net_test.sh Test: kernel_net_tests (on wahoo/walleye) Change-Id: Iecb7bb7f070dfbdd098dbb773d18ca21f23a42da --- net/test/iproute.py | 30 +++++++++++++++++++++++------- net/test/xfrm_tunnel_test.py | 26 ++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/net/test/iproute.py b/net/test/iproute.py index 19b1e26..9cfafc6 100644 --- a/net/test/iproute.py +++ b/net/test/iproute.py @@ -282,6 +282,8 @@ class IPRoute(netlink.NetlinkSocket): name = self._GetConstantName(nla_type, "RTAX_") elif command == -IFLA_LINKINFO: name = self._GetConstantName(nla_type, "IFLA_INFO_") + elif command == -IFLA_INFO_DATA: + name = self._GetConstantName(nla_type, "IFLA_VTI_") elif CommandSubject(command) == "ADDR": name = self._GetConstantName(nla_type, "IFA_") elif CommandSubject(command) == "LINK": @@ -304,6 +306,8 @@ class IPRoute(netlink.NetlinkSocket): "RTAX_HOPLIMIT", "IFLA_CARRIER_CHANGES", "IFLA_GSO_MAX_SEGS", "IFLA_GSO_MAX_SIZE", "RTA_UID"]: data = struct.unpack("=I", nla_data)[0] + elif name in ["IFLA_VTI_OKEY", "IFLA_VTI_IKEY"]: + data = struct.unpack("!I", nla_data)[0] elif name == "FRA_SUPPRESS_PREFIXLEN": data = struct.unpack("=i", nla_data)[0] elif name in ["IFLA_LINKMODE", "IFLA_OPERSTATE", "IFLA_CARRIER"]: @@ -318,6 +322,8 @@ class IPRoute(netlink.NetlinkSocket): data = self._ParseAttributes(-RTA_METRICS, None, nla_data, nested + 1) elif name == "IFLA_LINKINFO": data = self._ParseAttributes(-IFLA_LINKINFO, None, nla_data, nested + 1) + elif name == "IFLA_INFO_DATA": + data = self._ParseAttributes(-IFLA_INFO_DATA, None, nla_data) elif name == "RTA_CACHEINFO": data = RTACacheinfo(nla_data) elif name == "IFA_CACHEINFO": @@ -350,8 +356,8 @@ class IPRoute(netlink.NetlinkSocket): if CommandVerb(command) != "GET": flags |= netlink.NLM_F_ACK if CommandVerb(command) == "NEW": - if not flags & netlink.NLM_F_REPLACE: - flags |= (netlink.NLM_F_EXCL | netlink.NLM_F_CREATE) + if flags & (netlink.NLM_F_REPLACE | netlink.NLM_F_CREATE) == 0: + flags |= netlink.NLM_F_CREATE | netlink.NLM_F_EXCL super(IPRoute, self)._SendNlRequest(command, data, flags) @@ -640,8 +646,7 @@ class IPRoute(netlink.NetlinkSocket): def DeleteLink(self, dev_name): ifinfo = IfinfoMsg().Pack() ifinfo += self._NlAttrStr(IFLA_IFNAME, dev_name) - flags = netlink.NLM_F_REQUEST | netlink.NLM_F_ACK - return self._SendNlRequest(RTM_DELLINK, ifinfo, flags) + return self._SendNlRequest(RTM_DELLINK, ifinfo) def GetIfinfo(self, dev_name): """Fetches information about the specified interface. @@ -654,7 +659,7 @@ class IPRoute(netlink.NetlinkSocket): """ ifinfo = IfinfoMsg().Pack() ifinfo += self._NlAttrStr(IFLA_IFNAME, dev_name) - self._SendNlRequest(RTM_GETLINK, ifinfo, netlink.NLM_F_REQUEST) + self._SendNlRequest(RTM_GETLINK, ifinfo) hdr, data = cstruct.Read(self._Recv(), netlink.NLMsgHdr) if hdr.type == RTM_NEWLINK: return cstruct.Read(data, IfinfoMsg) @@ -675,12 +680,18 @@ class IPRoute(netlink.NetlinkSocket): attrs = self._ParseAttributes(RTM_NEWLINK, IfinfoMsg, attrs) return attrs["IFLA_STATS64"] + def GetVtiInfoData(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) + return attrs["IFLA_LINKINFO"]["IFLA_INFO_DATA"] + def GetRxTxPackets(self, dev_name): stats = self.GetIfaceStats(dev_name) return stats.rx_packets, stats.tx_packets def CreateVirtualTunnelInterface(self, dev_name, local_addr, remote_addr, - i_key=None, o_key=None): + i_key=None, o_key=None, is_update=False): """ Create a Virtual Tunnel Interface that provides a proxy interface for IPsec tunnels. @@ -725,7 +736,12 @@ class IPRoute(netlink.NetlinkSocket): ifinfo += self._NlAttr(IFLA_LINKINFO, linkinfo) - return self._SendNlRequest(RTM_NEWLINK, ifinfo) + # Always pass CREATE to prevent _SendNlRequest() from incorrectly + # guessing the flags. + flags = netlink.NLM_F_CREATE + if not is_update: + flags |= netlink.NLM_F_EXCL + return self._SendNlRequest(RTM_NEWLINK, ifinfo, flags) if __name__ == "__main__": diff --git a/net/test/xfrm_tunnel_test.py b/net/test/xfrm_tunnel_test.py index 94e846e..ac340d9 100755 --- a/net/test/xfrm_tunnel_test.py +++ b/net/test/xfrm_tunnel_test.py @@ -106,6 +106,13 @@ 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): + 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) def testAddVti(self): """Test the creation of a Virtual Tunnel Interface.""" @@ -118,6 +125,25 @@ 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), + _TEST_IKEY, _TEST_OKEY) + + new_remote_addr = {4: net_test.IPV4_ADDR2, 6: net_test.IPV6_ADDR2} + new_okey = _TEST_OKEY + _VTI_NETID + new_ikey = _TEST_IKEY + _VTI_NETID + self.iproute.CreateVirtualTunnelInterface( + dev_name=_VTI_IFNAME, + local_addr=local_addr, + remote_addr=new_remote_addr[version], + o_key=new_okey, + i_key=new_ikey, + is_update=True) + + self.verifyVtiInfoData(self.iproute.GetVtiInfoData(_VTI_IFNAME), + version, local_addr, new_remote_addr[version], + new_ikey, new_okey) + if_index = self.iproute.GetIfIndex(_VTI_IFNAME) # Validate that the netlink interface matches the ioctl interface. -- cgit v1.2.3 From ec4e4087ae4d6dba3518df13e8ee4a5716c1e654 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 19 Apr 2018 17:55:20 +0900 Subject: Be flexible about TCP RST and SOCK_DESTROY poll return values. The intent of PollOnCloseTest is to ensure that SOCK_DESTROY behaves in the same way with respect to poll() as receiving a TCP RST packet would. Unfortunately, due to a race inside the kernel, the return values of poll() in these circumstances is non-deterministic. The race when processing a TCP RST has been fixed, but the one when processing SOCK_DESTROY has not. Ensure that the test does not insist on specific return values on kernels that do not guarantee them. Also ensure that any assertion failures in SocketExceptionThread are passed on to the main thread and properly cause the test to fail. Bug: 73654768 Test: sock_diag_test passes 100 times in a row on taimen PPR1.180418.002. Change-Id: I509e23a67be842d3d112a165f1439d29c699b01f --- net/test/sock_diag_test.py | 50 +++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/net/test/sock_diag_test.py b/net/test/sock_diag_test.py index 9419587..e25035b 100755 --- a/net/test/sock_diag_test.py +++ b/net/test/sock_diag_test.py @@ -457,7 +457,7 @@ class SocketExceptionThread(threading.Thread): def run(self): try: self.operation(self.sock) - except IOError, e: + except (IOError, AssertionError), e: self.exception = e @@ -712,14 +712,26 @@ class PollOnCloseTest(tcp_test.TcpBaseTest, SockDiagBaseTest): super(PollOnCloseTest, self).setUp() self.netid = random.choice(self.tuns.keys()) - def BlockingPoll(self, sock, mask, expected_event): + POLL_FLAGS = [(select.POLLIN, "IN"), (select.POLLOUT, "OUT"), + (select.POLLERR, "ERR"), (select.POLLHUP, "HUP")] + + def PollResultToString(self, poll_events, ignoremask): + out = [] + for fd, event in poll_events: + flags = [name for (flag, name) in self.POLL_FLAGS + if event & flag & ~ignoremask != 0] + out.append((fd, "|".join(flags))) + return out + + def BlockingPoll(self, sock, mask, expected, ignoremask): p = select.poll() p.register(sock, mask) - expected_fds = [(sock.fileno(), expected_event)] + expected_fds = [(sock.fileno(), expected)] # Don't block forever or we'll hang continuous test runs on failure. # A 5-second timeout should be long enough not to be flaky. actual_fds = p.poll(5000) - self.assertEqual(expected_fds, actual_fds) + self.assertEqual(self.PollResultToString(expected_fds, ignoremask), + self.PollResultToString(actual_fds, ignoremask)) def RstDuringBlockingCall(self, sock, call, expected_errno): self._EventDuringBlockingCall( @@ -735,43 +747,53 @@ class PollOnCloseTest(tcp_test.TcpBaseTest, SockDiagBaseTest): self.assertEquals("", self.accepted.recv(4096)) self.assertEquals("", self.accepted.recv(4096)) - def CheckPollDestroy(self, mask, expected_event): + def CheckPollDestroy(self, mask, expected, ignoremask): """Interrupts a poll() with SOCK_DESTROY.""" for version in [4, 5, 6]: self.IncomingConnection(version, tcp_test.TCP_ESTABLISHED, self.netid) self.CloseDuringBlockingCall( self.accepted, - lambda sock: self.BlockingPoll(sock, mask, expected_event), + lambda sock: self.BlockingPoll(sock, mask, expected, ignoremask), None) self.assertSocketErrors(ECONNABORTED) - def CheckPollRst(self, mask, expected_event): + def CheckPollRst(self, mask, expected, ignoremask): """Interrupts a poll() by receiving a TCP RST.""" for version in [4, 5, 6]: self.IncomingConnection(version, tcp_test.TCP_ESTABLISHED, self.netid) self.RstDuringBlockingCall( self.accepted, - lambda sock: self.BlockingPoll(sock, mask, expected_event), + lambda sock: self.BlockingPoll(sock, mask, expected, ignoremask), None) self.assertSocketErrors(ECONNRESET) def testReadPollRst(self): - self.CheckPollRst(select.POLLIN, self.POLLIN_ERR_HUP) + # Until 3d4762639d ("tcp: remove poll() flakes when receiving RST"), poll() + # would sometimes return POLLERR and sometimes POLLIN|POLLERR|POLLHUP. This + # is due to a race inside the kernel and thus is not visible on the VM, only + # on physical hardware. + if net_test.LINUX_VERSION < (4, 14, 0): + ignoremask = select.POLLIN | select.POLLHUP + else: + ignoremask = 0 + self.CheckPollRst(select.POLLIN, self.POLLIN_ERR_HUP, ignoremask) def testWritePollRst(self): - self.CheckPollRst(select.POLLOUT, select.POLLOUT) + self.CheckPollRst(select.POLLOUT, select.POLLOUT, 0) def testReadWritePollRst(self): - self.CheckPollRst(self.POLLIN_OUT, select.POLLOUT) + self.CheckPollRst(self.POLLIN_OUT, select.POLLOUT, 0) def testReadPollDestroy(self): - self.CheckPollDestroy(select.POLLIN, self.POLLIN_ERR_HUP) + # tcp_abort has the same race that tcp_reset has, but it's not fixed yet. + ignoremask = select.POLLIN | select.POLLHUP + self.CheckPollDestroy(select.POLLIN, self.POLLIN_ERR_HUP, ignoremask) def testWritePollDestroy(self): - self.CheckPollDestroy(select.POLLOUT, select.POLLOUT) + self.CheckPollDestroy(select.POLLOUT, select.POLLOUT, 0) def testReadWritePollDestroy(self): - self.CheckPollDestroy(self.POLLIN_OUT, select.POLLOUT) + self.CheckPollDestroy(self.POLLIN_OUT, select.POLLOUT, 0) @unittest.skipUnless(HAVE_UDP_DIAG, "INET_UDP_DIAG not enabled") -- cgit v1.2.3 From 53cc9439b14431197f662beb947f326c3ff9ea56 Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Tue, 17 Apr 2018 13:42:42 -0700 Subject: Set SA mark to unused for Tunnel Mode This change updates the kernel network tests to reflect how the platform will be using IPsec. Specifically, limitations regarding how SAs are created require SPIs be allocated first, and the kernel does not currently support updating marks on larval SAs. Bug: 78126996 Test: Ran on 3.18, 4.4, 4.9 and net-next kernels Change-Id: Iba12f10621630b2d1d9dc75187327d6388dd4ffa --- net/test/xfrm.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/test/xfrm.py b/net/test/xfrm.py index 1bd10da..2fb466a 100755 --- a/net/test/xfrm.py +++ b/net/test/xfrm.py @@ -610,15 +610,17 @@ class Xfrm(netlink.NetlinkSocket): encryption: A tuple (XfrmAlgo, key), the encryption parameters. auth_trunc: A tuple (XfrmAlgoAuth, key), the authentication parameters. mark: An XfrmMark, the mark used for selecting packets to be tunneled, and - for matching the security policy and security association. None means - unspecified. + 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. """ outer_family = net_test.GetAddressFamily(net_test.GetAddressVersion(dst)) + # Device code does not use mark; during AllocSpi, the mark is unset, and + # 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, mark, output_mark) + None, None, None, output_mark) if selector is None: selectors = [EmptySelector(AF_INET), EmptySelector(AF_INET6)] -- cgit v1.2.3 From ab297908759d64ca023597dfdeac6ebc57114e98 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 24 Nov 2017 01:54:35 +0900 Subject: Test experimental xfrm interfaces if supported. This test adds code to transparently test VTI and/or the new xfrmi interface type depending on what is supported. Bug: 70371070 Test: all xfrm tests pass on ipsec-next-xfrmi2 Test: all xfrm tests pass on android-4.9 Change-Id: If52921bbb60ad2fa05313a3ce1156b37a50476af --- net/test/iproute.py | 26 ++++- net/test/run_net_test.sh | 2 +- net/test/xfrm.py | 74 +++++++++--- net/test/xfrm_test.py | 6 +- 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__": -- cgit v1.2.3 From 493cfb50cb18677628f0ae62dd8a14036a8eec0c Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Tue, 24 Apr 2018 14:21:10 -0700 Subject: Test for getFirstMapKey of bpf maps The upstream commit (8fe45924387be6b5c1be59a7eb330790c61d5d10 bpf: map_get_next_key to return first key on NULL) introduce the support for bpf maps to return the first key of the map when receive a NULL key from userspace. This change will test this functionality in supported kernels to make sure they have the correct backport. Test: ./bpf_test.py Bug: 30950746 Change-Id: I6a497a3ccb518ebb037bfa447f827d2715271291 --- net/test/bpf.py | 11 +++++++++-- net/test/bpf_test.py | 49 ++++++++++++++++++++++++++++++------------------- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/net/test/bpf.py b/net/test/bpf.py index ff23d79..c9ad264 100755 --- a/net/test/bpf.py +++ b/net/test/bpf.py @@ -195,13 +195,20 @@ def LookupMap(map_fd, key): def GetNextKey(map_fd, key): - c_key = ctypes.c_uint32(key) + if key is not None: + c_key = ctypes.c_uint32(key) + c_next_key = ctypes.c_uint32(0) + key_ptr = ctypes.addressof(c_key) + else: + key_ptr = 0; c_next_key = ctypes.c_uint32(0) attr = BpfAttrOps( - (map_fd, ctypes.addressof(c_key), ctypes.addressof(c_next_key), 0)) + (map_fd, key_ptr, ctypes.addressof(c_next_key), 0)) BpfSyscall(BPF_MAP_GET_NEXT_KEY, attr) return c_next_key +def GetFirstKey(map_fd): + return GetNextKey(map_fd, None) def DeleteMap(map_fd, key): c_key = ctypes.c_uint32(key) diff --git a/net/test/bpf_test.py b/net/test/bpf_test.py index 9da3907..e7a4edb 100755 --- a/net/test/bpf_test.py +++ b/net/test/bpf_test.py @@ -29,7 +29,6 @@ import net_test import sock_diag libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True) -HAVE_EBPF_SUPPORT = net_test.LINUX_VERSION >= (4, 9, 0) HAVE_EBPF_ACCOUNTING = net_test.LINUX_VERSION >= (4, 9, 0) KEY_SIZE = 8 VALUE_SIZE = 4 @@ -146,8 +145,8 @@ INS_BPF_PARAM_STORE = [ BpfStxMem(BPF_DW, BPF_REG_10, BPF_REG_0, key_offset), ] -@unittest.skipUnless(HAVE_EBPF_SUPPORT, - "eBPF function not fully supported") +@unittest.skipUnless(HAVE_EBPF_ACCOUNTING, + "BPF helper function is not fully supported") class BpfTest(net_test.NetworkTest): def setUp(self): @@ -172,31 +171,43 @@ class BpfTest(net_test.NetworkTest): DeleteMap(self.map_fd, key) self.assertRaisesErrno(errno.ENOENT, LookupMap, self.map_fd, key) - def testIterateMap(self): - self.map_fd = CreateMap(BPF_MAP_TYPE_HASH, KEY_SIZE, VALUE_SIZE, - TOTAL_ENTRIES) - value = 1024 - for key in xrange(1, TOTAL_ENTRIES): - UpdateMap(self.map_fd, key, value) - for key in xrange(1, TOTAL_ENTRIES): - self.assertEquals(value, LookupMap(self.map_fd, key).value) - self.assertRaisesErrno(errno.ENOENT, LookupMap, self.map_fd, 101) - key = 0 + def CheckAllMapEntry(self, nonexistent_key, totalEntries, value): count = 0 - while 1: - if count == TOTAL_ENTRIES - 1: + key = nonexistent_key + while True: + if count == totalEntries: self.assertRaisesErrno(errno.ENOENT, GetNextKey, self.map_fd, key) break else: result = GetNextKey(self.map_fd, key) key = result.value - self.assertGreater(key, 0) + self.assertGreaterEqual(key, 0) self.assertEquals(value, LookupMap(self.map_fd, key).value) count += 1 - # TODO: move this check to the begining of the class insdead. - @unittest.skipUnless(HAVE_EBPF_ACCOUNTING, - "BPF helper function is not fully supported") + def testIterateMap(self): + self.map_fd = CreateMap(BPF_MAP_TYPE_HASH, KEY_SIZE, VALUE_SIZE, + TOTAL_ENTRIES) + value = 1024 + for key in xrange(0, TOTAL_ENTRIES): + UpdateMap(self.map_fd, key, value) + for key in xrange(0, TOTAL_ENTRIES): + self.assertEquals(value, LookupMap(self.map_fd, key).value) + self.assertRaisesErrno(errno.ENOENT, LookupMap, self.map_fd, 101) + nonexistent_key = -1 + self.CheckAllMapEntry(nonexistent_key, TOTAL_ENTRIES, value) + + def testFindFirstMapKey(self): + self.map_fd = CreateMap(BPF_MAP_TYPE_HASH, KEY_SIZE, VALUE_SIZE, + TOTAL_ENTRIES) + value = 1024 + for key in xrange(0, TOTAL_ENTRIES): + UpdateMap(self.map_fd, key, value) + firstKey = GetFirstKey(self.map_fd) + key = firstKey.value + self.CheckAllMapEntry(key, TOTAL_ENTRIES - 1, value) + + def testRdOnlyMap(self): self.map_fd = CreateMap(BPF_MAP_TYPE_HASH, KEY_SIZE, VALUE_SIZE, TOTAL_ENTRIES, map_flags=BPF_F_RDONLY) -- cgit v1.2.3 From f1231b26342240949f4186bedc5b452fb0e55e5c Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Fri, 11 May 2018 17:24:51 -0700 Subject: Fix the flaky cgroup uid bpf test The cgroup uid traffic test is recording all the packets through the default uid (usually root uid). If there are other networking traffic happens when the test is running on a device, the total packets count will be incorrect. This can be fixed by generating traffic in a socket belong to a special test uid. Test: bpf_test pass on device Bug: 79488922 Change-Id: I34089336e48a0b1ffb6d5a5344dea444494c39dd --- net/test/bpf_test.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/net/test/bpf_test.py b/net/test/bpf_test.py index e7a4edb..270243e 100755 --- a/net/test/bpf_test.py +++ b/net/test/bpf_test.py @@ -33,6 +33,7 @@ HAVE_EBPF_ACCOUNTING = net_test.LINUX_VERSION >= (4, 9, 0) KEY_SIZE = 8 VALUE_SIZE = 4 TOTAL_ENTRIES = 20 +TEST_UID = 54321 # Offset to store the map key in stack register REG10 key_offset = -8 # Offset to store the map value in stack register REG10 @@ -300,7 +301,7 @@ class BpfTest(net_test.NetworkTest): + INS_SK_FILTER_ACCEPT) self.prog_fd = BpfProgLoad(BPF_PROG_TYPE_SOCKET_FILTER, instructions) packet_count = 10 - uid = 12345 + uid = TEST_UID with net_test.RunAsUid(uid): self.assertRaisesErrno(errno.ENOENT, LookupMap, self.map_fd, uid) SocketUDPLoopBack(packet_count, 4, self.prog_fd) @@ -385,13 +386,15 @@ class BpfCgroupTest(net_test.NetworkTest): + INS_CGROUP_ACCEPT + INS_PACK_COUNT_UPDATE + INS_CGROUP_ACCEPT) self.prog_fd = BpfProgLoad(BPF_PROG_TYPE_CGROUP_SKB, instructions) BpfProgAttach(self.prog_fd, self._cg_fd, BPF_CGROUP_INET_INGRESS) - uid = os.getuid() packet_count = 20 - SocketUDPLoopBack(packet_count, 4, None) - self.assertEquals(packet_count, LookupMap(self.map_fd, uid).value) - DeleteMap(self.map_fd, uid); - SocketUDPLoopBack(packet_count, 6, None) - self.assertEquals(packet_count, LookupMap(self.map_fd, uid).value) + uid = TEST_UID + with net_test.RunAsUid(uid): + self.assertRaisesErrno(errno.ENOENT, LookupMap, self.map_fd, uid) + SocketUDPLoopBack(packet_count, 4, None) + self.assertEquals(packet_count, LookupMap(self.map_fd, uid).value) + DeleteMap(self.map_fd, uid); + SocketUDPLoopBack(packet_count, 6, None) + self.assertEquals(packet_count, LookupMap(self.map_fd, uid).value) BpfProgDetach(self._cg_fd, BPF_CGROUP_INET_INGRESS) if __name__ == "__main__": -- cgit v1.2.3 From 7993a85525c0ce459f424a2152ea24b972c6acdc Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Thu, 10 May 2018 11:59:26 -0700 Subject: Add __NR_bpf constant for i686. This adds support for running the net tests on 32-bit x86. It was tested using the 64-bit cuttlefish kernel (4.9). Bug: 79532682 Test: vts_kernel_tests ran to completion on cuttlefish Change-Id: I5a8ade3a2292734001dd6e85f9c6b5cefc8de9c1 Signed-off-by: Alistair Strachan --- net/test/bpf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/test/bpf.py b/net/test/bpf.py index c9ad264..24dc7a1 100755 --- a/net/test/bpf.py +++ b/net/test/bpf.py @@ -27,7 +27,9 @@ import socket __NR_bpf = { "aarch64": 280, "armv8l": 386, - "x86_64": 321}[os.uname()[4]] + "i686" : 357, + "x86_64": 321, +}[os.uname()[4]] LOG_LEVEL = 1 LOG_SIZE = 65536 -- cgit v1.2.3 From 90bc84211ef0a71ec36187e94d7fe13f851465ab Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Thu, 10 May 2018 11:48:33 -0700 Subject: Fix selection of bpf syscall number with COMPAT_UTS_MACHINE. If the python interpreter is 64-bit but was launched from a 32-bit environment (this can happen if it is run from a chroot or container), then 'uname -m' and python's os.uname()[4] will return 'i686' instead of x86_64 for questionable compatibility reasons. This breaks the assumption in the bpf.py test that 'uname -m' reliably returns the personality of the python interpreter, which determines the syscall number to use for bpf. Harden the assumptions made by the test by qualifying the architecture test with the bitness of the python interpreter. We can then correctly determine when to use the 64-bit syscall number. This change has only been tested on x86. The ARM part of the change is very likely to be correct, and cannot make things worse, but I did not have a device with a 64-bit kernel and 32-bit userspace available to test. Bug: 79532682 Test: ./all_tests.sh ran to completion on cuttlefish Change-Id: I1e9e62ba22344db121e83e97906bc1dcbcb51386 Signed-off-by: Alistair Strachan --- net/test/bpf.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/net/test/bpf.py b/net/test/bpf.py index 24dc7a1..7625265 100755 --- a/net/test/bpf.py +++ b/net/test/bpf.py @@ -21,15 +21,23 @@ import csocket import cstruct import net_test import socket +import platform # __NR_bpf syscall numbers for various architectures. +# NOTE: If python inherited COMPAT_UTS_MACHINE, uname's 'machine' field will +# return the 32-bit architecture name, even if python itself is 64-bit. To work +# around this problem and pick the right syscall nr, we can additionally check +# the bitness of the python interpreter. Assume that the 64-bit architectures +# are not running with COMPAT_UTS_MACHINE and must be 64-bit at all times. # TODO: is there a better way of doing this? __NR_bpf = { - "aarch64": 280, - "armv8l": 386, - "i686" : 357, - "x86_64": 321, -}[os.uname()[4]] + "aarch64-64bit": 280, + "armv8l-32bit": 386, + "armv8l-64bit": 280, + "i686-32bit": 357, + "i686-64bit": 321, + "x86_64-64bit": 321, +}[os.uname()[4] + "-" + platform.architecture()[0]] LOG_LEVEL = 1 LOG_SIZE = 65536 -- cgit v1.2.3 From 5055c07de545510a2eb58ff699e7087a06959815 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Thu, 10 May 2018 15:36:55 -0700 Subject: Drop unnecessary CONFIG_ prefixes. The scripts/config command doesn't need you to specify the prefix, and in my testing, it'll actually duplicate it. I'm not sure how this has been working, but it's definitely wrong. Remove the CONFIG_ prefixes. Bug: 79546208 Change-Id: I27abde2e86a9b4c0fe12a6472a7c1032f7b53b7b Signed-off-by: Alistair Strachan --- net/test/run_net_test.sh | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index c7c18d3..4dcf5df 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -9,20 +9,20 @@ OPTIONS="$OPTIONS NETFILTER_XT_MARK NETFILTER_XT_TARGET_MARK" OPTIONS="$OPTIONS IP_NF_IPTABLES IP_NF_MANGLE IP_NF_FILTER" OPTIONS="$OPTIONS IP6_NF_IPTABLES IP6_NF_MANGLE IP6_NF_FILTER INET6_IPCOMP" OPTIONS="$OPTIONS IPV6_PRIVACY IPV6_OPTIMISTIC_DAD" -OPTIONS="$OPTIONS CONFIG_IPV6_ROUTE_INFO CONFIG_IPV6_ROUTER_PREF" -OPTIONS="$OPTIONS CONFIG_NETFILTER_XT_TARGET_NFLOG" -OPTIONS="$OPTIONS CONFIG_NETFILTER_XT_MATCH_QUOTA" -OPTIONS="$OPTIONS CONFIG_NETFILTER_XT_MATCH_QUOTA2" -OPTIONS="$OPTIONS CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG" -OPTIONS="$OPTIONS CONFIG_NETFILTER_TPROXY" -OPTIONS="$OPTIONS CONFIG_NETFILTER_XT_MATCH_SOCKET" -OPTIONS="$OPTIONS CONFIG_NETFILTER_XT_MATCH_QTAGUID" -# For 4.14 CONFIG_NETFILTER_XT_MATCH_SOCKET demands CONFIG_NF_SOCKET_IPV4/6 -OPTIONS="$OPTIONS CONFIG_NF_SOCKET_IPV4 CONFIG_NF_SOCKET_IPV6" -OPTIONS="$OPTIONS CONFIG_INET_UDP_DIAG CONFIG_INET_DIAG_DESTROY" +OPTIONS="$OPTIONS IPV6_ROUTE_INFO IPV6_ROUTER_PREF" +OPTIONS="$OPTIONS NETFILTER_XT_TARGET_NFLOG" +OPTIONS="$OPTIONS NETFILTER_XT_MATCH_QUOTA" +OPTIONS="$OPTIONS NETFILTER_XT_MATCH_QUOTA2" +OPTIONS="$OPTIONS NETFILTER_XT_MATCH_QUOTA2_LOG" +OPTIONS="$OPTIONS NETFILTER_TPROXY" +OPTIONS="$OPTIONS NETFILTER_XT_MATCH_SOCKET" +OPTIONS="$OPTIONS NETFILTER_XT_MATCH_QTAGUID" +# For 4.14 NETFILTER_XT_MATCH_SOCKET demands NF_SOCKET_IPV4/6 +OPTIONS="$OPTIONS NF_SOCKET_IPV4 NF_SOCKET_IPV6" +OPTIONS="$OPTIONS INET_UDP_DIAG INET_DIAG_DESTROY" OPTIONS="$OPTIONS IP_SCTP INET_SCTP_DIAG" -OPTIONS="$OPTIONS CONFIG_IP_NF_TARGET_REJECT CONFIG_IP_NF_TARGET_REJECT_SKERR" -OPTIONS="$OPTIONS CONFIG_IP6_NF_TARGET_REJECT CONFIG_IP6_NF_TARGET_REJECT_SKERR" +OPTIONS="$OPTIONS IP_NF_TARGET_REJECT IP_NF_TARGET_REJECT_SKERR" +OPTIONS="$OPTIONS IP6_NF_TARGET_REJECT IP6_NF_TARGET_REJECT_SKERR" OPTIONS="$OPTIONS BPF_SYSCALL NET_KEY XFRM_USER XFRM_STATISTICS CRYPTO_CBC" OPTIONS="$OPTIONS CRYPTO_CTR CRYPTO_HMAC CRYPTO_AES CRYPTO_SHA1 CRYPTO_SHA256" OPTIONS="$OPTIONS CRYPTO_SHA12 CRYPTO_USER INET_AH INET_ESP INET_XFRM_MODE" @@ -30,18 +30,19 @@ 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 CONFIG_XFRM_INTERFACE" +OPTIONS="$OPTIONS SOCK_CGROUP_DATA CGROUP_BPF XFRM_INTERFACE" # For 4.14 kernels, where UBD and HOSTFS are not set -OPTIONS="$OPTIONS CONFIG_BLK_DEV_UBD CONFIG_HOSTFS" +OPTIONS="$OPTIONS BLK_DEV_UBD HOSTFS" # For 3.1 kernels, where devtmpfs is not on by default. OPTIONS="$OPTIONS DEVTMPFS DEVTMPFS_MOUNT" # These two break the flo kernel due to differences in -Werror on recent GCC. -DISABLE_OPTIONS=" CONFIG_REISERFS_FS CONFIG_ANDROID_PMEM" +DISABLE_OPTIONS=" REISERFS_FS ANDROID_PMEM" + # This one breaks the fugu kernel due to a nonexistent sem_wait_array. -DISABLE_OPTIONS="$DISABLE_OPTIONS CONFIG_SYSVIPC" +DISABLE_OPTIONS="$DISABLE_OPTIONS SYSVIPC" # How many TAP interfaces to create to provide the VM with real network access # via the host. This requires privileges (e.g., root access) on the host. -- cgit v1.2.3 From 8f52a621c3cdf3844d089a32030b51a2df52f13a Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Thu, 10 May 2018 15:48:44 -0700 Subject: Fix some invalid config options. INET_AH, INET6_AH - These options are never used and not tested INET_MODE_XFRM, TRANSPORT - Option string had become split. Merged. CRYPTO_SHA12 - Not a valid kernel option. SHA512 is specified below. CRYPTO_SHA256 - Duplicate definition removed. Bug: 79546208 Change-Id: I954791bd764dc6f7d19ae1dfda66a29aede83245 Signed-off-by: Alistair Strachan --- net/test/run_net_test.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index 4dcf5df..8ab645d 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -24,9 +24,9 @@ OPTIONS="$OPTIONS IP_SCTP INET_SCTP_DIAG" OPTIONS="$OPTIONS IP_NF_TARGET_REJECT IP_NF_TARGET_REJECT_SKERR" OPTIONS="$OPTIONS IP6_NF_TARGET_REJECT IP6_NF_TARGET_REJECT_SKERR" OPTIONS="$OPTIONS BPF_SYSCALL NET_KEY XFRM_USER XFRM_STATISTICS CRYPTO_CBC" -OPTIONS="$OPTIONS CRYPTO_CTR CRYPTO_HMAC CRYPTO_AES CRYPTO_SHA1 CRYPTO_SHA256" -OPTIONS="$OPTIONS CRYPTO_SHA12 CRYPTO_USER INET_AH INET_ESP INET_XFRM_MODE" -OPTIONS="$OPTIONS TRANSPORT INET_XFRM_MODE_TUNNEL INET6_AH INET6_ESP" +OPTIONS="$OPTIONS CRYPTO_CTR CRYPTO_HMAC CRYPTO_AES CRYPTO_SHA1" +OPTIONS="$OPTIONS CRYPTO_USER INET_ESP INET_XFRM_MODE_TRANSPORT" +OPTIONS="$OPTIONS INET_XFRM_MODE_TUNNEL 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" -- cgit v1.2.3 From ab987108fa98a213d56f3b99028579a3c83517da Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Thu, 10 May 2018 16:44:52 -0700 Subject: Annotate non-common kernel config options. Annotate any kernel config option not present in all the kernel branches regularly being tested (3.10, 3.18, 4.4, 4.9, 4.14) with when the option was added or removed, and whether it was an Android kernel option or not. These annotations are useful when cross-checking the net tests list with the kernel/configs project. Bug: 79546208 Change-Id: Ie501624818025b9d70c2af53d4b23ce1c7225b0b Signed-off-by: Alistair Strachan --- net/test/run_net_test.sh | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index 8ab645d..17b1891 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -1,42 +1,49 @@ #!/bin/bash -# Kernel configuration options. +# Common kernel options OPTIONS=" DEBUG_SPINLOCK DEBUG_ATOMIC_SLEEP DEBUG_MUTEXES DEBUG_RT_MUTEXES" +OPTIONS="$OPTIONS DEVTMPFS DEVTMPFS_MOUNT" OPTIONS="$OPTIONS IPV6 IPV6_ROUTER_PREF IPV6_MULTIPLE_TABLES IPV6_ROUTE_INFO" OPTIONS="$OPTIONS TUN SYN_COOKIES IP_ADVANCED_ROUTER IP_MULTIPLE_TABLES" OPTIONS="$OPTIONS NETFILTER NETFILTER_ADVANCED NETFILTER_XTABLES" OPTIONS="$OPTIONS NETFILTER_XT_MARK NETFILTER_XT_TARGET_MARK" OPTIONS="$OPTIONS IP_NF_IPTABLES IP_NF_MANGLE IP_NF_FILTER" OPTIONS="$OPTIONS IP6_NF_IPTABLES IP6_NF_MANGLE IP6_NF_FILTER INET6_IPCOMP" -OPTIONS="$OPTIONS IPV6_PRIVACY IPV6_OPTIMISTIC_DAD" +OPTIONS="$OPTIONS IPV6_OPTIMISTIC_DAD" OPTIONS="$OPTIONS IPV6_ROUTE_INFO IPV6_ROUTER_PREF" OPTIONS="$OPTIONS NETFILTER_XT_TARGET_NFLOG" OPTIONS="$OPTIONS NETFILTER_XT_MATCH_QUOTA" OPTIONS="$OPTIONS NETFILTER_XT_MATCH_QUOTA2" OPTIONS="$OPTIONS NETFILTER_XT_MATCH_QUOTA2_LOG" -OPTIONS="$OPTIONS NETFILTER_TPROXY" OPTIONS="$OPTIONS NETFILTER_XT_MATCH_SOCKET" OPTIONS="$OPTIONS NETFILTER_XT_MATCH_QTAGUID" -# For 4.14 NETFILTER_XT_MATCH_SOCKET demands NF_SOCKET_IPV4/6 -OPTIONS="$OPTIONS NF_SOCKET_IPV4 NF_SOCKET_IPV6" OPTIONS="$OPTIONS INET_UDP_DIAG INET_DIAG_DESTROY" -OPTIONS="$OPTIONS IP_SCTP INET_SCTP_DIAG" +OPTIONS="$OPTIONS IP_SCTP" OPTIONS="$OPTIONS IP_NF_TARGET_REJECT IP_NF_TARGET_REJECT_SKERR" OPTIONS="$OPTIONS IP6_NF_TARGET_REJECT IP6_NF_TARGET_REJECT_SKERR" -OPTIONS="$OPTIONS BPF_SYSCALL NET_KEY XFRM_USER XFRM_STATISTICS CRYPTO_CBC" +OPTIONS="$OPTIONS NET_KEY XFRM_USER XFRM_STATISTICS CRYPTO_CBC" OPTIONS="$OPTIONS CRYPTO_CTR CRYPTO_HMAC CRYPTO_AES CRYPTO_SHA1" OPTIONS="$OPTIONS CRYPTO_USER INET_ESP INET_XFRM_MODE_TRANSPORT" OPTIONS="$OPTIONS INET_XFRM_MODE_TUNNEL 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 XFRM_INTERFACE" - -# For 4.14 kernels, where UBD and HOSTFS are not set -OPTIONS="$OPTIONS BLK_DEV_UBD HOSTFS" - -# For 3.1 kernels, where devtmpfs is not on by default. -OPTIONS="$OPTIONS DEVTMPFS DEVTMPFS_MOUNT" +OPTIONS="$OPTIONS CRYPTO_GCM CRYPTO_ECHAINIV NET_IPVTI" + +# Kernel version specific options +OPTIONS="$OPTIONS XFRM_INTERFACE" # Various device kernels +OPTIONS="$OPTIONS BLK_DEV_UBD HOSTFS" # Unset by default in 4.14 +OPTIONS="$OPTIONS CGROUP_BPF" # Added in android-4.9 +OPTIONS="$OPTIONS NF_SOCKET_IPV4 NF_SOCKET_IPV6" # Added in 4.9 +OPTIONS="$OPTIONS INET_SCTP_DIAG" # Added in 4.7 +OPTIONS="$OPTIONS SOCK_CGROUP_DATA" # Added in 4.5 +OPTIONS="$OPTIONS CRYPTO_ECHAINIV" # Added in 4.1 +OPTIONS="$OPTIONS BPF_SYSCALL" # Added in 3.18 +OPTIONS="$OPTIONS IPV6_VTI" # Added in 3.13 +OPTIONS="$OPTIONS IPV6_PRIVACY" # Removed in 3.12 +OPTIONS="$OPTIONS NETFILTER_TPROXY" # Removed in 3.11 + +# Obsolete options present at some time in Android kernels +OPTIONS="$OPTIONS IP_NF_TARGET_REJECT_SKERR IP6_NF_TARGET_REJECT_SKERR" # These two break the flo kernel due to differences in -Werror on recent GCC. DISABLE_OPTIONS=" REISERFS_FS ANDROID_PMEM" -- cgit v1.2.3 From 473536b011ebc06a48cc43687104e82e6eacb97d Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Mon, 14 May 2018 12:58:46 -0700 Subject: Add support for running the harness with QEMU. If ARCH makes it down as something other than 'um', the harness switches to using QEMU instead of UML. It uses DEFCONFIG as a basis for the kernel, but the networking flags are enabled/disabled in the same way. Bug: 79694505 Test: Built tests locally, with and without UML Change-Id: Ifbd638adceb4f148fd9c5aa0c37c5df63fe2505e Signed-off-by: Alistair Strachan --- net/test/net_test.sh | 4 +- net/test/run_net_test.sh | 136 +++++++++++++++++++++++++++++++++++------------ 2 files changed, 105 insertions(+), 35 deletions(-) diff --git a/net/test/net_test.sh b/net/test/net_test.sh index bade6de..e2ed6b1 100755 --- a/net/test/net_test.sh +++ b/net/test/net_test.sh @@ -22,6 +22,6 @@ fi echo -e "Running $net_test $net_test_args\n" $net_test $net_test_args -# Write exit code of net_test to /proc/exitcode so that the builder can use it +# Write exit code of net_test to a file so that the builder can use it # to signal failure if any tests fail. -echo $? >/proc/exitcode +echo $? >$net_test_exitcode diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index 17b1891..f81fb89 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -31,7 +31,6 @@ OPTIONS="$OPTIONS CRYPTO_GCM CRYPTO_ECHAINIV NET_IPVTI" # Kernel version specific options OPTIONS="$OPTIONS XFRM_INTERFACE" # Various device kernels -OPTIONS="$OPTIONS BLK_DEV_UBD HOSTFS" # Unset by default in 4.14 OPTIONS="$OPTIONS CGROUP_BPF" # Added in android-4.9 OPTIONS="$OPTIONS NF_SOCKET_IPV4 NF_SOCKET_IPV6" # Added in 4.9 OPTIONS="$OPTIONS INET_SCTP_DIAG" # Added in 4.7 @@ -42,6 +41,12 @@ OPTIONS="$OPTIONS IPV6_VTI" # Added in 3.13 OPTIONS="$OPTIONS IPV6_PRIVACY" # Removed in 3.12 OPTIONS="$OPTIONS NETFILTER_TPROXY" # Removed in 3.11 +# UML specific options +OPTIONS="$OPTIONS BLK_DEV_UBD HOSTFS" + +# QEMU specific options +OPTIONS="$OPTIONS VIRTIO VIRTIO_PCI VIRTIO_BLK NET_9P NET_9P_VIRTIO 9P_FS" + # Obsolete options present at some time in Android kernels OPTIONS="$OPTIONS IP_NF_TARGET_REJECT_SKERR IP6_NF_TARGET_REJECT_SKERR" @@ -69,6 +74,7 @@ COMPRESSED_ROOTFS=$ROOTFS.xz URL=https://dl.google.com/dl/android/$COMPRESSED_ROOTFS # Parse arguments and figure out which test to run. +ARCH=${ARCH:-um} J=${J:-64} MAKE="make" OUT_DIR=$(readlink -f ${OUT_DIR:-.}) @@ -80,8 +86,10 @@ SCRIPT_DIR=$(dirname $(readlink -f $0)) CONFIG_SCRIPT=${KERNEL_DIR}/scripts/config CONFIG_FILE=${OUT_DIR}/.config consolemode= +netconfig= testmode= -blockdevice=ubda +cmdline= +nowrite=0 nobuild=0 norun=0 @@ -91,7 +99,7 @@ while [ -n "$1" ]; do testmode=builder shift elif [ "$1" == "--readonly" ]; then - blockdevice="${blockdevice}r" + nowrite=1 shift elif [ "$1" == "--nobuild" ]; then nobuild=1 @@ -160,12 +168,16 @@ cd - if (( $NUMTAPINTERFACES > 0 )); then user=${USER:0:10} tapinterfaces= - netconfig= for id in $(seq 0 $(( NUMTAPINTERFACES - 1 )) ); do tap=${user}TAP$id tapinterfaces="$tapinterfaces $tap" mac=$(printf fe:fd:00:00:00:%02x $id) - netconfig="$netconfig eth$id=tuntap,$tap,$mac" + if [ "$ARCH" == "um" ]; then + netconfig="$netconfig eth$id=tuntap,$tap,$mac" + else + netconfig="$netconfig -netdev tap,id=hostnet$id,ifname=$tap,script=no,downscript=no" + netconfig="$netconfig -device virtio-net-pci,netdev=hostnet$id,id=net$id,mac=$mac" + fi done for tap in $tapinterfaces; do @@ -179,50 +191,108 @@ fi if [ -n "$KERNEL_BINARY" ]; then nobuild=1 -else - KERNEL_BINARY=./linux fi if ((nobuild == 0)); then - # Exporting ARCH=um SUBARCH=x86_64 doesn't seem to work, as it "sometimes" - # (?) results in a 32-bit kernel. + make_flags= + if [ "$ARCH" == "um" ]; then + # Exporting ARCH=um SUBARCH=x86_64 doesn't seem to work, as it + # "sometimes" (?) results in a 32-bit kernel. + make_flags="$make_flags ARCH=$ARCH SUBARCH=x86_64 CROSS_COMPILE= " + fi + if [ -n "$CC" ]; then + # The CC flag is *not* inherited from the environment, so it must be + # passed in on the command line. + make_flags="$make_flags CC=$CC" + fi # If there's no kernel config at all, create one or UML won't work. - [ -f $CONFIG_FILE ] || (cd $KERNEL_DIR && $MAKE defconfig ARCH=um SUBARCH=x86_64) + [ -n "$DEFCONFIG" ] || DEFCONFIG=defconfig + [ -f $CONFIG_FILE ] || (cd $KERNEL_DIR && $MAKE $make_flags $DEFCONFIG) # Enable the kernel config options listed in $OPTIONS. - cmdline=${OPTIONS// / -e } - $CONFIG_SCRIPT --file $CONFIG_FILE $cmdline + $CONFIG_SCRIPT --file $CONFIG_FILE ${OPTIONS// / -e } # Disable the kernel config options listed in $DISABLE_OPTIONS. - cmdline=${DISABLE_OPTIONS// / -d } - $CONFIG_SCRIPT --file $CONFIG_FILE $cmdline - - # olddefconfig doesn't work on old kernels. - if ! $MAKE olddefconfig ARCH=um SUBARCH=x86_64 CROSS_COMPILE= ; then - cat >&2 << EOF + $CONFIG_SCRIPT --file $CONFIG_FILE ${DISABLE_OPTIONS// / -d } -Warning: "make olddefconfig" failed. -Perhaps this kernel is too old to support it. -You may get asked lots of questions. -Keep enter pressed to accept the defaults. - -EOF - fi + $MAKE $make_flags olddefconfig # Compile the kernel. - $MAKE -j$J linux ARCH=um SUBARCH=x86_64 CROSS_COMPILE= + if [ "$ARCH" == "um" ]; then + $MAKE -j$J $make_flags linux + KERNEL_BINARY=./linux + else + $MAKE -j$J $make_flags + # Assume x86_64 bzImage for now + KERNEL_BINARY=./arch/x86/boot/bzImage + fi fi if (( norun == 1 )); then exit 0 fi -# Get the absolute path to the test file that's being run. -dir=/host$SCRIPT_DIR +if (( nowrite == 1 )); then + cmdline="ro" +fi +cmdline="$cmdline init=/sbin/net_test.sh" +cmdline="$cmdline net_test_args=\"$test_args\" net_test_mode=$testmode" + +if [ "$ARCH" == "um" ]; then + # Get the absolute path to the test file that's being run. + cmdline="$cmdline net_test=/host$SCRIPT_DIR/$test" -# Start the VM. -exec $KERNEL_BINARY umid=net_test $blockdevice=$SCRIPT_DIR/$ROOTFS \ - mem=512M init=/sbin/net_test.sh net_test=$dir/$test \ - net_test_args=\"$test_args\" \ - net_test_mode=$testmode $netconfig $consolemode >&2 + # Use UML's /proc/exitcode feature to communicate errors on test failure + cmdline="$cmdline net_test_exitcode=/proc/exitcode" + + # Map the --readonly flag to UML block device names + if ((nowrite == 0)); then + blockdevice=ubda + else + blockdevice="${blockdevice}r" + fi + + exec $KERNEL_BINARY >&2 umid=net_test mem=512M \ + $blockdevice=$SCRIPT_DIR/$ROOTFS $netconfig $consolemode $cmdline +else + # We boot into the filesystem image directly in all cases + cmdline="$cmdline root=/dev/vda" + + # The path is stripped by the 9p export; we don't need SCRIPT_DIR + cmdline="$cmdline net_test=/host/$test" + + # QEMU has no way to modify its exitcode; simulate it with a serial port. + # + # Choose to do it this way over writing a file to /host, because QEMU will + # initialize the 'exitcode' file for us, it avoids unnecessary writes to the + # host filesystem (which is normally not written to) and it allows us to + # communicate an exit code back in cases we do not have /host mounted. + # + # The assignment of 'ttyS1' here is magical -- we know 'ttyS0' will be our + # serial port from the hard-coded '-serial stdio' flag below, and so this + # second serial port will be 'ttyS1'. + cmdline="$cmdline net_test_exitcode=/dev/ttyS1" + + # Map the --readonly flag to a QEMU block device flag + blockdevice= + if ((nowrite > 0)); then + blockdevice=",readonly" + fi + blockdevice="-drive file=$SCRIPT_DIR/$ROOTFS,format=raw,if=none,id=drive-virtio-disk0$blockdevice" + blockdevice="$blockdevice -device virtio-blk-pci,drive=drive-virtio-disk0" + + # Assume x86_64 PC emulation for now + qemu-system-x86_64 >&2 -name net_test -m 512 \ + -kernel $KERNEL_BINARY \ + -no-user-config -nodefaults -no-reboot -display none \ + -machine pc,accel=kvm -cpu host -smp 4,sockets=4,cores=1,threads=1 \ + -chardev file,id=exitcode,path=exitcode \ + -device isa-serial,chardev=exitcode \ + -fsdev local,security_model=mapped-xattr,id=fsdev0,fmode=0644,dmode=0755,path=$SCRIPT_DIR \ + -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=host \ + $blockdevice $netconfig -serial stdio -append "$cmdline" + [ -s exitcode ] && exitcode=`cat exitcode | tr -d '\r'` || exitcode=1 + rm -f exitcode + exit $exitcode +fi -- cgit v1.2.3 From 805d82cce067183af3dec98b4b04f0824cd1562f Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Tue, 15 May 2018 11:18:28 -0700 Subject: Implement a workaround for clang + PARAVIRT failure. Make sure that CC is passed to kbuild when we build the kernel. We must disable CONFIG_PARAVIRT due to an upstream bug. Apply the workaround unconditionally, as PARAVIRT does nothing on UML. Bug: 79694505 Test: CC=clang build/build_test.sh Change-Id: Iaa4083bbb6505a04d4140ed8ef0c84ad0358e061 Signed-off-by: Alistair Strachan --- net/test/run_net_test.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index f81fb89..d448705 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -204,6 +204,14 @@ if ((nobuild == 0)); then # The CC flag is *not* inherited from the environment, so it must be # passed in on the command line. make_flags="$make_flags CC=$CC" + # TODO: Remove this workaround for https://lkml.org/lkml/2018/5/7/534 + # Needs a change to clang to be merged, an updated toolchain, and + # a new __nostackprotector annotation of the affected PARAVIRT + # code in the affected kernel branches (android-4.4, android-4.9, + # android-4.14). This sidesteps the issue by disabling PARAVIRT. + if [ "$CC" == "clang" ]; then + DISABLE_OPTIONS="$DISABLE_OPTIONS PARAVIRT" + fi fi # If there's no kernel config at all, create one or UML won't work. -- cgit v1.2.3 From 9493cb18c3fd19e1bfe0a6ab4a185b29be9f7cb2 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Sun, 17 Jun 2018 14:10:52 -0700 Subject: Enable FHANDLE to support systemd Newer Debian versions require CONFIG_FHANDLE for various bits of the systemd boot sequence to work without problems. Bug: 79694505 Test: Booted Debian 9 ("stretch") successfully Change-Id: I292ec363baeb88d6f4334eb773f1639af675a11b Signed-off-by: Alistair Strachan --- net/test/run_net_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index d448705..84536f0 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -2,7 +2,7 @@ # Common kernel options OPTIONS=" DEBUG_SPINLOCK DEBUG_ATOMIC_SLEEP DEBUG_MUTEXES DEBUG_RT_MUTEXES" -OPTIONS="$OPTIONS DEVTMPFS DEVTMPFS_MOUNT" +OPTIONS="$OPTIONS DEVTMPFS DEVTMPFS_MOUNT FHANDLE" OPTIONS="$OPTIONS IPV6 IPV6_ROUTER_PREF IPV6_MULTIPLE_TABLES IPV6_ROUTE_INFO" OPTIONS="$OPTIONS TUN SYN_COOKIES IP_ADVANCED_ROUTER IP_MULTIPLE_TABLES" OPTIONS="$OPTIONS NETFILTER NETFILTER_ADVANCED NETFILTER_XTABLES" -- cgit v1.2.3 From d6d6b2b53129f218567c16d586b29259f27f72c5 Mon Sep 17 00:00:00 2001 From: Hsin-Yi Chen Date: Mon, 25 Jun 2018 18:42:21 +0800 Subject: Fix net tests for 32-bit kernel - Add bpf syscall number for arm7l. - Ignore non-numeric suffix of kernel version. Bug: 110440652 Test: run vts -m VtsKernelNetTest Change-Id: If217c961fdd45b013a7d83967bcab8c746804b97 --- net/test/bpf.py | 1 + net/test/csocket.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/net/test/bpf.py b/net/test/bpf.py index 7625265..aa50f3e 100755 --- a/net/test/bpf.py +++ b/net/test/bpf.py @@ -32,6 +32,7 @@ import platform # TODO: is there a better way of doing this? __NR_bpf = { "aarch64-64bit": 280, + "armv7l-32bit": 386, "armv8l-32bit": 386, "armv8l-64bit": 280, "i686-32bit": 357, diff --git a/net/test/csocket.py b/net/test/csocket.py index bdd501c..ccabf4a 100644 --- a/net/test/csocket.py +++ b/net/test/csocket.py @@ -17,6 +17,7 @@ import ctypes import ctypes.util import os +import re import socket import struct @@ -78,9 +79,9 @@ libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True) # TODO: Unlike most of this file, these functions aren't specific to wrapping C # library calls. Move them to a utils.py or constants.py file, once we have one. def LinuxVersion(): - # Example: "3.4.67-00753-gb7a556f". - # Get the part before the dash. - version = os.uname()[2].split("-")[0] + # Example: "3.4.67-00753-gb7a556f", "4.4.135+". + # Get the prefix consisting of digits and dots. + version = re.search("^[0-9.]*", os.uname()[2]).group() # Convert it into a tuple such as (3, 4, 67). That allows comparing versions # using < and >, since tuples are compared lexicographically. version = tuple(int(i) for i in version.split(".")) -- cgit v1.2.3 From e57d074dc520ab1feabe5b202e6ef1c3f9f9844b Mon Sep 17 00:00:00 2001 From: Hsin-Yi Chen Date: Mon, 25 Jun 2018 18:33:21 +0800 Subject: Fix net tests for 32-bit kernel - Add bpf syscall number for arm7l. - Ignore non-numeric suffix of kernel version. Bug: 110440652 Test: run vts -m VtsKernelNetTest Change-Id: Idd7f278b7f801296957c2a5b2fd092e8ba34da69 Merged-In: If217c961fdd45b013a7d83967bcab8c746804b97 --- net/test/bpf.py | 1 + net/test/csocket.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/net/test/bpf.py b/net/test/bpf.py index c9ad264..5e90daa 100755 --- a/net/test/bpf.py +++ b/net/test/bpf.py @@ -26,6 +26,7 @@ import socket # TODO: is there a better way of doing this? __NR_bpf = { "aarch64": 280, + "armv7l": 386, "armv8l": 386, "x86_64": 321}[os.uname()[4]] diff --git a/net/test/csocket.py b/net/test/csocket.py index bdd501c..ccabf4a 100644 --- a/net/test/csocket.py +++ b/net/test/csocket.py @@ -17,6 +17,7 @@ import ctypes import ctypes.util import os +import re import socket import struct @@ -78,9 +79,9 @@ libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True) # TODO: Unlike most of this file, these functions aren't specific to wrapping C # library calls. Move them to a utils.py or constants.py file, once we have one. def LinuxVersion(): - # Example: "3.4.67-00753-gb7a556f". - # Get the part before the dash. - version = os.uname()[2].split("-")[0] + # Example: "3.4.67-00753-gb7a556f", "4.4.135+". + # Get the prefix consisting of digits and dots. + version = re.search("^[0-9.]*", os.uname()[2]).group() # Convert it into a tuple such as (3, 4, 67). That allows comparing versions # using < and >, since tuples are compared lexicographically. version = tuple(int(i) for i in version.split(".")) -- cgit v1.2.3 From 4e398426edba77b1e4cbbf97df61efd5b0710ef6 Mon Sep 17 00:00:00 2001 From: Yongqin Liu Date: Wed, 30 May 2018 20:54:48 +0800 Subject: anycast_test.py: change to use thread.join to wait CloseFileDescriptorThread finished The original implementation closes fd in a separate thread, and the use time.sleep(0.1) to wait it finished, but there is the case that on platforms like hikey the thread is not finished even after the slept 0.1s, to make this test works for more platforms, we change to use thread.join(0.5) here to wait up to 0.5 seconds. And use thread.isActive to check if there is the hang problem. Test: run vts-kernel -m VtsKernelNetTest with 4.9 and 4.14 kernel There will be 1 or more failed in 5 iterations, after this change all will pass in 10 iterations. Change-Id: I24341667c9a889d79d38a9eb1d88d05325d7ffff Signed-off-by: Yongqin Liu --- net/test/anycast_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/test/anycast_test.py b/net/test/anycast_test.py index 82130db..62d874e 100755 --- a/net/test/anycast_test.py +++ b/net/test/anycast_test.py @@ -93,7 +93,9 @@ class AnycastTest(multinetwork_base.MultiNetworkBaseTest): # This will hang if the kernel has the bug. thread = CloseFileDescriptorThread(self.tuns[netid]) thread.start() - time.sleep(0.1) + # Wait up to 0.5 seconds for the thread to finish, but + # continue and fail the test if the thread hangs. + thread.join(0.5) # Make teardown work. del self.tuns[netid] -- cgit v1.2.3 From 2d99985d80d90d626e147ff8e5b087db9cbcc450 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Wed, 20 Jun 2018 11:08:17 -0700 Subject: Add scripts for building the net tests rootfs. These scripts enable the bootstrapping of the Debian images used by the net tests. The default image type, and only recommended one at this time, is based on Debian Stretch (9.4). The stretch image now contains a copy of the AOSP version of iptables (which has full bpf support). Although the knowledge of exactly how the original Debian Wheezy (7.0) image was built has been lost, this change also includes a script which builds a compatible image. Both images support running with their native init system with UML and QEMU, or booting directly into the net_test.sh script. Bug: 74373492 Bug: 79694505 Change-Id: I497fac43b9cef6551efee1cc2134075d16ee2481 Signed-off-by: Alistair Strachan --- net/test/build_rootfs.sh | 119 ++++++++++++++++++++++++++++++++++ net/test/rootfs/common.sh | 57 ++++++++++++++++ net/test/rootfs/net_test.sh | 34 ++++++++++ net/test/rootfs/stretch.list | 31 +++++++++ net/test/rootfs/stretch.sh | 150 +++++++++++++++++++++++++++++++++++++++++++ net/test/rootfs/wheezy.list | 33 ++++++++++ net/test/rootfs/wheezy.sh | 50 +++++++++++++++ 7 files changed, 474 insertions(+) create mode 100755 net/test/build_rootfs.sh create mode 100644 net/test/rootfs/common.sh create mode 100755 net/test/rootfs/net_test.sh create mode 100644 net/test/rootfs/stretch.list create mode 100755 net/test/rootfs/stretch.sh create mode 100644 net/test/rootfs/wheezy.list create mode 100755 net/test/rootfs/wheezy.sh diff --git a/net/test/build_rootfs.sh b/net/test/build_rootfs.sh new file mode 100755 index 0000000..e0788c1 --- /dev/null +++ b/net/test/build_rootfs.sh @@ -0,0 +1,119 @@ +#!/bin/bash +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P) + +usage() { + echo "usage: $0 [-h] [-s wheezy|stretch] [-n net_test.rootfs.`date +%Y%m%d`]" + exit 1 +} + +name=net_test.rootfs.`date +%Y%m%d` +suite=stretch + +while getopts ":hs:n:" opt; do + case $opt in + h) + usage + ;; + s) + if [ "$OPTARG" != "wheezy" -a "$OPTARG" != "stretch" ]; then + echo "Invalid suite: $OPTARG" >&2 + usage + fi + suite=$OPTARG + ;; + n) + name=$OPTARG + ;; + \?) + echo "Invalid option: $OPTARG" >&2 + usage + ;; + :) + echo "Invalid option: $OPTARG requires an argument" >&2 + usage + ;; + esac +done + +# Sometimes it isn't obvious when the script fails +failure() { + echo "Filesystem generation process failed." >&2 +} +trap failure ERR + +# Import the package list for this release +packages=`cat $SCRIPT_DIR/rootfs/$suite.list | xargs | tr -s ' ' ','` + +# For the debootstrap intermediates +workdir=`mktemp -d` +workdir_remove() { + echo "Removing temporary files.." >&2 + sudo rm -rf $workdir +} +trap workdir_remove EXIT + +# Run the debootstrap first +cd $workdir +sudo debootstrap --arch=amd64 --variant=minbase --include=$packages \ + $suite . http://ftp.debian.org/debian +# Workarounds for bugs in the debootstrap suite scripts +for mount in `cat /proc/mounts | cut -d' ' -f2 | grep -e ^$workdir`; do + echo "Unmounting mountpoint $mount.." >&2 + sudo umount $mount +done +# Copy the chroot preparation scripts, and enter the chroot +for file in $suite.sh common.sh net_test.sh; do + sudo cp -a $SCRIPT_DIR/rootfs/$file root/$file + sudo chown root:root root/$file +done +sudo chroot . /root/$suite.sh + +# Leave the workdir, to build the filesystem +cd - + +# For the final image mount +mount=`mktemp -d` +mount_remove() { + rmdir $mount + workdir_remove +} +trap mount_remove EXIT + +# Create a 1G empty ext3 filesystem +truncate -s 1G $name +mke2fs -F -t ext3 -L ROOT $name + +# Mount the new filesystem locally +sudo mount -o loop -t ext3 $name $mount +image_unmount() { + sudo umount $mount + mount_remove +} +trap image_unmount EXIT + +# Copy the patched debootstrap results into the new filesystem +sudo cp -a $workdir/* $mount + +# Fill the rest of the space with zeroes, to optimize compression +sudo dd if=/dev/zero of=$mount/sparse bs=1M 2>/dev/null || true +sudo rm -f $mount/sparse + +echo "Debian $suite filesystem generated at '$name'." diff --git a/net/test/rootfs/common.sh b/net/test/rootfs/common.sh new file mode 100644 index 0000000..172d9b6 --- /dev/null +++ b/net/test/rootfs/common.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +chroot_sanity_check() { + if [ ! -f /var/log/bootstrap.log ]; then + echo "Do not run this script directly!" + echo "This is supposed to be run from inside a debootstrap chroot!" + echo "Aborting." + exit 1 + fi +} + +chroot_cleanup() { + # Read-only root breaks booting via init + cat >/etc/fstab << EOF +tmpfs /tmp tmpfs defaults 0 0 +tmpfs /var/log tmpfs defaults 0 0 +tmpfs /var/tmp tmpfs defaults 0 0 +EOF + + # systemd will attempt to re-create this symlink if it does not exist, + # which fails if it is booting from a read-only root filesystem (which + # is normally the case). The syslink must be relative, not absolute, + # and it must point to /proc/self/mounts, not /proc/mounts. + ln -sf ../proc/self/mounts /etc/mtab + + # Remove contaminants coming from the debootstrap process + echo vm >/etc/hostname + echo "nameserver 127.0.0.1" >/etc/resolv.conf + + # Put the helper net_test.sh script into place + mv /root/net_test.sh /sbin/net_test.sh + + # Make sure the /host mountpoint exists for net_test.sh + mkdir /host + + # Disable the root password + passwd -d root + + # Clean up any junk created by the imaging process + rm -rf /var/lib/apt/lists/* /var/log/bootstrap.log /root/* /tmp/* + find /var/log -type f -exec rm -f '{}' ';' +} diff --git a/net/test/rootfs/net_test.sh b/net/test/rootfs/net_test.sh new file mode 100755 index 0000000..072a8a3 --- /dev/null +++ b/net/test/rootfs/net_test.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +mount -t proc none /proc +mount -t sys none /sys +mount -t tmpfs tmpfs /tmp +mount -t tmpfs tmpfs /run + +# If this system was booted under UML, it will always have a /proc/exitcode +# file. If it was booted natively or under QEMU, it will not have this file. +if [ -e /proc/exitcode ]; then + mount -t hostfs hostfs /host +else + mount -t 9p -o trans=virtio,version=9p2000.L host /host +fi + +test=$(cat /proc/cmdline | sed -re 's/.*net_test=([^ ]*).*/\1/g') +cd $(dirname $test) +./net_test.sh +poweroff -f diff --git a/net/test/rootfs/stretch.list b/net/test/rootfs/stretch.list new file mode 100644 index 0000000..e6c0177 --- /dev/null +++ b/net/test/rootfs/stretch.list @@ -0,0 +1,31 @@ +apt +apt-utils +bash-completion +bsdmainutils +ca-certificates +file +gpgv +ifupdown +insserv +iputils-ping +less +libnetfilter-conntrack3 +libnfnetlink0 +mime-support +netbase +netcat-openbsd +netcat-traditional +net-tools +openssl +procps +psmisc +python +python-scapy +strace +systemd-sysv +tcpdump +traceroute +udev +udhcpc +vim-tiny +wget diff --git a/net/test/rootfs/stretch.sh b/net/test/rootfs/stretch.sh new file mode 100755 index 0000000..6d8a9a4 --- /dev/null +++ b/net/test/rootfs/stretch.sh @@ -0,0 +1,150 @@ +#!/bin/bash +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P) + +. $SCRIPT_DIR/common.sh + +chroot_sanity_check + +cd /root + +# Add the needed debian sources +cat >/etc/apt/sources.list </etc/apt/apt.conf.d/80default </etc/apt/apt.conf.d/90recommends </etc/apt/preferences.d/90buster <originally-installed + +# Install everything needed from stretch to build iptables +apt-get install -y \ + build-essential \ + autoconf \ + automake \ + bison \ + debhelper \ + devscripts \ + fakeroot \ + flex \ + libmnl-dev \ + libnetfilter-conntrack-dev \ + libnfnetlink-dev \ + libnftnl-dev \ + libtool + +# Install newer linux-libc headers (these are from 4.16) +apt-get install -y -t stretch-backports linux-libc-dev + +# We are done with apt; reclaim the disk space +apt-get clean + +# Construct the iptables source package to build +iptables=iptables-1.6.1 +mkdir -p /usr/src/$iptables + +cd /usr/src/$iptables +# Download a specific revision of iptables from AOSP +aosp_iptables=android-wear-p-preview-2 +wget -qO - \ + https://android.googlesource.com/platform/external/iptables/+archive/$aosp_iptables.tar.gz | \ + tar -zxf - +# Download a compatible 'debian' overlay from Debian salsa +# We don't want all of the sources, just the Debian modifications +debian_iptables=1.6.1-2_bpo9+1 +debian_iptables_dir=pkg-iptables-debian-$debian_iptables +wget -qO - \ + https://salsa.debian.org/pkg-netfilter-team/pkg-iptables/-/archive/debian/$debian_iptables/$debian_iptables_dir.tar.gz | \ + tar --strip-components 1 -zxf - \ + $debian_iptables_dir/debian +cd - + +cd /usr/src +# Generate a source package to leave in the filesystem. This is done for license +# compliance and build reproducibility. +tar --exclude=debian -cf - $iptables | \ + xz -9 >`echo $iptables | tr -s '-' '_'`.orig.tar.xz +cd - + +cd /usr/src/$iptables +# Build debian packages from the integrated iptables source +dpkg-buildpackage -F -us -uc +cd - + +# Record the list of packages we have installed now +LANG=C dpkg --get-selections | sort >installed + +# Compute the difference, and remove anything installed between the snapshots +dpkg -P `comm -3 originally-installed installed | sed -e 's,install,,' -e 's,\t,,' | xargs` + +cd /usr/src +# Find any packages generated, resolve to the debian package name, then +# exclude any compat, header or symbol packages +packages=`find -maxdepth 1 -name '*.deb' | colrm 1 2 | cut -d'_' -f1 | + grep -ve '-compat$\|-dbg$\|-dbgsym$\|-dev$' | xargs` +# Install the patched iptables packages, and 'hold' then so +# "apt-get dist-upgrade" doesn't replace them +dpkg -i ` +for package in $packages; do + echo ${package}_*.deb +done | xargs` +for package in $packages; do + echo "$package hold" | dpkg --set-selections +done +# Tidy up the mess we left behind, leaving just the source tarballs +rm -rf $iptables *.buildinfo *.changes *.deb *.dsc +cd - + +# Ensure a getty is spawned on ttyS0, if booting the image manually +ln -s /lib/systemd/system/serial-getty\@.service \ + /etc/systemd/system/getty.target.wants/serial-getty\@ttyS0.service + +# systemd needs some directories to be created +mkdir -p /var/lib/systemd/coredump /var/lib/systemd/rfkill + +# Finalize and tidy up the created image +chroot_cleanup diff --git a/net/test/rootfs/wheezy.list b/net/test/rootfs/wheezy.list new file mode 100644 index 0000000..44e3d85 --- /dev/null +++ b/net/test/rootfs/wheezy.list @@ -0,0 +1,33 @@ +adduser +apt +apt-utils +bash-completion +binutils +bsdmainutils +ca-certificates +file +gpgv +ifupdown +insserv +iptables +iputils-ping +less +libpopt0 +mime-support +netbase +netcat6 +netcat-traditional +net-tools +module-init-tools +openssl +procps +psmisc +python2.7 +python-scapy +strace +tcpdump +traceroute +udev +udhcpc +vim-tiny +wget diff --git a/net/test/rootfs/wheezy.sh b/net/test/rootfs/wheezy.sh new file mode 100755 index 0000000..81cfad7 --- /dev/null +++ b/net/test/rootfs/wheezy.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# NOTE: It is highly recommended that you do not create new wheezy rootfs +# images. This script is here for forensic purposes only, to understand +# how the original rootfs was created. + +set -e + +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P) + +. $SCRIPT_DIR/common.sh + +chroot_sanity_check + +# Remove things pulled in by debootstrap that we do not need +dpkg -P \ + debconf-i18n \ + liblocale-gettext-perl \ + libtext-charwidth-perl \ + libtext-iconv-perl \ + libtext-wrapi18n-perl \ + python2.6 \ + python2.6-minimal \ + xz-utils + +# We are done with apt; reclaim the disk space +apt-get clean + +# Ensure a getty is spawned on ttyS0, if booting the image manually +# This also removes the vt gettys, as we may have no vt +sed -i '/tty[123456]/d' /etc/inittab +echo "s0:1235:respawn:/sbin/getty 115200 ttyS0 linux" >>/etc/inittab + +# Finalize and tidy up the created image +chroot_cleanup -- cgit v1.2.3 From 7878ca7eb5513051031dfefc76e5b2ca069a7faa Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Fri, 13 Jul 2018 15:56:36 -0700 Subject: Fix nobuild runs of run_net_test.sh without KERNEL_BINARY env var set This commit sets the default location of KERNEL_BINARY based on the architechture, and ensures that it is set for --nobuild runs as well. Test: Ran kernel unit tests Change-Id: I0db4f3aaab811cc0aec0406f24e4a2506cbe694a --- net/test/run_net_test.sh | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index 84536f0..4ae7705 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -191,6 +191,14 @@ fi if [ -n "$KERNEL_BINARY" ]; then nobuild=1 +else + # Set default KERNEL_BINARY location if it was not provided. + if [ "$ARCH" == "um" ]; then + KERNEL_BINARY=./linux + else + # Assume x86_64 bzImage for now + KERNEL_BINARY=./arch/x86/boot/bzImage + fi fi if ((nobuild == 0)); then @@ -229,11 +237,8 @@ if ((nobuild == 0)); then # Compile the kernel. if [ "$ARCH" == "um" ]; then $MAKE -j$J $make_flags linux - KERNEL_BINARY=./linux else $MAKE -j$J $make_flags - # Assume x86_64 bzImage for now - KERNEL_BINARY=./arch/x86/boot/bzImage fi fi -- cgit v1.2.3 From ad7a31a77695b60bdcd223df568a2b921acc41b0 Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Tue, 3 Jul 2018 15:27:26 -0700 Subject: Refactor parameterization logic in net tests This change refactors the logic to create parameterized tests in the kernel unit tests. Logic for name generation is left to classes, while common code for test injection is moved to the utility class Bug: 66467511 Test: Ran net tests Change-Id: I7eba57c616145246637beefac3aca16f9e2e899e --- net/test/parameterization_test.py | 83 +++++++++++++++++++++++++++++ net/test/util.py | 57 +++++++++++++++++++- net/test/xfrm_algorithm_test.py | 109 ++++++++++++-------------------------- 3 files changed, 173 insertions(+), 76 deletions(-) create mode 100755 net/test/parameterization_test.py diff --git a/net/test/parameterization_test.py b/net/test/parameterization_test.py new file mode 100755 index 0000000..8f9e130 --- /dev/null +++ b/net/test/parameterization_test.py @@ -0,0 +1,83 @@ +#!/usr/bin/python +# +# Copyright 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import itertools +import unittest + +import net_test +import util + + +def InjectTests(): + ParmeterizationTest.InjectTests() + + +# This test class ensures that the Parameterized Test generator in utils.py +# works properly. It injects test methods into itself, and ensures that they +# are generated as expected, and that the TestClosures being run are properly +# defined, and running different parameterized tests each time. +class ParmeterizationTest(net_test.NetworkTest): + tests_run_list = [] + + @staticmethod + def NameGenerator(a, b, c): + return str(a) + "_" + str(b) + "_" + str(c) + + @classmethod + def InjectTests(cls): + PARAMS_A = (1, 2) + PARAMS_B = (3, 4) + PARAMS_C = (5, 6) + + param_list = itertools.product(PARAMS_A, PARAMS_B, PARAMS_C) + util.InjectParameterizedTest(cls, param_list, cls.NameGenerator) + + def ParamTestDummyFunc(self, a, b, c): + self.tests_run_list.append( + "testDummyFunc_" + ParmeterizationTest.NameGenerator(a, b, c)) + + def testParameterization(self): + expected = [ + "testDummyFunc_1_3_5", + "testDummyFunc_1_3_6", + "testDummyFunc_1_4_5", + "testDummyFunc_1_4_6", + "testDummyFunc_2_3_5", + "testDummyFunc_2_3_6", + "testDummyFunc_2_4_5", + "testDummyFunc_2_4_6", + ] + + actual = [name for name in dir(self) if name.startswith("testDummyFunc")] + + # Check that name and contents are equal + self.assertEqual(len(expected), len(actual)) + self.assertEqual(sorted(expected), sorted(actual)) + + # Start a clean list, and run all the tests. + self.tests_run_list = list() + for test_name in expected: + test_method = getattr(self, test_name) + test_method() + + # Make sure all tests have been run with the correct parameters + for test_name in expected: + self.assertTrue(test_name in self.tests_run_list) + + +if __name__ == "__main__": + ParmeterizationTest.InjectTests() + unittest.main() diff --git a/net/test/util.py b/net/test/util.py index bed3e1d..cbcd2d0 100644 --- a/net/test/util.py +++ b/net/test/util.py @@ -13,4 +13,59 @@ # limitations under the License. def GetPadLength(block_size, length): - return (block_size - (length % block_size)) % block_size \ No newline at end of file + return (block_size - (length % block_size)) % block_size + + +def InjectParameterizedTest(cls, param_list, name_generator): + """Injects parameterized tests into the provided class + + This method searches for all tests that start with the name "ParamTest", + and injects a test method for each set of parameters in param_list. Names + are generated via the use of the name_generator. + + Args: + cls: the class for which to inject all parameterized tests + param_list: a list of tuples, where each tuple is a combination of + of parameters to test (i.e. representing a single test case) + name_generator: A function that takes a combination of parameters and + returns a string that identifies the test case. + """ + param_test_names = [name for name in dir(cls) if name.startswith("ParamTest")] + + # Force param_list to an actual list; otherwise itertools.Product will hit + # the end, resulting in only the first ParamTest* method actually being + # parameterized + param_list = list(param_list) + + # Parameterize each test method starting with "ParamTest" + for test_name in param_test_names: + func = getattr(cls, test_name) + + for params in param_list: + # Give the test method a readable, debuggable name. + param_string = name_generator(*params) + new_name = "%s_%s" % (func.__name__.replace("ParamTest", "test"), + param_string) + new_name = new_name.replace("(", "-").replace(")", "") # remove parens + + # Inject the test method + setattr(cls, new_name, _GetTestClosure(func, params)) + + +def _GetTestClosure(func, params): + """ Creates a no-argument test method for the given function and parameters. + + This is required to be separate from the InjectParameterizedTest method, due + to some interesting scoping issues with internal function declarations. If + left in InjectParameterizedTest, all the tests end up using the same + instance of TestClosure + + Args: + func: the function for which this test closure should run + params: the parameters for the run of this test function + """ + + def TestClosure(self): + func(self, *params) + + return TestClosure diff --git a/net/test/xfrm_algorithm_test.py b/net/test/xfrm_algorithm_test.py index 6adc461..0176265 100755 --- a/net/test/xfrm_algorithm_test.py +++ b/net/test/xfrm_algorithm_test.py @@ -27,6 +27,7 @@ import unittest import multinetwork_base import net_test from tun_twister import TapTwister +import util import xfrm import xfrm_base @@ -72,49 +73,26 @@ AEAD_ALGOS = [ ] def InjectTests(): - XfrmAlgorithmTest.InjectTests() + XfrmAlgorithmTest.InjectTests() + class XfrmAlgorithmTest(xfrm_base.XfrmLazyTest): @classmethod def InjectTests(cls): - """Inject parameterized test cases into this class. - - Because a library for parameterized testing is not availble in - net_test.rootfs.20150203, this does a minimal parameterization. - - This finds methods named like "ParamTestFoo" and replaces them with several - "testFoo(*)" methods taking different parameter dicts. A set of test - parameters is generated from every combination of encryption, - authentication, IP version, and TCP/UDP. - - The benefit of this approach is that an individually failing tests have a - clearly separated stack trace, and one failed test doesn't prevent the rest - from running. - """ - param_test_names = [ - name for name in dir(cls) if name.startswith("ParamTest") - ] VERSIONS = (4, 6) TYPES = (SOCK_DGRAM, SOCK_STREAM) # Tests all combinations of auth & crypt. Mutually exclusive with aead. - for crypt, auth, version, proto, name in itertools.product( - CRYPT_ALGOS, AUTH_ALGOS, VERSIONS, TYPES, param_test_names): - XfrmAlgorithmTest.InjectSingleTest(name, version, proto, crypt=crypt, auth=auth) + param_list = itertools.product(VERSIONS, TYPES, AUTH_ALGOS, CRYPT_ALGOS, + [None]) + util.InjectParameterizedTest(cls, param_list, cls.TestNameGenerator) # Tests all combinations of aead. Mutually exclusive with auth/crypt. - for aead, version, proto, name in itertools.product( - AEAD_ALGOS, VERSIONS, TYPES, param_test_names): - XfrmAlgorithmTest.InjectSingleTest(name, version, proto, aead=aead) - - @classmethod - def InjectSingleTest(cls, name, version, proto, crypt=None, auth=None, aead=None): - func = getattr(cls, name) - - def TestClosure(self): - func(self, {"crypt": crypt, "auth": auth, "aead": aead, - "version": version, "proto": proto}) + param_list = itertools.product(VERSIONS, TYPES, [None], [None], AEAD_ALGOS) + util.InjectParameterizedTest(cls, param_list, cls.TestNameGenerator) + @staticmethod + def TestNameGenerator(version, proto, auth, crypt, aead): # Produce a unique and readable name for each test. e.g. # testSocketPolicySimple_cbc-aes_256_hmac-sha512_512_256_IPv6_UDP param_string = "" @@ -131,12 +109,9 @@ class XfrmAlgorithmTest(xfrm_base.XfrmLazyTest): param_string += "%s_%s" % ("IPv4" if version == 4 else "IPv6", "UDP" if proto == SOCK_DGRAM else "TCP") - new_name = "%s_%s" % (func.__name__.replace("ParamTest", "test"), - param_string) - new_name = new_name.replace("(", "-").replace(")", "") # remove parens - setattr(cls, new_name, TestClosure) + return param_string - def ParamTestSocketPolicySimple(self, params): + def ParamTestSocketPolicySimple(self, version, proto, auth, crypt, aead): """Test two-way traffic using transport mode and socket policies.""" def AssertEncrypted(packet): @@ -153,37 +128,21 @@ class XfrmAlgorithmTest(xfrm_base.XfrmLazyTest): # other using transport mode ESP. Because of TapTwister, both sockets # perceive each other as owning "remote_addr". netid = self.RandomNetid() - family = net_test.GetAddressFamily(params["version"]) - local_addr = self.MyAddress(params["version"], netid) - remote_addr = self.GetRemoteSocketAddress(params["version"]) - crypt_left = (xfrm.XfrmAlgo(( - params["crypt"].name, - params["crypt"].key_len)), - os.urandom(params["crypt"].key_len / 8)) if params["crypt"] else None - crypt_right = (xfrm.XfrmAlgo(( - params["crypt"].name, - params["crypt"].key_len)), - os.urandom(params["crypt"].key_len / 8)) if params["crypt"] else None - auth_left = (xfrm.XfrmAlgoAuth(( - params["auth"].name, - params["auth"].key_len, - params["auth"].trunc_len)), - os.urandom(params["auth"].key_len / 8)) if params["auth"] else None - auth_right = (xfrm.XfrmAlgoAuth(( - params["auth"].name, - params["auth"].key_len, - params["auth"].trunc_len)), - os.urandom(params["auth"].key_len / 8)) if params["auth"] else None - aead_left = (xfrm.XfrmAlgoAead(( - params["aead"].name, - params["aead"].key_len, - params["aead"].icv_len)), - os.urandom(params["aead"].key_len / 8)) if params["aead"] else None - aead_right = (xfrm.XfrmAlgoAead(( - params["aead"].name, - params["aead"].key_len, - params["aead"].icv_len)), - os.urandom(params["aead"].key_len / 8)) if params["aead"] else None + family = net_test.GetAddressFamily(version) + local_addr = self.MyAddress(version, netid) + remote_addr = self.GetRemoteSocketAddress(version) + auth_left = (xfrm.XfrmAlgoAuth((auth.name, auth.key_len, auth.trunc_len)), + os.urandom(auth.key_len / 8)) if auth else None + auth_right = (xfrm.XfrmAlgoAuth((auth.name, auth.key_len, auth.trunc_len)), + os.urandom(auth.key_len / 8)) if auth else None + crypt_left = (xfrm.XfrmAlgo((crypt.name, crypt.key_len)), + os.urandom(crypt.key_len / 8)) if crypt else None + crypt_right = (xfrm.XfrmAlgo((crypt.name, crypt.key_len)), + os.urandom(crypt.key_len / 8)) if crypt else None + aead_left = (xfrm.XfrmAlgoAead((aead.name, aead.key_len, aead.icv_len)), + os.urandom(aead.key_len / 8)) if aead else None + aead_right = (xfrm.XfrmAlgoAead((aead.name, aead.key_len, aead.icv_len)), + os.urandom(aead.key_len / 8)) if aead else None spi_left = 0xbeefface spi_right = 0xcafed00d req_ids = [100, 200, 300, 400] # Used to match templates and SAs. @@ -242,20 +201,20 @@ class XfrmAlgorithmTest(xfrm_base.XfrmLazyTest): output_mark=None) # Make two sockets. - sock_left = socket(family, params["proto"], 0) + sock_left = socket(family, proto, 0) sock_left.settimeout(2.0) sock_left.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) self.SelectInterface(sock_left, netid, "mark") - sock_right = socket(family, params["proto"], 0) + sock_right = socket(family, proto, 0) sock_right.settimeout(2.0) sock_right.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) self.SelectInterface(sock_right, netid, "mark") # For UDP, set SO_LINGER to 0, to prevent TCP sockets from hanging around # in a TIME_WAIT state. - if params["proto"] == SOCK_STREAM: - net_test.DisableFinWait(sock_left) - net_test.DisableFinWait(sock_right) + if proto == SOCK_STREAM: + net_test.DisableFinWait(sock_left) + net_test.DisableFinWait(sock_right) # Apply the left outbound socket policy. xfrm_base.ApplySocketPolicy(sock_left, family, xfrm.XFRM_POLICY_OUT, @@ -302,14 +261,14 @@ class XfrmAlgorithmTest(xfrm_base.XfrmLazyTest): sock.close() # Server and client need to know each other's port numbers in advance. - wildcard_addr = net_test.GetWildcardAddress(params["version"]) + wildcard_addr = net_test.GetWildcardAddress(version) sock_left.bind((wildcard_addr, 0)) sock_right.bind((wildcard_addr, 0)) left_port = sock_left.getsockname()[1] right_port = sock_right.getsockname()[1] # Start the appropriate server type on sock_right. - target = TcpServer if params["proto"] == SOCK_STREAM else UdpServer + target = TcpServer if proto == SOCK_STREAM else UdpServer server = threading.Thread( target=target, args=(sock_right, left_port), -- cgit v1.2.3 From 30639494111a16eb5d8832b6d753f35b44754441 Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Wed, 10 Jan 2018 13:30:10 -0800 Subject: Add tunnel input tests to net_tests This commit adds tunnel (non-VTI) input tests to xfrm_tunnel_test using null encryption. Bug: 66467511 Test: this; passes on all kernels Change-Id: I829e6b12e126bf40915544cdfa94271b0ad1b1b5 --- net/test/xfrm_tunnel_test.py | 115 +++++++++++++++++++++++++++++++------------ 1 file changed, 84 insertions(+), 31 deletions(-) diff --git a/net/test/xfrm_tunnel_test.py b/net/test/xfrm_tunnel_test.py index 0c355b8..e71b46b 100755 --- a/net/test/xfrm_tunnel_test.py +++ b/net/test/xfrm_tunnel_test.py @@ -19,15 +19,18 @@ from errno import * # pylint: disable=wildcard-import from socket import * # pylint: disable=wildcard-import import random +import itertools import struct import unittest +from scapy import all as scapy from tun_twister import TunTwister import csocket import iproute import multinetwork_base import net_test import packets +import util import xfrm import xfrm_base @@ -80,50 +83,99 @@ def _GetRemoteOuterAddress(version): return {4: net_test.IPV4_ADDR, 6: net_test.IPV6_ADDR}[version] +def InjectTests(): + InjectParameterizedTests(XfrmTunnelTest) + + +def InjectParameterizedTests(cls): + VERSIONS = (4, 6) + param_list = itertools.product(VERSIONS, VERSIONS) + + def NameGenerator(*args): + return "IPv%d_in_IPv%d" % tuple(args) + + util.InjectParameterizedTest(cls, param_list, NameGenerator) + + class XfrmTunnelTest(xfrm_base.XfrmLazyTest): - def _CheckTunnelOutput(self, inner_version, outer_version): - """Test a bi-directional XFRM Tunnel with explicit selectors""" + def _CheckTunnelOutput(self, inner_version, outer_version, underlying_netid, + netid, local_inner, remote_inner, local_outer, + remote_outer, write_sock): + + write_sock.sendto(net_test.UDP_PAYLOAD, (remote_inner, 53)) + self._ExpectEspPacketOn(underlying_netid, _TEST_OUT_SPI, 1, None, + local_outer, remote_outer) + + def _CheckTunnelInput(self, inner_version, outer_version, underlying_netid, + netid, local_inner, remote_inner, local_outer, + remote_outer, read_sock): + + # The second parameter of the tuple is the port number regardless of AF. + local_port = read_sock.getsockname()[1] + + # Build and receive an ESP packet destined for the inner socket + IpType = {4: scapy.IP, 6: scapy.IPv6}[inner_version] + input_pkt = ( + IpType(src=remote_inner, dst=local_inner) / scapy.UDP( + sport=1234, dport=local_port) / net_test.UDP_PAYLOAD) + input_pkt = IpType(str(input_pkt)) # Compute length, checksum. + input_pkt = xfrm_base.EncryptPacketWithNull(input_pkt, _TEST_IN_SPI, 1, + (remote_outer, local_outer)) + self.ReceivePacketOn(underlying_netid, input_pkt) + + # Verify that the packet data and src are correct + data, src = read_sock.recvfrom(4096) + self.assertEquals(net_test.UDP_PAYLOAD, data) + self.assertEquals(remote_inner, src[0]) + self.assertEquals(1234, src[1]) + + def _TestTunnel(self, inner_version, outer_version, func, direction): + """Test a unidirectional XFRM Tunnel with explicit selectors""" # Select the underlying netid, which represents the external # interface from/to which to route ESP packets. - underlying_netid = self.RandomNetid() + u_netid = self.RandomNetid() # Select a random netid that will originate traffic locally and - # which represents the logical tunnel network. - netid = self.RandomNetid(exclude=underlying_netid) + # which represents the netid on which the plaintext is sent + netid = self.RandomNetid(exclude=u_netid) local_inner = self.MyAddress(inner_version, netid) remote_inner = _GetRemoteInnerAddress(inner_version) - local_outer = self.MyAddress(outer_version, underlying_netid) + local_outer = self.MyAddress(outer_version, u_netid) remote_outer = _GetRemoteOuterAddress(outer_version) + # Create input/ouput SPs, SAs and sockets to simulate a more realistic + # environment. + self.xfrm.CreateTunnel(xfrm.XFRM_POLICY_IN, + xfrm.SrcDstSelector(remote_inner, local_inner), + remote_outer, local_outer, _TEST_IN_SPI, + xfrm_base._ALGO_CRYPT_NULL, + xfrm_base._ALGO_AUTH_NULL, None, None, None) + self.xfrm.CreateTunnel(xfrm.XFRM_POLICY_OUT, xfrm.SrcDstSelector(local_inner, remote_inner), local_outer, remote_outer, _TEST_OUT_SPI, xfrm_base._ALGO_CBC_AES_256, - xfrm_base._ALGO_HMAC_SHA1, - None, underlying_netid, None) + xfrm_base._ALGO_HMAC_SHA1, None, u_netid, None) write_sock = socket(net_test.GetAddressFamily(inner_version), SOCK_DGRAM, 0) - # Select an interface, which provides the source address of the inner - # packet. self.SelectInterface(write_sock, netid, "mark") - write_sock.sendto(net_test.UDP_PAYLOAD, (remote_inner, 53)) - self._ExpectEspPacketOn(underlying_netid, _TEST_OUT_SPI, 1, None, - local_outer, remote_outer) - - # TODO: Add support for the input path. - - def testIpv4InIpv4TunnelOutput(self): - self._CheckTunnelOutput(4, 4) + read_sock = socket(net_test.GetAddressFamily(inner_version), SOCK_DGRAM, 0) + read_sock.bind((net_test.GetWildcardAddress(inner_version), 0)) + # Guard against the eventuality of the receive failing. + net_test.SetNonBlocking(read_sock.fileno()) - def testIpv4InIpv6TunnelOutput(self): - self._CheckTunnelOutput(4, 6) + sock = write_sock if direction == xfrm.XFRM_POLICY_OUT else read_sock + func(inner_version, outer_version, u_netid, netid, local_inner, + remote_inner, local_outer, remote_outer, sock) - def testIpv6InIpv4TunnelOutput(self): - self._CheckTunnelOutput(6, 4) + def ParamTestTunnelInput(self, inner_version, outer_version): + self._TestTunnel(inner_version, outer_version, self._CheckTunnelInput, + xfrm.XFRM_POLICY_IN) - def testIpv6InIpv6TunnelOutput(self): - self._CheckTunnelOutput(6, 6) + def ParamTestTunnelOutput(self, inner_version, outer_version): + self._TestTunnel(inner_version, outer_version, self._CheckTunnelOutput, + xfrm.XFRM_POLICY_OUT) @unittest.skipUnless(net_test.LINUX_VERSION >= (3, 18, 0), "VTI Unsupported") @@ -448,7 +500,7 @@ class XfrmTunnelBase(xfrm_base.XfrmBaseTest): # The second parameter of the tuple is the port number regardless of AF. port = read_sock.getsockname()[1] # Guard against the eventuality of the receive failing. - csocket.SetSocketTimeout(read_sock, 100) + net_test.SetNonBlocking(read_sock.fileno()) # Send a packet out via the tunnel-backed network, bound for the port number # of the input socket. @@ -522,20 +574,21 @@ class XfrmTunnelBase(xfrm_base.XfrmBaseTest): @unittest.skipUnless(net_test.LINUX_VERSION >= (3, 18, 0), "VTI Unsupported") class XfrmVtiTest(XfrmTunnelBase): - INTERFACE_CLASS = VtiInterface + INTERFACE_CLASS = VtiInterface - def testVtiInputOutput(self): - self.CheckTunnelInputOutput() + def testVtiInputOutput(self): + self.CheckTunnelInputOutput() @unittest.skipUnless(HAVE_XFRM_INTERFACES, "XFRM interfaces unsupported") class XfrmInterfaceTest(XfrmTunnelBase): - INTERFACE_CLASS = XfrmInterface + INTERFACE_CLASS = XfrmInterface - def testXfrmiInputOutput(self): - self.CheckTunnelInputOutput() + def testXfrmiInputOutput(self): + self.CheckTunnelInputOutput() if __name__ == "__main__": + InjectTests() unittest.main() -- cgit v1.2.3 From 91b562615b825f859f2a07f4e42de03b2e62e0c0 Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Thu, 11 Jan 2018 15:50:43 -0800 Subject: Refactor VTI tests to support null encryption -Split input and output into separate tests -Refactor to inject null encrypted packets while still allowing encrypted packets to be mirrored -Add a check to ensure plaintext packets are not sent. Bug: 66467511 Test: this Change-Id: I6cad55f65d32fae9ea17fb44ea796bdcd4aba3a3 --- net/test/xfrm_tunnel_test.py | 515 +++++++++++++++++++++++++++++-------------- 1 file changed, 350 insertions(+), 165 deletions(-) diff --git a/net/test/xfrm_tunnel_test.py b/net/test/xfrm_tunnel_test.py index e71b46b..652a0c2 100755 --- a/net/test/xfrm_tunnel_test.py +++ b/net/test/xfrm_tunnel_test.py @@ -56,20 +56,22 @@ def HaveXfrmInterfaces(): HAVE_XFRM_INTERFACES = HaveXfrmInterfaces() -# Parameters to Set up VTI as a special network -_BASE_VTI_NETID = {4: 40, 6: 60} +# Parameters to setup tunnels as special networks +_TUNNEL_NETID_OFFSET = 0xFC00 # Matches reserved netid range for IpSecService +_BASE_TUNNEL_NETID = {4: 40, 6: 60} _BASE_VTI_OKEY = 2000000100 _BASE_VTI_IKEY = 2000000200 -_VTI_NETID = 50 -_VTI_IFNAME = "test_vti" - _TEST_OUT_SPI = 0x1234 _TEST_IN_SPI = _TEST_OUT_SPI _TEST_OKEY = 2000000100 _TEST_IKEY = 2000000200 +_TEST_REMOTE_PORT = 1234 + +_SCAPY_IP_TYPE = {4: scapy.IP, 6: scapy.IPv6} + def _GetLocalInnerAddress(version): return {4: "10.16.5.15", 6: "2001:db8:1::1"}[version] @@ -83,8 +85,50 @@ def _GetRemoteOuterAddress(version): return {4: net_test.IPV4_ADDR, 6: net_test.IPV6_ADDR}[version] +def _GetNullAuthCryptTunnelModePkt(inner_version, src_inner, src_outer, + src_port, dst_inner, dst_outer, + dst_port, spi, seq_num, ip_hdr_options={}): + ip_hdr_options.update({'src': src_inner, 'dst': dst_inner}) + + # Build and receive an ESP packet destined for the inner socket + IpType = {4: scapy.IP, 6: scapy.IPv6}[inner_version] + input_pkt = ( + IpType(**ip_hdr_options) / scapy.UDP(sport=src_port, dport=dst_port) / + net_test.UDP_PAYLOAD) + input_pkt = IpType(str(input_pkt)) # Compute length, checksum. + input_pkt = xfrm_base.EncryptPacketWithNull(input_pkt, spi, seq_num, + (src_outer, dst_outer)) + + return input_pkt + + +def _CreateReceiveSock(version, port=0): + # Create a socket to receive packets. + read_sock = socket(net_test.GetAddressFamily(version), SOCK_DGRAM, 0) + read_sock.bind((net_test.GetWildcardAddress(version), port)) + # The second parameter of the tuple is the port number regardless of AF. + local_port = read_sock.getsockname()[1] + # Guard against the eventuality of the receive failing. + net_test.SetNonBlocking(read_sock.fileno()) + + return read_sock, local_port + + +def _SendPacket(testInstance, netid, version, remote, remote_port): + # Send a packet out via the tunnel-backed network, bound for the port number + # of the input socket. + write_sock = socket(net_test.GetAddressFamily(version), SOCK_DGRAM, 0) + testInstance.SelectInterface(write_sock, netid, "mark") + write_sock.sendto(net_test.UDP_PAYLOAD, (remote, remote_port)) + local_port = write_sock.getsockname()[1] + + return local_port + + def InjectTests(): InjectParameterizedTests(XfrmTunnelTest) + InjectParameterizedTests(XfrmInterfaceTest) + InjectParameterizedTests(XfrmVtiTest) def InjectParameterizedTests(cls): @@ -115,20 +159,15 @@ class XfrmTunnelTest(xfrm_base.XfrmLazyTest): local_port = read_sock.getsockname()[1] # Build and receive an ESP packet destined for the inner socket - IpType = {4: scapy.IP, 6: scapy.IPv6}[inner_version] - input_pkt = ( - IpType(src=remote_inner, dst=local_inner) / scapy.UDP( - sport=1234, dport=local_port) / net_test.UDP_PAYLOAD) - input_pkt = IpType(str(input_pkt)) # Compute length, checksum. - input_pkt = xfrm_base.EncryptPacketWithNull(input_pkt, _TEST_IN_SPI, 1, - (remote_outer, local_outer)) + input_pkt = _GetNullAuthCryptTunnelModePkt( + inner_version, remote_inner, remote_outer, _TEST_REMOTE_PORT, + local_inner, local_outer, local_port, _TEST_IN_SPI, 1) self.ReceivePacketOn(underlying_netid, input_pkt) # Verify that the packet data and src are correct data, src = read_sock.recvfrom(4096) self.assertEquals(net_test.UDP_PAYLOAD, data) - self.assertEquals(remote_inner, src[0]) - self.assertEquals(1234, src[1]) + self.assertEquals((remote_inner, _TEST_REMOTE_PORT), src[:2]) def _TestTunnel(self, inner_version, outer_version, func, direction): """Test a unidirectional XFRM Tunnel with explicit selectors""" @@ -160,10 +199,7 @@ class XfrmTunnelTest(xfrm_base.XfrmLazyTest): write_sock = socket(net_test.GetAddressFamily(inner_version), SOCK_DGRAM, 0) self.SelectInterface(write_sock, netid, "mark") - read_sock = socket(net_test.GetAddressFamily(inner_version), SOCK_DGRAM, 0) - read_sock.bind((net_test.GetWildcardAddress(inner_version), 0)) - # Guard against the eventuality of the receive failing. - net_test.SetNonBlocking(read_sock.fileno()) + read_sock, _ = _CreateReceiveSock(inner_version) sock = write_sock if direction == xfrm.XFRM_POLICY_OUT else read_sock func(inner_version, outer_version, u_netid, netid, local_inner, @@ -197,38 +233,37 @@ class XfrmAddDeleteVtiTest(xfrm_base.XfrmBaseTest): netid = self.RandomNetid() local_addr = self.MyAddress(version, netid) self.iproute.CreateVirtualTunnelInterface( - dev_name=_VTI_IFNAME, + dev_name=_TEST_XFRM_IFNAME, local_addr=local_addr, remote_addr=_GetRemoteOuterAddress(version), o_key=_TEST_OKEY, i_key=_TEST_IKEY) - self._VerifyVtiInfoData(self.iproute.GetIfinfoData(_VTI_IFNAME), - version, local_addr, - _GetRemoteOuterAddress(version), - _TEST_IKEY, _TEST_OKEY) + self._VerifyVtiInfoData( + self.iproute.GetIfinfoData(_TEST_XFRM_IFNAME), version, local_addr, + _GetRemoteOuterAddress(version), _TEST_IKEY, _TEST_OKEY) new_remote_addr = {4: net_test.IPV4_ADDR2, 6: net_test.IPV6_ADDR2} - new_okey = _TEST_OKEY + _VTI_NETID - new_ikey = _TEST_IKEY + _VTI_NETID + new_okey = _TEST_OKEY + _TEST_XFRM_IF_ID + new_ikey = _TEST_IKEY + _TEST_XFRM_IF_ID self.iproute.CreateVirtualTunnelInterface( - dev_name=_VTI_IFNAME, + dev_name=_TEST_XFRM_IFNAME, local_addr=local_addr, remote_addr=new_remote_addr[version], o_key=new_okey, i_key=new_ikey, is_update=True) - self._VerifyVtiInfoData(self.iproute.GetIfinfoData(_VTI_IFNAME), - version, local_addr, new_remote_addr[version], - new_ikey, new_okey) + self._VerifyVtiInfoData( + self.iproute.GetIfinfoData(_TEST_XFRM_IFNAME), version, local_addr, + new_remote_addr[version], new_ikey, new_okey) - if_index = self.iproute.GetIfIndex(_VTI_IFNAME) + if_index = self.iproute.GetIfIndex(_TEST_XFRM_IFNAME) # Validate that the netlink interface matches the ioctl interface. - self.assertEquals(net_test.GetInterfaceIndex(_VTI_IFNAME), if_index) - self.iproute.DeleteLink(_VTI_IFNAME) + self.assertEquals(net_test.GetInterfaceIndex(_TEST_XFRM_IFNAME), if_index) + self.iproute.DeleteLink(_TEST_XFRM_IFNAME) with self.assertRaises(IOError): - self.iproute.GetIfIndex(_VTI_IFNAME) + self.iproute.GetIfIndex(_TEST_XFRM_IFNAME) def _QuietDeleteLink(self, ifname): try: @@ -239,59 +274,94 @@ class XfrmAddDeleteVtiTest(xfrm_base.XfrmBaseTest): def tearDown(self): super(XfrmAddDeleteVtiTest, self).tearDown() - self._QuietDeleteLink(_VTI_IFNAME) + self._QuietDeleteLink(_TEST_XFRM_IFNAME) + +class SaInfo(object): -class VtiInterface(object): + def __init__(self, spi): + self.spi = spi + self.seq_num = 1 - def __init__(self, iface, netid, underlying_netid, _, local, remote): + +class IpSecBaseInterface(object): + + def __init__(self, iface, netid, underlying_netid, local, remote, version): self.iface = iface self.netid = netid self.underlying_netid = underlying_netid self.local, self.remote = local, remote + + # XFRM interfaces technically do not have a version. This keeps track of + # the IP version of the local and remote addresses. + self.version = version self.rx = self.tx = 0 - self.ikey = _TEST_IKEY + netid - self.okey = _TEST_OKEY + netid - self.out_spi = self.in_spi = random.randint(0, 0x7fffffff) + self.addrs = {} self.iproute = iproute.IPRoute() self.xfrm = xfrm.Xfrm() - self.SetupInterface() - self.SetupXfrm() - self.addrs = {} - def Teardown(self): self.TeardownXfrm() self.TeardownInterface() + def TeardownInterface(self): + self.iproute.DeleteLink(self.iface) + + def SetupXfrm(self, use_null_crypt): + rand_spi = random.randint(0, 0x7fffffff) + self.in_sa = SaInfo(rand_spi) + self.out_sa = SaInfo(rand_spi) + + # Select algorithms: + if use_null_crypt: + auth, crypt = xfrm_base._ALGO_AUTH_NULL, xfrm_base._ALGO_CRYPT_NULL + else: + auth, crypt = xfrm_base._ALGO_HMAC_SHA1, xfrm_base._ALGO_CBC_AES_256 + + self._SetupXfrmByType(auth, crypt) + + def TeardownXfrm(self): + raise NotImplementedError("Subclasses should implement this") + + def _SetupXfrmByType(self, auth_algo, crypt_algo): + raise NotImplementedError("Subclasses should implement this") + + +class VtiInterface(IpSecBaseInterface): + + def __init__(self, iface, netid, underlying_netid, _, local, remote, version): + super(VtiInterface, self).__init__(iface, netid, underlying_netid, local, + remote, version) + + self.ikey = _TEST_IKEY + netid + self.okey = _TEST_OKEY + netid + + self.SetupInterface() + self.SetupXfrm(False) + def SetupInterface(self): return self.iproute.CreateVirtualTunnelInterface( self.iface, self.local, self.remote, self.ikey, self.okey) - def TeardownInterface(self): - self.iproute.DeleteLink(self.iface) - - def SetupXfrm(self): + def _SetupXfrmByType(self, auth_algo, crypt_algo): # For the VTI, the selectors are wildcard since packets will only # be selected if they have the appropriate mark, hence the inner # addresses are wildcard. 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, + self.out_sa.spi, crypt_algo, auth_algo, xfrm.ExactMatchMark(self.okey), 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, + self.in_sa.spi, crypt_algo, auth_algo, xfrm.ExactMatchMark(self.ikey), None, None) def TeardownXfrm(self): self.xfrm.DeleteTunnel(xfrm.XFRM_POLICY_OUT, None, self.remote, - self.out_spi, self.okey, None) + self.out_sa.spi, self.okey, None) self.xfrm.DeleteTunnel(xfrm.XFRM_POLICY_IN, None, self.local, - self.in_spi, self.ikey, None) + self.in_sa.spi, self.ikey, None) @unittest.skipUnless(HAVE_XFRM_INTERFACES, "XFRM interfaces unsupported") @@ -311,53 +381,36 @@ class XfrmAddDeleteXfrmInterfaceTest(xfrm_base.XfrmBaseTest): self.iproute.GetIfIndex(_TEST_XFRM_IFNAME) -class XfrmInterface(object): +class XfrmInterface(IpSecBaseInterface): + + def __init__(self, iface, netid, underlying_netid, ifindex, local, remote, + version): + super(XfrmInterface, self).__init__(iface, netid, underlying_netid, local, + remote, version) - 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() + self.SetupXfrm(False) 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): + def _SetupXfrmByType(self, auth_algo, crypt_algo): 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.out_sa.spi, crypt_algo, auth_algo, 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) - + self.in_sa.spi, crypt_algo, auth_algo, 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.out_sa.spi, None, self.xfrm_if_id) self.xfrm.DeleteTunnel(xfrm.XFRM_POLICY_IN, None, self.local, - self.in_spi, None, self.xfrm_if_id) + self.in_sa.spi, None, self.xfrm_if_id) @@ -366,43 +419,54 @@ class XfrmTunnelBase(xfrm_base.XfrmBaseTest): @classmethod def setUpClass(cls): xfrm_base.XfrmBaseTest.setUpClass() - # VTI interfaces use marks extensively, so configure realistic packet + # Tunnel interfaces use marks extensively, so configure realistic packet # marking rules to make the test representative, make PMTUD work, etc. cls.SetInboundMarks(True) cls.SetMarkReflectSysctls(1) - cls.tunnels = {} + # Group by tunnel version to ensure that we test at least one IPv4 and one + # IPv6 tunnel + cls.tunnelsV4 = {} + cls.tunnelsV6 = {} for i, underlying_netid in enumerate(cls.tuns): for version in 4, 6: - netid = _BASE_VTI_NETID[version] + i + netid = _BASE_TUNNEL_NETID[version] + _TUNNEL_NETID_OFFSET + i iface = "ipsec%s" % netid local = cls.MyAddress(version, underlying_netid) if version == 4: - remote = net_test.IPV4_ADDR2 if (i % 2) else net_test.IPV4_ADDR + remote = (net_test.IPV4_ADDR if (i % 2) else net_test.IPV4_ADDR2) else: - remote = net_test.IPV6_ADDR2 if (i % 2) else net_test.IPV6_ADDR - ifindex = cls.ifindices[underlying_netid] + remote = (net_test.IPV6_ADDR if (i % 2) else net_test.IPV6_ADDR2) + ifindex = cls.ifindices[underlying_netid] tunnel = cls.INTERFACE_CLASS(iface, netid, underlying_netid, ifindex, - local, remote) - + local, remote, version) cls._SetInboundMarking(netid, iface, True) cls._SetupTunnelNetwork(tunnel, True) - cls.tunnels[netid] = tunnel + + if version == 4: + cls.tunnelsV4[netid] = tunnel + else: + cls.tunnelsV6[netid] = tunnel @classmethod def tearDownClass(cls): # The sysctls are restored by MultinetworkBaseTest.tearDownClass. cls.SetInboundMarks(False) - for tunnel in cls.tunnels.values(): + for tunnel in cls.tunnelsV4.values() + cls.tunnelsV6.values(): cls._SetInboundMarking(tunnel.netid, tunnel.iface, False) cls._SetupTunnelNetwork(tunnel, False) tunnel.Teardown() xfrm_base.XfrmBaseTest.tearDownClass() + def randomTunnel(self, outer_version): + version_dict = self.tunnelsV4 if outer_version == 4 else self.tunnelsV6 + return random.choice(version_dict.values()) + def setUp(self): multinetwork_base.MultiNetworkBaseTest.setUp(self) self.iproute = iproute.IPRoute() + self.xfrm = xfrm.Xfrm() def tearDown(self): multinetwork_base.MultiNetworkBaseTest.tearDown(self) @@ -422,6 +486,13 @@ class XfrmTunnelBase(xfrm_base.XfrmBaseTest): self.iproute.DelAddress(old_addr, net_test.AddressLengthBits(version), ifindex) + @classmethod + def _GetLocalAddress(cls, version, netid): + if version == 4: + return cls._MyIPv4Address(netid - _TUNNEL_NETID_OFFSET) + else: + return cls.OnlinkPrefix(6, netid - _TUNNEL_NETID_OFFSET) + "1" + @classmethod def _SetupTunnelNetwork(cls, tunnel, is_add): """Setup rules and routes for a tunnel Network. @@ -461,10 +532,7 @@ class XfrmTunnelBase(xfrm_base.XfrmBaseTest): table, cls.PRIORITY_FWMARK) # Configure IP addresses. - if version == 4: - addr = cls._MyIPv4Address(tunnel.netid) - else: - addr = cls.OnlinkPrefix(6, tunnel.netid) + "1" + addr = cls._GetLocalAddress(version, tunnel.netid) prefixlen = net_test.AddressLengthBits(version) tunnel.addrs[version] = addr if is_add: @@ -474,83 +542,149 @@ class XfrmTunnelBase(xfrm_base.XfrmBaseTest): cls.iproute.DelRoute(version, table, "default", 0, None, ifindex) cls.iproute.DelAddress(addr, prefixlen, ifindex) - def assertReceivedPacket(self, tunnel): + def assertReceivedPacket(self, tunnel, sa_info): tunnel.rx += 1 self.assertEquals((tunnel.rx, tunnel.tx), self.iproute.GetRxTxPackets(tunnel.iface)) + sa_info.seq_num += 1 - def assertSentPacket(self, tunnel): + def assertSentPacket(self, tunnel, sa_info): tunnel.tx += 1 self.assertEquals((tunnel.rx, tunnel.tx), self.iproute.GetRxTxPackets(tunnel.iface)) + sa_info.seq_num += 1 - # 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 _CheckTunnelInputOutput(self, tunnel, inner_version): - local_outer = tunnel.local - remote_outer = tunnel.remote - - # Create a socket to receive packets. - read_sock = socket( - net_test.GetAddressFamily(inner_version), SOCK_DGRAM, 0) - read_sock.bind((net_test.GetWildcardAddress(inner_version), 0)) - # The second parameter of the tuple is the port number regardless of AF. - port = read_sock.getsockname()[1] - # Guard against the eventuality of the receive failing. - net_test.SetNonBlocking(read_sock.fileno()) - - # 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, tunnel.netid, "mark") - write_sock.sendto(net_test.UDP_PAYLOAD, - (_GetRemoteInnerAddress(inner_version), port)) + def _CheckTunnelInput(self, tunnel, inner_version, local_inner, remote_inner, + sa_info=None): + """Test null-crypt input path over an IPsec interface.""" + if sa_info is None: + sa_info = tunnel.in_sa + read_sock, local_port = _CreateReceiveSock(inner_version) + + input_pkt = _GetNullAuthCryptTunnelModePkt( + inner_version, remote_inner, tunnel.remote, _TEST_REMOTE_PORT, + local_inner, tunnel.local, local_port, sa_info.spi, sa_info.seq_num) + self.ReceivePacketOn(tunnel.underlying_netid, input_pkt) + + # Verify that the packet data and src are correct + self.assertReceivedPacket(tunnel, sa_info) + data, src = read_sock.recvfrom(4096) + self.assertEquals(net_test.UDP_PAYLOAD, data) + self.assertEquals((remote_inner, _TEST_REMOTE_PORT), src[:2]) + + def _CheckTunnelOutput(self, tunnel, inner_version, local_inner, + remote_inner, sa_info=None): + """Test null-crypt output path over an IPsec interface.""" + if sa_info is None: + sa_info = tunnel.out_sa + local_port = _SendPacket(self, tunnel.netid, inner_version, remote_inner, + _TEST_REMOTE_PORT) # Read a tunneled IP packet on the underlying (outbound) network # verifying that it is an ESP packet. - 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 tunnel interface; this - # way, the twisted inner packet finds a destination via the tunnel once - # decrypted. - remote = _GetRemoteInnerAddress(inner_version) - local = tunnel.addrs[inner_version] - self._SwapInterfaceAddress(tunnel.iface, new_addr=remote, old_addr=local) + pkt = self._ExpectEspPacketOn(tunnel.underlying_netid, sa_info.spi, + sa_info.seq_num, None, tunnel.local, + tunnel.remote) + + # Get and update the IP headers on the inner payload so that we can do a simple + # comparison of byte data. Unfortunately, due to the scapy version this runs on, + # we cannot parse past the ESP header to the inner IP header, and thus have to + # workaround in this manner + if inner_version == 4: + ip_hdr_options = { + 'id': scapy.IP(str(pkt.payload)[8:]).id, + 'flags': scapy.IP(str(pkt.payload)[8:]).flags + } + else: + ip_hdr_options = {'fl': scapy.IPv6(str(pkt.payload)[8:]).fl} + + expected = _GetNullAuthCryptTunnelModePkt( + inner_version, local_inner, tunnel.local, local_port, remote_inner, + tunnel.remote, _TEST_REMOTE_PORT, sa_info.spi, sa_info.seq_num, + ip_hdr_options) + + # Check outer header manually (Avoids having to overwrite outer header's + # id, flags or flow label) + self.assertSentPacket(tunnel, sa_info) + self.assertEquals(expected.src, pkt.src) + self.assertEquals(expected.dst, pkt.dst) + self.assertEquals(len(expected), len(pkt)) + + # Check everything else + self.assertEquals(str(expected.payload), str(pkt.payload)) + + def _CheckTunnelEncryption(self, tunnel, inner_version, local_inner, + remote_inner): + """Test both input and output paths over an encrypted IPsec interface. + + This tests specifically makes sure that the both encryption and decryption + work together, as opposed to the _CheckTunnel(Input|Output) where the + input and output paths are tested separately, and using null encryption. + """ + src_port = _SendPacket(self, tunnel.netid, inner_version, remote_inner, + _TEST_REMOTE_PORT) + + # Make sure it appeared on the underlying interface + pkt = self._ExpectEspPacketOn(tunnel.underlying_netid, tunnel.out_sa.spi, + tunnel.out_sa.seq_num, None, tunnel.local, + tunnel.remote) + + # Check that packet is not sent in plaintext + self.assertTrue(str(net_test.UDP_PAYLOAD) not in str(pkt)) + + # Check src/dst + self.assertEquals(tunnel.local, pkt.src) + self.assertEquals(tunnel.remote, pkt.dst) + + # Check that the interface statistics recorded the outbound packet + self.assertSentPacket(tunnel, tunnel.out_sa) + try: - # Swap the packet's IP headers and write it back to the - # underlying network. + # Swap the interface addresses to pretend we are the remote + self._SwapInterfaceAddress( + tunnel.iface, new_addr=remote_inner, old_addr=local_inner) + + # Swap the packet's IP headers and write it back to the underlying + # network. pkt = TunTwister.TwistPacket(pkt) + read_sock, local_port = _CreateReceiveSock(inner_version, + _TEST_REMOTE_PORT) 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(tunnel.iface, new_addr=local, old_addr=remote) + # Verify that the packet data and src are correct + data, src = read_sock.recvfrom(4096) + self.assertEquals(net_test.UDP_PAYLOAD, data) + self.assertEquals((local_inner, src_port), src[:2]) + + # Check that the interface statistics recorded the inbound packet + self.assertReceivedPacket(tunnel, tunnel.in_sa) + finally: + # Swap the interface addresses to pretend we are the remote + self._SwapInterfaceAddress( + tunnel.iface, new_addr=local_inner, old_addr=remote_inner) + + def _CheckTunnelIcmp(self, tunnel, inner_version, local_inner, remote_inner, + sa_info=None): + """Test ICMP error path over an IPsec interface.""" + if sa_info is None: + sa_info = tunnel.out_sa # Now attempt to provoke an ICMP error. # TODO: deduplicate with multinetwork_test.py. - version = net_test.GetAddressVersion(tunnel.remote) dst_prefix, intermediate = { 4: ("172.19.", "172.16.9.12"), 6: ("2001:db8::", "2001:db8::1") - }[version] - - write_sock.sendto(net_test.UDP_PAYLOAD, - (_GetRemoteInnerAddress(inner_version), port)) - self.assertSentPacket(tunnel) - pkt = self._ExpectEspPacketOn(tunnel.underlying_netid, tunnel.out_spi, tunnel.tx, None, - local_outer, remote_outer) - myaddr = self.MyAddress(version, tunnel.underlying_netid) - _, toobig = packets.ICMPPacketTooBig(version, intermediate, myaddr, pkt) + }[tunnel.version] + + local_port = _SendPacket(self, tunnel.netid, inner_version, remote_inner, + _TEST_REMOTE_PORT) + pkt = self._ExpectEspPacketOn(tunnel.underlying_netid, sa_info.spi, + sa_info.seq_num, None, tunnel.local, + tunnel.remote) + self.assertSentPacket(tunnel, sa_info) + + myaddr = self.MyAddress(tunnel.version, tunnel.underlying_netid) + _, toobig = packets.ICMPPacketTooBig(tunnel.version, intermediate, myaddr, + pkt) self.ReceivePacketOn(tunnel.underlying_netid, toobig) # Check that the packet too big reduced the MTU. @@ -561,14 +695,35 @@ class XfrmTunnelBase(xfrm_base.XfrmBaseTest): 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, tunnel.underlying_netid) + self.InvalidateDstCache(tunnel.version, tunnel.underlying_netid) + + def _CheckTunnelEncryptionWithIcmp(self, tunnel, inner_version, local_inner, + remote_inner): + """Test combined encryption path with ICMP errors over an IPsec tunnel""" + self._CheckTunnelEncryption(tunnel, inner_version, local_inner, + remote_inner) + self._CheckTunnelIcmp(tunnel, inner_version, local_inner, remote_inner) + self._CheckTunnelEncryption(tunnel, inner_version, local_inner, + remote_inner) + + 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: + tunnel.TeardownXfrm() + tunnel.SetupXfrm(use_null_crypt) + + local_inner = tunnel.addrs[inner_version] + remote_inner = _GetRemoteInnerAddress(inner_version) - def CheckTunnelInputOutput(self): - """Test packet input and output over a Virtual Tunnel Interface.""" - for i in xrange(3 * len(self.tunnels.values())): - tunnel = random.choice(self.tunnels.values()) - self._CheckTunnelInputOutput(tunnel, 4) - self._CheckTunnelInputOutput(tunnel, 6) + # Run twice to ensure sequence numbers are tested + for i in range(2): + func(tunnel, inner_version, local_inner, remote_inner) + finally: + if use_null_crypt: + tunnel.TeardownXfrm() + tunnel.SetupXfrm(False) @unittest.skipUnless(net_test.LINUX_VERSION >= (3, 18, 0), "VTI Unsupported") @@ -576,8 +731,23 @@ class XfrmVtiTest(XfrmTunnelBase): INTERFACE_CLASS = VtiInterface - def testVtiInputOutput(self): - self.CheckTunnelInputOutput() + def ParamTestVtiInput(self, inner_version, outer_version): + self._TestTunnel(inner_version, outer_version, self._CheckTunnelInput, True) + + def ParamTestVtiOutput(self, inner_version, outer_version): + self._TestTunnel(inner_version, outer_version, self._CheckTunnelOutput, + True) + + def ParamTestVtiInOutEncrypted(self, inner_version, outer_version): + self._TestTunnel(inner_version, outer_version, self._CheckTunnelEncryption, + False) + + def ParamTestVtiIcmp(self, inner_version, outer_version): + self._TestTunnel(inner_version, outer_version, self._CheckTunnelIcmp, False) + + def ParamTestVtiEncryptionWithIcmp(self, inner_version, outer_version): + self._TestTunnel(inner_version, outer_version, + self._CheckTunnelEncryptionWithIcmp, False) @unittest.skipUnless(HAVE_XFRM_INTERFACES, "XFRM interfaces unsupported") @@ -585,8 +755,23 @@ class XfrmInterfaceTest(XfrmTunnelBase): INTERFACE_CLASS = XfrmInterface - def testXfrmiInputOutput(self): - self.CheckTunnelInputOutput() + def ParamTestXfrmIntfInput(self, inner_version, outer_version): + self._TestTunnel(inner_version, outer_version, self._CheckTunnelInput, True) + + def ParamTestXfrmIntfOutput(self, inner_version, outer_version): + self._TestTunnel(inner_version, outer_version, self._CheckTunnelOutput, + True) + + def ParamTestXfrmIntfInOutEncrypted(self, inner_version, outer_version): + self._TestTunnel(inner_version, outer_version, self._CheckTunnelEncryption, + False) + + def ParamTestXfrmIntfIcmp(self, inner_version, outer_version): + self._TestTunnel(inner_version, outer_version, self._CheckTunnelIcmp, False) + + def ParamTestXfrmIntfEncryptionWithIcmp(self, inner_version, outer_version): + self._TestTunnel(inner_version, outer_version, + self._CheckTunnelEncryptionWithIcmp, False) if __name__ == "__main__": -- cgit v1.2.3 From e59c063ef4331ca047d51a0df4aa4dd0ce49e09e Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Thu, 23 Aug 2018 16:08:02 -0700 Subject: Always test UDP_DIAG for 4.9 kernel The devices that ship with P and running 4.9 kernel need CONFIG_INET_UDP_DIAG to work properly. It cannot be added to android-base.config now but it is required for P. So we do not skip the test for that config anymore in this case. Bug: 112068616 Test: sock_diag_test pass on device Change-Id: I10b26a8e667de83cd156b4622eb7a4cd0b35bb2e --- net/test/sock_diag_test.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/net/test/sock_diag_test.py b/net/test/sock_diag_test.py index e25035b..4b1d055 100755 --- a/net/test/sock_diag_test.py +++ b/net/test/sock_diag_test.py @@ -39,10 +39,24 @@ HAVE_SO_COOKIE_SUPPORT = net_test.LINUX_VERSION >= (4, 9, 0) IPPROTO_SCTP = 132 def HaveUdpDiag(): - # There is no way to tell whether a dump succeeded: if the appropriate handler - # wasn't found, __inet_diag_dump just returns an empty result instead of an - # error. So, just check to see if a UDP dump returns no sockets when we know - # it should return one. + """Checks if the current kernel has config CONFIG_INET_UDP_DIAG enabled. + + This config is required for device running 4.9 kernel that ship with P, In + this case always assume the config is there and use the tests to check if the + config is enabled as required. + + For all ther other kernel version, there is no way to tell whether a dump + succeeded: if the appropriate handler wasn't found, __inet_diag_dump just + returns an empty result instead of an error. So, just check to see if a UDP + dump returns no sockets when we know it should return one. If not, some tests + will be skipped. + + Returns: + True if the kernel is 4.9 or above, or the CONFIG_INET_UDP_DIAG is enabled. + False otherwise. + """ + if HAVE_SO_COOKIE_SUPPORT: + return True; s = socket(AF_INET6, SOCK_DGRAM, 0) s.bind(("::", 0)) s.connect((s.getsockname())) -- cgit v1.2.3 From 90ac4e43dc6cfe43bac4373b976d9f1df8cdd58a Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Thu, 11 Jan 2018 15:50:43 -0800 Subject: Add tests for VTI rekey procedure This change adds tests for rekeying of VTIs. The general flow is to create new SAs, update the policy to use the new SAs (outbound should use new SA exclusively, inbound is permissive), and then delete the old SAs. Bug: 66467511 Test: this; Passes on all android-common kernels Change-Id: I65713926e727fdb99200836da918000a5cddfad8 --- net/test/xfrm.py | 30 ++++++- net/test/xfrm_tunnel_test.py | 201 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 206 insertions(+), 25 deletions(-) diff --git a/net/test/xfrm.py b/net/test/xfrm.py index 56b4774..acdfd4f 100755 --- a/net/test/xfrm.py +++ b/net/test/xfrm.py @@ -208,7 +208,7 @@ NO_LIFETIME_CFG = XfrmLifetimeCfg((_INF, _INF, _INF, _INF, 0, 0, 0, 0)) NO_LIFETIME_CUR = "\x00" * len(XfrmLifetimeCur) # IPsec constants. -IPSEC_PROTO_ANY = 255 +IPSEC_PROTO_ANY = 255 # ESP header, not technically XFRM but we need a place for a protocol # header and this is the only one we have. @@ -219,6 +219,11 @@ EspHdr = cstruct.Struct("EspHdr", "!II", "spi seqnum") _DEFAULT_REPLAY_WINDOW = 4 ALL_ALGORITHMS = 0xffffffff +# Policy-SA match method (for VTI/XFRM-I). +MATCH_METHOD_ALL = "all" +MATCH_METHOD_MARK = "mark" +MATCH_METHOD_IFID = "ifid" + def RawAddress(addr): """Converts an IP address string to binary format.""" @@ -630,7 +635,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, xfrm_if_id): + auth_trunc, mark, output_mark, xfrm_if_id, match_method): """Create an XFRM Tunnel Consisting of a Policy and an SA. Create a unidirectional XFRM tunnel, which entails one Policy and one @@ -652,9 +657,28 @@ class Xfrm(netlink.NetlinkSocket): 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. + match_method: One of MATCH_METHOD_[MARK | ALL | IFID]. This determines how + SAs and policies are matched. """ outer_family = net_test.GetAddressFamily(net_test.GetAddressVersion(dst)) + # SA mark is currently unused due to UPDSA not updating marks. + # Kept as documentation of ideal/desired behavior. + if match_method == MATCH_METHOD_MARK: + # sa_mark = mark + tmpl_spi = 0 + if_id = None + elif match_method == MATCH_METHOD_ALL: + # sa_mark = mark + tmpl_spi = spi + if_id = xfrm_if_id + elif match_method == MATCH_METHOD_IFID: + # sa_mark = None + tmpl_spi = 0 + if_id = xfrm_if_id + else: + raise ValueError("Unknown match_method supplied: %s" % match_method) + # Device code does not use mark; during AllocSpi, the mark is unset, and # UPDSA does not update marks at this time. Actual use case will have no # mark set. Test this use case. @@ -668,7 +692,7 @@ class Xfrm(netlink.NetlinkSocket): for selector in selectors: policy = UserPolicy(direction, selector) - tmpl = UserTemplate(outer_family, spi, 0, (src, dst)) + tmpl = UserTemplate(outer_family, tmpl_spi, 0, (src, dst)) self.AddPolicyInfo(policy, tmpl, mark, xfrm_if_id=xfrm_if_id) def DeleteTunnel(self, direction, selector, dst, spi, mark, xfrm_if_id): diff --git a/net/test/xfrm_tunnel_test.py b/net/test/xfrm_tunnel_test.py index 652a0c2..778eb26 100755 --- a/net/test/xfrm_tunnel_test.py +++ b/net/test/xfrm_tunnel_test.py @@ -185,17 +185,15 @@ class XfrmTunnelTest(xfrm_base.XfrmLazyTest): # Create input/ouput SPs, SAs and sockets to simulate a more realistic # environment. - self.xfrm.CreateTunnel(xfrm.XFRM_POLICY_IN, - xfrm.SrcDstSelector(remote_inner, local_inner), - remote_outer, local_outer, _TEST_IN_SPI, - xfrm_base._ALGO_CRYPT_NULL, - xfrm_base._ALGO_AUTH_NULL, None, None, None) - - self.xfrm.CreateTunnel(xfrm.XFRM_POLICY_OUT, - xfrm.SrcDstSelector(local_inner, remote_inner), - local_outer, remote_outer, _TEST_OUT_SPI, - xfrm_base._ALGO_CBC_AES_256, - xfrm_base._ALGO_HMAC_SHA1, None, u_netid, None) + self.xfrm.CreateTunnel( + xfrm.XFRM_POLICY_IN, xfrm.SrcDstSelector(remote_inner, local_inner), + remote_outer, local_outer, _TEST_IN_SPI, xfrm_base._ALGO_CRYPT_NULL, + xfrm_base._ALGO_AUTH_NULL, None, None, None, xfrm.MATCH_METHOD_ALL) + + self.xfrm.CreateTunnel( + xfrm.XFRM_POLICY_OUT, xfrm.SrcDstSelector(local_inner, remote_inner), + local_outer, remote_outer, _TEST_OUT_SPI, xfrm_base._ALGO_CBC_AES_256, + xfrm_base._ALGO_HMAC_SHA1, None, u_netid, None, xfrm.MATCH_METHOD_ALL) write_sock = socket(net_test.GetAddressFamily(inner_version), SOCK_DGRAM, 0) self.SelectInterface(write_sock, netid, "mark") @@ -321,12 +319,31 @@ class IpSecBaseInterface(object): self._SetupXfrmByType(auth, crypt) + def Rekey(self, outer_family, new_out_sa, new_in_sa): + """Rekeys the Tunnel Interface + + Creates new SAs and updates the outbound security policy to use new SAs. + + Args: + outer_family: AF_INET or AF_INET6 + new_out_sa: An SaInfo struct representing the new outbound SA's info + new_in_sa: An SaInfo struct representing the new inbound SA's info + """ + self._Rekey(outer_family, new_out_sa, new_in_sa) + + # Update Interface object + self.out_sa = new_out_sa + self.in_sa = new_in_sa + def TeardownXfrm(self): raise NotImplementedError("Subclasses should implement this") def _SetupXfrmByType(self, auth_algo, crypt_algo): raise NotImplementedError("Subclasses should implement this") + def _Rekey(self, outer_family, new_out_sa, new_in_sa): + raise NotImplementedError("Subclasses should implement this") + class VtiInterface(IpSecBaseInterface): @@ -351,11 +368,12 @@ class VtiInterface(IpSecBaseInterface): self.xfrm.CreateTunnel(xfrm.XFRM_POLICY_OUT, None, self.local, self.remote, self.out_sa.spi, crypt_algo, auth_algo, xfrm.ExactMatchMark(self.okey), - self.underlying_netid, None) + self.underlying_netid, None, xfrm.MATCH_METHOD_ALL) self.xfrm.CreateTunnel(xfrm.XFRM_POLICY_IN, None, self.remote, self.local, self.in_sa.spi, crypt_algo, auth_algo, - xfrm.ExactMatchMark(self.ikey), None, None) + xfrm.ExactMatchMark(self.ikey), None, None, + xfrm.MATCH_METHOD_MARK) def TeardownXfrm(self): self.xfrm.DeleteTunnel(xfrm.XFRM_POLICY_OUT, None, self.remote, @@ -363,6 +381,35 @@ class VtiInterface(IpSecBaseInterface): self.xfrm.DeleteTunnel(xfrm.XFRM_POLICY_IN, None, self.local, self.in_sa.spi, self.ikey, None) + def _Rekey(self, outer_family, new_out_sa, new_in_sa): + # TODO: Consider ways to share code with xfrm.CreateTunnel(). It's mostly + # the same, but rekeys are asymmetric, and only update the outbound + # policy. + self.xfrm.AddSaInfo(self.local, self.remote, new_out_sa.spi, + xfrm.XFRM_MODE_TUNNEL, 0, xfrm_base._ALGO_CRYPT_NULL, + xfrm_base._ALGO_AUTH_NULL, None, None, + xfrm.ExactMatchMark(self.okey), self.underlying_netid) + + self.xfrm.AddSaInfo(self.remote, self.local, new_in_sa.spi, + xfrm.XFRM_MODE_TUNNEL, 0, xfrm_base._ALGO_CRYPT_NULL, + xfrm_base._ALGO_AUTH_NULL, None, None, + xfrm.ExactMatchMark(self.ikey), None) + + # Create new policies for IPv4 and IPv6. + for sel in [xfrm.EmptySelector(AF_INET), xfrm.EmptySelector(AF_INET6)]: + # Add SPI-specific output policy to enforce using new outbound SPI + policy = xfrm.UserPolicy(xfrm.XFRM_POLICY_OUT, sel) + tmpl = xfrm.UserTemplate(outer_family, new_out_sa.spi, 0, + (self.local, self.remote)) + self.xfrm.UpdatePolicyInfo(policy, tmpl, xfrm.ExactMatchMark(self.okey), + 0) + + def DeleteOldSaInfo(self, outer_family, old_in_spi, old_out_spi): + self.xfrm.DeleteSaInfo(self.local, old_in_spi, IPPROTO_ESP, + xfrm.ExactMatchMark(self.ikey)) + self.xfrm.DeleteSaInfo(self.remote, old_out_spi, IPPROTO_ESP, + xfrm.ExactMatchMark(self.okey)) + @unittest.skipUnless(HAVE_XFRM_INTERFACES, "XFRM interfaces unsupported") class XfrmAddDeleteXfrmInterfaceTest(xfrm_base.XfrmBaseTest): @@ -401,10 +448,11 @@ class XfrmInterface(IpSecBaseInterface): def _SetupXfrmByType(self, auth_algo, crypt_algo): self.xfrm.CreateTunnel(xfrm.XFRM_POLICY_OUT, None, self.local, self.remote, self.out_sa.spi, crypt_algo, auth_algo, None, - self.underlying_netid, self.xfrm_if_id) + self.underlying_netid, self.xfrm_if_id, + xfrm.MATCH_METHOD_ALL) self.xfrm.CreateTunnel(xfrm.XFRM_POLICY_IN, None, self.remote, self.local, self.in_sa.spi, crypt_algo, auth_algo, None, None, - self.xfrm_if_id) + self.xfrm_if_id, xfrm.MATCH_METHOD_IFID) def TeardownXfrm(self): self.xfrm.DeleteTunnel(xfrm.XFRM_POLICY_OUT, None, self.remote, @@ -412,6 +460,33 @@ class XfrmInterface(IpSecBaseInterface): self.xfrm.DeleteTunnel(xfrm.XFRM_POLICY_IN, None, self.local, self.in_sa.spi, None, self.xfrm_if_id) + def _Rekey(self, outer_family, new_out_sa, new_in_sa): + # TODO: Consider ways to share code with xfrm.CreateTunnel(). It's mostly + # the same, but rekeys are asymmetric, and only update the outbound + # policy. + self.xfrm.AddSaInfo( + self.local, self.remote, new_out_sa.spi, xfrm.XFRM_MODE_TUNNEL, 0, + xfrm_base._ALGO_CRYPT_NULL, xfrm_base._ALGO_AUTH_NULL, None, None, + None, self.underlying_netid, xfrm_if_id=self.xfrm_if_id) + + self.xfrm.AddSaInfo( + self.remote, self.local, new_in_sa.spi, xfrm.XFRM_MODE_TUNNEL, 0, + xfrm_base._ALGO_CRYPT_NULL, xfrm_base._ALGO_AUTH_NULL, None, None, + None, None, xfrm_if_id=self.xfrm_if_id) + + # Create new policies for IPv4 and IPv6. + for sel in [xfrm.EmptySelector(AF_INET), xfrm.EmptySelector(AF_INET6)]: + # Add SPI-specific output policy to enforce using new outbound SPI + policy = xfrm.UserPolicy(xfrm.XFRM_POLICY_OUT, sel) + tmpl = xfrm.UserTemplate(outer_family, new_out_sa.spi, 0, + (self.local, self.remote)) + self.xfrm.UpdatePolicyInfo(policy, tmpl, None, self.xfrm_if_id) + + def DeleteOldSaInfo(self, outer_family, old_in_spi, old_out_spi): + self.xfrm.DeleteSaInfo(self.local, old_in_spi, IPPROTO_ESP, None, + self.xfrm_if_id) + self.xfrm.DeleteSaInfo(self.remote, old_out_spi, IPPROTO_ESP, None, + self.xfrm_if_id) class XfrmTunnelBase(xfrm_base.XfrmBaseTest): @@ -555,7 +630,7 @@ class XfrmTunnelBase(xfrm_base.XfrmBaseTest): sa_info.seq_num += 1 def _CheckTunnelInput(self, tunnel, inner_version, local_inner, remote_inner, - sa_info=None): + sa_info=None, expect_fail=False): """Test null-crypt input path over an IPsec interface.""" if sa_info is None: sa_info = tunnel.in_sa @@ -566,11 +641,14 @@ class XfrmTunnelBase(xfrm_base.XfrmBaseTest): local_inner, tunnel.local, local_port, sa_info.spi, sa_info.seq_num) self.ReceivePacketOn(tunnel.underlying_netid, input_pkt) - # Verify that the packet data and src are correct - self.assertReceivedPacket(tunnel, sa_info) - data, src = read_sock.recvfrom(4096) - self.assertEquals(net_test.UDP_PAYLOAD, data) - self.assertEquals((remote_inner, _TEST_REMOTE_PORT), src[:2]) + if expect_fail: + self.assertRaisesErrno(EAGAIN, read_sock.recv, 4096) + else: + # Verify that the packet data and src are correct + self.assertReceivedPacket(tunnel, sa_info) + data, src = read_sock.recvfrom(4096) + self.assertEquals(net_test.UDP_PAYLOAD, data) + self.assertEquals((remote_inner, _TEST_REMOTE_PORT), src[:2]) def _CheckTunnelOutput(self, tunnel, inner_version, local_inner, remote_inner, sa_info=None): @@ -711,13 +789,25 @@ class XfrmTunnelBase(xfrm_base.XfrmBaseTest): 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) local_inner = tunnel.addrs[inner_version] remote_inner = _GetRemoteInnerAddress(inner_version) - # Run twice to ensure sequence numbers are tested for i in range(2): func(tunnel, inner_version, local_inner, remote_inner) finally: @@ -725,6 +815,67 @@ class XfrmTunnelBase(xfrm_base.XfrmBaseTest): tunnel.TeardownXfrm() tunnel.SetupXfrm(False) + def _CheckTunnelRekey(self, tunnel, inner_version, local_inner, remote_inner): + old_out_sa = tunnel.out_sa + old_in_sa = tunnel.in_sa + + # Check to make sure that both directions work before rekey + self._CheckTunnelInput(tunnel, inner_version, local_inner, remote_inner, + old_in_sa) + self._CheckTunnelOutput(tunnel, inner_version, local_inner, remote_inner, + old_out_sa) + + # Rekey + outer_family = net_test.GetAddressFamily(tunnel.version) + + # Create new SA + # Distinguish the new SAs with new SPIs. + new_out_sa = SaInfo(old_out_sa.spi + 1) + new_in_sa = SaInfo(old_in_sa.spi + 1) + + # Perform Rekey + tunnel.Rekey(outer_family, new_out_sa, new_in_sa) + + # Expect that the old SPI still works for inbound packets + self._CheckTunnelInput(tunnel, inner_version, local_inner, remote_inner, + old_in_sa) + + # Test both paths with new SPIs, expect outbound to use new SPI + self._CheckTunnelInput(tunnel, inner_version, local_inner, remote_inner, + new_in_sa) + self._CheckTunnelOutput(tunnel, inner_version, local_inner, remote_inner, + new_out_sa) + + # Delete old SAs + tunnel.DeleteOldSaInfo(outer_family, old_in_sa.spi, old_out_sa.spi) + + # Test both paths with new SPIs; should still work + self._CheckTunnelInput(tunnel, inner_version, local_inner, remote_inner, + new_in_sa) + self._CheckTunnelOutput(tunnel, inner_version, local_inner, remote_inner, + new_out_sa) + + # Expect failure upon trying to receive a packet with the deleted SPI + self._CheckTunnelInput(tunnel, inner_version, local_inner, remote_inner, + old_in_sa, True) + + def _TestTunnelRekey(self, inner_version, outer_version): + """Test packet input and output over a Virtual Tunnel Interface.""" + tunnel = self.randomTunnel(outer_version) + + try: + # Always use null_crypt, so we can check input and output separately + tunnel.TeardownXfrm() + tunnel.SetupXfrm(True) + + local_inner = tunnel.addrs[inner_version] + remote_inner = _GetRemoteInnerAddress(inner_version) + + self._CheckTunnelRekey(tunnel, inner_version, local_inner, remote_inner) + finally: + tunnel.TeardownXfrm() + tunnel.SetupXfrm(False) + @unittest.skipUnless(net_test.LINUX_VERSION >= (3, 18, 0), "VTI Unsupported") class XfrmVtiTest(XfrmTunnelBase): @@ -749,6 +900,9 @@ class XfrmVtiTest(XfrmTunnelBase): self._TestTunnel(inner_version, outer_version, self._CheckTunnelEncryptionWithIcmp, False) + def ParamTestVtiRekey(self, inner_version, outer_version): + self._TestTunnelRekey(inner_version, outer_version) + @unittest.skipUnless(HAVE_XFRM_INTERFACES, "XFRM interfaces unsupported") class XfrmInterfaceTest(XfrmTunnelBase): @@ -773,6 +927,9 @@ class XfrmInterfaceTest(XfrmTunnelBase): self._TestTunnel(inner_version, outer_version, self._CheckTunnelEncryptionWithIcmp, False) + def ParamTestXfrmIntfRekey(self, inner_version, outer_version): + self._TestTunnelRekey(inner_version, outer_version) + if __name__ == "__main__": InjectTests() -- cgit v1.2.3 From a05e9a297c0a70777ea30fe858cae567d203da9e Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Fri, 18 May 2018 16:49:58 -0700 Subject: Add tests for netfilter reject policies This patch adds tests to ensure that the local-ICMP reflection path works correctly. Bug: 79896641 Test: This, ran on walleye, UML kernels Change-Id: I1d3a5a2c0b90f240139ddd105cfc1e6969572a0a --- net/test/all_tests.py | 1 + net/test/nf_test.py | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100755 net/test/nf_test.py diff --git a/net/test/all_tests.py b/net/test/all_tests.py index 72d3c4e..bfba0e5 100755 --- a/net/test/all_tests.py +++ b/net/test/all_tests.py @@ -27,6 +27,7 @@ test_modules = [ 'leak_test', 'multinetwork_test', 'neighbour_test', + 'nf_test', 'pf_key_test', 'ping6_test', 'qtaguid_test', diff --git a/net/test/nf_test.py b/net/test/nf_test.py new file mode 100755 index 0000000..cd6c976 --- /dev/null +++ b/net/test/nf_test.py @@ -0,0 +1,86 @@ +#!/usr/bin/python +# +# Copyright 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +import errno +from socket import * + +import multinetwork_base +import net_test + +_TEST_IP4_ADDR = "192.0.2.1" +_TEST_IP6_ADDR = "2001:db8::" + + +# Regression tests for interactions between kernel networking and netfilter +# +# These tests were added to ensure that the lookup path for local-ICMP errors +# do not cause failures. Specifically, local-ICMP packets do not have a +# net_device in the skb, and has been known to trigger bugs in surrounding code. +class NetilterRejectTargetTest(multinetwork_base.MultiNetworkBaseTest): + + def setUp(self): + multinetwork_base.MultiNetworkBaseTest.setUp(self) + net_test.RunIptablesCommand(4, "-A OUTPUT -d " + _TEST_IP4_ADDR + " -j REJECT") + net_test.RunIptablesCommand(6, "-A OUTPUT -d " + _TEST_IP6_ADDR + " -j REJECT") + + def tearDown(self): + net_test.RunIptablesCommand(4, "-D OUTPUT -d " + _TEST_IP4_ADDR + " -j REJECT") + net_test.RunIptablesCommand(6, "-D OUTPUT -d " + _TEST_IP6_ADDR + " -j REJECT") + multinetwork_base.MultiNetworkBaseTest.tearDown(self) + + # Test a rejected TCP connect. The responding ICMP may not have skb->dev set. + # This tests the local-ICMP output-input path. + def CheckRejectedTcp(self, version, addr): + sock = net_test.TCPSocket(net_test.GetAddressFamily(version)) + netid = self.RandomNetid() + self.SelectInterface(sock, netid, "mark") + + # Expect this to fail with ICMP unreachable + try: + sock.connect((addr, 53)) + except IOError: + pass + + def testRejectTcp4(self): + self.CheckRejectedTcp(4, _TEST_IP4_ADDR) + + def testRejectTcp6(self): + self.CheckRejectedTcp(6, _TEST_IP6_ADDR) + + # Test a rejected UDP connect. The responding ICMP may not have skb->dev set. + # This tests the local-ICMP output-input path. + def CheckRejectedUdp(self, version, addr): + sock = net_test.UDPSocket(net_test.GetAddressFamily(version)) + netid = self.RandomNetid() + self.SelectInterface(sock, netid, "mark") + + # Expect this to fail with ICMP unreachable + try: + sock.sendto(net_test.UDP_PAYLOAD, (addr, 53)) + except IOError: + pass + + def testRejectUdp4(self): + self.CheckRejectedUdp(4, _TEST_IP4_ADDR) + + def testRejectUdp6(self): + self.CheckRejectedUdp(6, _TEST_IP6_ADDR) + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file -- cgit v1.2.3 From 202221f726983460c08bea36dceee4f1222d04f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Wed, 12 Sep 2018 14:38:06 -0700 Subject: net_test: fix sock_diag_test.py to handle ipv5 correctly [ipv5 is ipv4 mapped ipv6] af_inet6_socket.connect(('8.8.8.8', 53)) is bogus and should be af_inet6_socket.connect(('::ffff:8.8.8.8', 53)) instead Python's behaviour when passing an ipv4 literal to an ipv6 socket has always been notoriously stupid: no error, just treat as ::. Seems like newer python is even stupider: no error, just treat as garbage. Bug: 74373492 Change-Id: I7c36236cd34246a2fff63864f81463ba2627841c --- net/test/sock_diag_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/test/sock_diag_test.py b/net/test/sock_diag_test.py index 4b1d055..a561f31 100755 --- a/net/test/sock_diag_test.py +++ b/net/test/sock_diag_test.py @@ -891,7 +891,7 @@ class SockDestroyUdpTest(SockDiagBaseTest): family = {4: AF_INET, 5: AF_INET6, 6: AF_INET6}[version] s = net_test.UDPSocket(family) self.SelectInterface(s, random.choice(self.NETIDS), "mark") - addr = self.GetRemoteAddress(version) + addr = self.GetRemoteSocketAddress(version) # Check that reads on connected sockets are interrupted. s.connect((addr, 53)) -- cgit v1.2.3 From cff4420cec1870300337b29cea832948e53c1788 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Wed, 12 Sep 2018 16:28:15 -0700 Subject: Fix sysfs mount in net_test.sh. The new net_test.sh used by build_rootfs.sh used a broken filesystem type for sysfs. This meant that sysfs was not mounted. This did not actually break anything because sysfs does not appear to be used by the tests. Bug: 79694505 Test: run_net_test.sh all_tests.sh Change-Id: I781902d5561f5d7a9815cd886f30bf7d27bf996e Signed-off-by: Alistair Strachan --- net/test/rootfs/net_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/test/rootfs/net_test.sh b/net/test/rootfs/net_test.sh index 072a8a3..9c94d06 100755 --- a/net/test/rootfs/net_test.sh +++ b/net/test/rootfs/net_test.sh @@ -16,7 +16,7 @@ # mount -t proc none /proc -mount -t sys none /sys +mount -t sysfs none /sys mount -t tmpfs tmpfs /tmp mount -t tmpfs tmpfs /run -- cgit v1.2.3 From f9e22fba0a22afcafba358ba44ca6972c980f085 Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Mon, 13 Aug 2018 16:31:49 +0800 Subject: Check xfrm state to delete embryonic SA 3 VtsKernelNetTest test cases fail on a platform where an IPsec key manager is running. They are testSocketPolicyIPv4, testSocketPolicyIPv6 and testSocketPolicyMapped. These 3 cases fail because CreateNewSa returns EEXIST when test cases call CreateNewSa() to send netlink message of adding xfrm state info to kernel. Take testSocketPolicyIPv4 as an example, normal flow is as below. udp send dns query -> apply socket policy -> assertRaisesErrno(EAGAIN, s.sendto, ...) -> CreateNewSa() -> Send encrypted dns query When it calls sendto after applying socket policy, kernel will call xfrm_state_find() to check if policy and state match. If there is a xfrm netlink message listener, kernel will allocate xfrm state. Therefore, when CreateNewSa() tries to create a new xfrm state, kernel returns EEXIST. On a platform where an IPsec key manager is running, there is a daemon which listens to xfrm policy message. We suggest that we can delete embryonic SA before creating a new SA when there is a existing user space key manager. Without existing user space key manager, test case still creates a new SA. In both conditions, EAGAIN will be returned after calling sendto(). Bug: 112518266 Test: VtsKernelNetTest can pass Change-Id: Id461b6c06884ff3e3464cd80dcf091f7da28f418 --- net/test/xfrm_test.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/test/xfrm_test.py b/net/test/xfrm_test.py index 93c66f4..24f9edc 100755 --- a/net/test/xfrm_test.py +++ b/net/test/xfrm_test.py @@ -132,6 +132,14 @@ class XfrmFunctionalTest(xfrm_base.XfrmLazyTest): EAGAIN, s.sendto, net_test.UDP_PAYLOAD, (remotesockaddr, 53)) + # If there is a user space key manager, calling sendto() after applying the socket policy + # creates an SA whose state is XFRM_STATE_ACQ. So this just deletes it. + # If there is no user space key manager, deleting SA returns ESRCH as the error code. + try: + self.xfrm.DeleteSaInfo(self.GetRemoteAddress(xfrm_version), TEST_SPI, IPPROTO_ESP) + except IOError as e: + self.assertEquals(ESRCH, e.errno, "Unexpected error when deleting ACQ SA") + # Adding a matching SA causes the packet to go out encrypted. The SA's # SPI must match the one in our template, and the destination address must # match the packet's destination address (in tunnel mode, it has to match @@ -139,6 +147,7 @@ class XfrmFunctionalTest(xfrm_base.XfrmLazyTest): self.CreateNewSa( net_test.GetWildcardAddress(xfrm_version), self.GetRemoteAddress(xfrm_version), TEST_SPI, reqid, None) + s.sendto(net_test.UDP_PAYLOAD, (remotesockaddr, 53)) expected_length = xfrm_base.GetEspPacketLength(xfrm.XFRM_MODE_TRANSPORT, version, False, -- cgit v1.2.3 From 308c2a300aab6907e164b20f249f7b364c6b5298 Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Mon, 17 Sep 2018 13:33:08 -0700 Subject: Allow ROOTFS to use environment variables This patch allows ROOTFS to use environment variables, preventing the need for the full script to be changed in order to use qemu as the testing platform. This treats ROOTFS the same as ARCH and DEFCONFIG, both of which are used to select qemu vs uml. Bug: None Test: Runs Change-Id: I30bc244eb011bcac3c18324d8bbd0d7207123258 --- net/test/run_net_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index 4ae7705..8249767 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -69,7 +69,7 @@ DISABLE_OPTIONS="$DISABLE_OPTIONS SYSVIPC" NUMTAPINTERFACES=0 # The root filesystem disk image we'll use. -ROOTFS=net_test.rootfs.20150203 +ROOTFS=${ROOTFS:-net_test.rootfs.20150203} COMPRESSED_ROOTFS=$ROOTFS.xz URL=https://dl.google.com/dl/android/$COMPRESSED_ROOTFS -- cgit v1.2.3 From 74faec5d15ea0804e09c60506fc96a1712e427f7 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 13 Sep 2018 15:56:49 +0900 Subject: Document/enforce a bug in udp_dump_one. udp_dump_one passes arguments to __udp[46]_lib_lookup in the wrong order. Thus, to find a UDP socket from an inet_diag_req_v2, userspace must swap src and dst addresses and ports. Upstream has stated that this cannot be fixed any more for backwards compatibility reasons. Test: test passes on android-4.9 Change-Id: I98dfaa34608bb8291a21be4da03e1ad6b4039899 --- net/test/sock_diag_test.py | 52 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/net/test/sock_diag_test.py b/net/test/sock_diag_test.py index a561f31..b195fe3 100755 --- a/net/test/sock_diag_test.py +++ b/net/test/sock_diag_test.py @@ -206,10 +206,10 @@ class SockDiagTest(SockDiagBaseTest): self.sock_diag.GetSockInfo(diag_req) # No errors? Good. - def testFindsAllMySockets(self): + def CheckFindsAllMySockets(self, socktype, proto): """Tests that basic socket dumping works.""" - self.socketpairs = self._CreateLotsOfSockets(SOCK_STREAM) - sockets = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, NO_BYTECODE) + self.socketpairs = self._CreateLotsOfSockets(socktype) + sockets = self.sock_diag.DumpAllInetSockets(proto, NO_BYTECODE) self.assertGreaterEqual(len(sockets), NUM_SOCKETS) # Find the cookies for all of our sockets. @@ -239,9 +239,21 @@ class SockDiagTest(SockDiagBaseTest): # Check that we can find a diag_msg once we know the cookie. req = self.sock_diag.DiagReqFromSocket(sock) req.id.cookie = cookie + if proto == IPPROTO_UDP: + # Kernel bug: for UDP sockets, the order of arguments must be swapped. + # See testDemonstrateUdpGetSockIdBug. + req.id.sport, req.id.dport = req.id.dport, req.id.sport + req.id.src, req.id.dst = req.id.dst, req.id.src info = self.sock_diag.GetSockInfo(req) self.assertSockInfoMatchesSocket(sock, info) + def testFindsAllMySocketsTcp(self): + self.CheckFindsAllMySockets(SOCK_STREAM, IPPROTO_TCP) + + @unittest.skipUnless(HAVE_UDP_DIAG, "INET_UDP_DIAG not enabled") + def testFindsAllMySocketsUdp(self): + self.CheckFindsAllMySockets(SOCK_DGRAM, IPPROTO_UDP) + def testBytecodeCompilation(self): # pylint: disable=bad-whitespace instructions = [ @@ -379,6 +391,40 @@ class SockDiagTest(SockDiagBaseTest): self.CheckSocketCookie(AF_INET, "127.0.0.1") self.CheckSocketCookie(AF_INET6, "::1") + @unittest.skipUnless(HAVE_UDP_DIAG, "INET_UDP_DIAG not enabled") + def testDemonstrateUdpGetSockIdBug(self): + # TODO: this is because udp_dump_one mistakenly uses __udp[46]_lib_lookup + # by passing the source address as the source address argument. + # Unfortunately those functions are intended to match local sockets based + # on received packets, and the argument that ends up being compared with + # e.g., sk_daddr is actually saddr, not daddr. udp_diag_destroy does not + # have this bug. Upstream has confirmed that this will not be fixed: + # https://www.mail-archive.com/netdev@vger.kernel.org/msg248638.html + """Documents a bug: getting UDP sockets requires swapping src and dst.""" + for version in [4, 5, 6]: + family = net_test.GetAddressFamily(version) + s = socket(family, SOCK_DGRAM, 0) + self.SelectInterface(s, self.RandomNetid(), "mark") + s.connect((self.GetRemoteSocketAddress(version), 53)) + + # Create a fully-specified diag req from our socket, including cookie if + # we can get it. + req = self.sock_diag.DiagReqFromSocket(s) + if HAVE_SO_COOKIE_SUPPORT: + req.id.cookie = s.getsockopt(net_test.SOL_SOCKET, net_test.SO_COOKIE, 8) + else: + req.id.cookie = "\xff" * 16 # INET_DIAG_NOCOOKIE[2] + + # As is, this request does not find anything. + with self.assertRaisesErrno(ENOENT): + self.sock_diag.GetSockInfo(req) + + # But if we swap src and dst, the kernel finds our socket. + req.id.sport, req.id.dport = req.id.dport, req.id.sport + req.id.src, req.id.dst = req.id.dst, req.id.src + + self.assertSockInfoMatchesSocket(s, self.sock_diag.GetSockInfo(req)) + class SockDestroyTest(SockDiagBaseTest): """Tests that SOCK_DESTROY works correctly. -- cgit v1.2.3 From d3caaa0b9a85e3a6bcbba2ecf22e59da6c4b6cf9 Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Wed, 19 Sep 2018 14:40:20 -0700 Subject: Remove mutable default parameter in tunnel_test This change prevents mutable default parameters from being used in _GetNullAuthCryptTunnelModePkt to ensure subsequent calls do not use cached values. Bug: 116148058 Test: Passes on 4.9 kernels Change-Id: I09ec9d72f0e4180684531baa8653ed642e7550e6 --- net/test/xfrm_tunnel_test.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/test/xfrm_tunnel_test.py b/net/test/xfrm_tunnel_test.py index 778eb26..89df027 100755 --- a/net/test/xfrm_tunnel_test.py +++ b/net/test/xfrm_tunnel_test.py @@ -87,7 +87,10 @@ def _GetRemoteOuterAddress(version): def _GetNullAuthCryptTunnelModePkt(inner_version, src_inner, src_outer, src_port, dst_inner, dst_outer, - dst_port, spi, seq_num, ip_hdr_options={}): + dst_port, spi, seq_num, ip_hdr_options=None): + if ip_hdr_options is None: + ip_hdr_options = {} + ip_hdr_options.update({'src': src_inner, 'dst': dst_inner}) # Build and receive an ESP packet destined for the inner socket -- cgit v1.2.3 From 56237c9b64f54c2b978fb49975b18d7782f2fac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 9 Oct 2018 15:58:28 -0700 Subject: Add lspci & lsusb commands to stretch image. This adds pciutils & usbutils debs. Before: 252M net_test.rootfs.20181009 After: 254M net_test.rootfs.20181009 Change-Id: I1e12bb4eb1adfb847ab6cf0e950f574bd0b71676 --- net/test/rootfs/stretch.list | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/test/rootfs/stretch.list b/net/test/rootfs/stretch.list index e6c0177..fbeddde 100644 --- a/net/test/rootfs/stretch.list +++ b/net/test/rootfs/stretch.list @@ -17,6 +17,7 @@ netcat-openbsd netcat-traditional net-tools openssl +pciutils procps psmisc python @@ -27,5 +28,6 @@ tcpdump traceroute udev udhcpc +usbutils vim-tiny wget -- cgit v1.2.3 From 2cc987d9dd9ad877e6f6d59e714c1a864c978444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 9 Oct 2018 15:54:58 -0700 Subject: Enable virtio rng device for net tests on qemu/kvm. Before: root@(none):/host# dmesg | egrep -i 'virtio|random|rnd|rng' [ 0.000000] random: get_random_bytes called from start_kernel+0x94/0x445 with crng_init=0 [ 1.068452] random: fast init done [ 1.226737] random: all_tests.py: uninitialized urandom read (2500 bytes read) [ 1.297999] random: all_tests.py: uninitialized urandom read (2500 bytes read) [ 1.301358] random: all_tests.py: uninitialized urandom read (2500 bytes read) [ 51.829187] urandom_read: 1 callbacks suppressed [ 51.829189] random: all_tests.py: uninitialized urandom read (8 bytes read) [ 51.833191] random: all_tests.py: uninitialized urandom read (8 bytes read) [ 51.836113] random: all_tests.py: uninitialized urandom read (8 bytes read) [ 78.285661] random: crng init done [ 78.287299] random: 3 urandom warning(s) missed due to ratelimiting After: root@(none):/host# dmesg | egrep -i 'virtio|random|rnd|rng' [ 0.000000] random: get_random_bytes called from start_kernel+0x94/0x445 with crng_init=0 [ 1.018666] random: fast init done [ 1.020632] random: crng init done root@(none):/host# lspci 00:00.0 Host bridge: Intel Corporation 440FX - 82441FX PMC [Natoma] (rev 02) 00:01.0 ISA bridge: Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II] 00:01.1 IDE interface: Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II] 00:01.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 03) 00:02.0 Unclassified device [00ff]: Red Hat, Inc Virtio RNG 00:03.0 Unclassified device [0002]: Red Hat, Inc Virtio filesystem 00:04.0 SCSI storage controller: Red Hat, Inc Virtio block device (note the new Virtio RNG device) Change-Id: Ifea0c261af1209f9307adb150cf32f7bc92aba74 --- net/test/run_net_test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index 8249767..54e64a4 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -300,6 +300,7 @@ else -kernel $KERNEL_BINARY \ -no-user-config -nodefaults -no-reboot -display none \ -machine pc,accel=kvm -cpu host -smp 4,sockets=4,cores=1,threads=1 \ + -device virtio-rng-pci \ -chardev file,id=exitcode,path=exitcode \ -device isa-serial,chardev=exitcode \ -fsdev local,security_model=mapped-xattr,id=fsdev0,fmode=0644,dmode=0755,path=$SCRIPT_DIR \ -- cgit v1.2.3 From 75b9e5ee69dc85cd9938edef211d3f3227f908c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Fri, 12 Oct 2018 14:57:55 -0700 Subject: run_net_test.sh: fix UML --readonly flag Without this you end up with r=... instead of ubdar=... and: Failed to initialize ubd device 0 :Couldn't determine size of device's file VFS: Cannot open root device "98:0" or unknown-block(98,0): error -6 Please append a correct "root=" boot option; here are the available partitions: Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(98,0) Aborted Change-Id: If0d53a8560b8dca8908f30794550b7a1360c85a7 --- net/test/run_net_test.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index 54e64a4..1768832 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -263,7 +263,7 @@ if [ "$ARCH" == "um" ]; then if ((nowrite == 0)); then blockdevice=ubda else - blockdevice="${blockdevice}r" + blockdevice=ubdar fi exec $KERNEL_BINARY >&2 umid=net_test mem=512M \ @@ -288,9 +288,10 @@ else cmdline="$cmdline net_test_exitcode=/dev/ttyS1" # Map the --readonly flag to a QEMU block device flag - blockdevice= if ((nowrite > 0)); then blockdevice=",readonly" + else + blockdevice= fi blockdevice="-drive file=$SCRIPT_DIR/$ROOTFS,format=raw,if=none,id=drive-virtio-disk0$blockdevice" blockdevice="$blockdevice -device virtio-blk-pci,drive=drive-virtio-disk0" -- cgit v1.2.3 From a9c03cc882dc2b9a9a00fc2f4bf11bead3b5a306 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 15 Oct 2018 13:01:31 +0900 Subject: Filter neighbour dumps by interface. This might speed DumpNeighbours (and thus make it more likely for neighbour_test.py to pass) if there are many ND cache entries on the system when the test is run. Bug: 116671340 Test: neighbour_test.py passes on android-4.14 Change-Id: I5baa006d838ec8fd3629841da52e00cfbcd02008 --- net/test/iproute.py | 6 ++++-- net/test/neighbour_test.py | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/net/test/iproute.py b/net/test/iproute.py index 77c41b2..8376eb6 100644 --- a/net/test/iproute.py +++ b/net/test/iproute.py @@ -147,6 +147,7 @@ NDA_DST = 1 NDA_LLADDR = 2 NDA_CACHEINFO = 3 NDA_PROBES = 4 +NDA_IFINDEX = 8 # Neighbour cache entry states. NUD_PERMANENT = 0x80 @@ -639,9 +640,10 @@ class IPRoute(netlink.NetlinkSocket): self._Neighbour(version, True, addr, lladdr, dev, state, flags=netlink.NLM_F_REPLACE) - def DumpNeighbours(self, version): + def DumpNeighbours(self, version, ifindex): ndmsg = NdMsg((self._AddressFamily(version), 0, 0, 0, 0)) - return self._Dump(RTM_GETNEIGH, ndmsg, NdMsg, "") + attrs = self._NlAttrU32(NDA_IFINDEX, ifindex) if ifindex else "" + return self._Dump(RTM_GETNEIGH, ndmsg, NdMsg, attrs) def ParseNeighbourMessage(self, msg): msg, _ = self._ParseNLMsg(msg, NdMsg) diff --git a/net/test/neighbour_test.py b/net/test/neighbour_test.py index 2caba44..caf2e6e 100755 --- a/net/test/neighbour_test.py +++ b/net/test/neighbour_test.py @@ -87,14 +87,14 @@ class NeighbourTest(multinetwork_base.MultiNetworkBaseTest): self.netid = random.choice(self.tuns.keys()) self.ifindex = self.ifindices[self.netid] - def GetNeighbour(self, addr): + def GetNeighbour(self, addr, ifindex): version = csocket.AddressVersion(addr) - for msg, args in self.iproute.DumpNeighbours(version): + for msg, args in self.iproute.DumpNeighbours(version, ifindex): if args["NDA_DST"] == addr: return msg, args def GetNdEntry(self, addr): - return self.GetNeighbour(addr) + return self.GetNeighbour(addr, self.ifindex) def CheckNoNdEvents(self): self.assertRaisesErrno(errno.EAGAIN, self.sock.recvfrom, 4096, MSG_PEEK) -- cgit v1.2.3 From ac82e9246667ae9efde19792637d6703af150723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Sun, 14 Oct 2018 20:10:47 -0700 Subject: run_net_test: add 'no_test' test to allow simply getting into the VM with minimal fuss Change-Id: I4ad75878007c4ed065a68c6aed67bb491047336c --- net/test/no_test | 1 + 1 file changed, 1 insertion(+) create mode 100755 net/test/no_test diff --git a/net/test/no_test b/net/test/no_test new file mode 100755 index 0000000..b23e556 --- /dev/null +++ b/net/test/no_test @@ -0,0 +1 @@ +#!/bin/true -- cgit v1.2.3 From 43115d51eebe94a360105144e2baee1f2f508255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Fri, 12 Oct 2018 15:02:07 -0700 Subject: run_net_test.sh: switch to readonly by default And also add support for --readwrite --rw --ro flags. Read only by default is better cause it allows multiple parallel invocations making use of the same image. Read write access to the file system is only occasionally useful, and can always be requested by hand. While I'm at it, change some occurences of [ to [[ to honour Google's shell style guide: https://google.github.io/styleguide/shell.xml. Change-Id: I94ac0d9d05470bfd5e62465454c398174d644e5b --- net/test/parallel_tests.sh | 2 +- net/test/run_net_test.sh | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/net/test/parallel_tests.sh b/net/test/parallel_tests.sh index 93a43c8..eb67421 100755 --- a/net/test/parallel_tests.sh +++ b/net/test/parallel_tests.sh @@ -15,7 +15,7 @@ function runtests() { local test=$3 local j=0 while ((j < runs)); do - $DIR/run_net_test.sh --readonly --builder --nobuild $test \ + $DIR/run_net_test.sh --builder --nobuild $test \ > /dev/null 2> $RESULTSDIR/results.$worker.$j j=$((j + 1)) echo -n "." >&2 diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index 1768832..384a097 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -89,22 +89,25 @@ consolemode= netconfig= testmode= cmdline= -nowrite=0 +nowrite=1 nobuild=0 norun=0 -while [ -n "$1" ]; do - if [ "$1" = "--builder" ]; then +while [[ -n "$1" ]]; do + if [[ "$1" == "--builder" ]]; then consolemode="con=null,fd:1" testmode=builder shift - elif [ "$1" == "--readonly" ]; then + elif [[ "$1" == "--readwrite" || "$1" == "--rw" ]]; then + nowrite=0 + shift + elif [[ "$1" == "--readonly" || "$1" == "--ro" ]]; then nowrite=1 shift - elif [ "$1" == "--nobuild" ]; then + elif [[ "$1" == "--nobuild" ]]; then nobuild=1 shift - elif [ "$1" == "--norun" ]; then + elif [[ "$1" == "--norun" ]]; then norun=1 shift else @@ -138,7 +141,7 @@ function isBuildOnly() { if ! isRunningTest && ! isBuildOnly; then echo "Usage:" >&2 - echo " $0 [--builder] [--readonly] [--nobuild] " >&2 + echo " $0 [--builder] [--readonly|--ro|--readwrite|--rw] [--nobuild] " >&2 echo " $0 --norun" >&2 exit 1 fi -- cgit v1.2.3 From 99cd00d2063ce288f386f6ccba090645d99de5b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Sun, 14 Oct 2018 20:03:23 -0700 Subject: run_net_test: handle UML's tendency to leave stdout in non-blocking mode which then results in crazy weird build failures and other weirdness as a result of writes to stdout rarely returning EAGAIN. Change-Id: I8f36b46b8e81711824e173925601eb7cd58168d1 --- net/test/run_net_test.sh | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index 384a097..4c089f9 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -1,5 +1,16 @@ #!/bin/bash +# Builds mysteriously fail if stdout is non-blocking. +fixup_ptys() { + python << 'EOF' +import fcntl, os, sys +fd = sys.stdout.fileno() +flags = fcntl.fcntl(fd, fcntl.F_GETFL) +flags &= ~(fcntl.FASYNC | os.O_NONBLOCK | os.O_APPEND) +fcntl.fcntl(fd, fcntl.F_SETFL, flags) +EOF +} + # Common kernel options OPTIONS=" DEBUG_SPINLOCK DEBUG_ATOMIC_SLEEP DEBUG_MUTEXES DEBUG_RT_MUTEXES" OPTIONS="$OPTIONS DEVTMPFS DEVTMPFS_MOUNT FHANDLE" @@ -269,8 +280,9 @@ if [ "$ARCH" == "um" ]; then blockdevice=ubdar fi - exec $KERNEL_BINARY >&2 umid=net_test mem=512M \ + $KERNEL_BINARY >&2 umid=net_test mem=512M \ $blockdevice=$SCRIPT_DIR/$ROOTFS $netconfig $consolemode $cmdline + exitcode=$? else # We boot into the filesystem image directly in all cases cmdline="$cmdline root=/dev/vda" @@ -312,5 +324,8 @@ else $blockdevice $netconfig -serial stdio -append "$cmdline" [ -s exitcode ] && exitcode=`cat exitcode | tr -d '\r'` || exitcode=1 rm -f exitcode - exit $exitcode fi + +# UML reliably screws up the ptys, QEMU probably can as well... +fixup_ptys +exit "${exitcode}" -- cgit v1.2.3 From e29f3e6f06afe653052dddfb3dfcbaf4da0d28de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Sun, 14 Oct 2018 20:57:52 -0700 Subject: run_net_test: UML - fix insufficient entropy problems This resolves IPv6 ping problems on recent (for example 4.17) kernels. Change-Id: I5310bad2f3148d1195367aade10b9761995134a4 --- net/test/net_test.sh | 17 +++++++++++++++++ net/test/run_net_test.sh | 11 +++++++++++ 2 files changed, 28 insertions(+) diff --git a/net/test/net_test.sh b/net/test/net_test.sh index e2ed6b1..0c3a3ad 100755 --- a/net/test/net_test.sh +++ b/net/test/net_test.sh @@ -1,4 +1,21 @@ #!/bin/bash +if [[ -n "${entropy}" ]]; then + echo "adding entropy from hex string [${entropy}]" 1>&2 + + # In kernel/include/uapi/linux/random.h RNDADDENTROPY is defined as + # _IOW('R', 0x03, int[2]) =(R is 0x52)= 0x40085203 = 1074287107 + python 3>/dev/random < Date: Mon, 15 Oct 2018 13:01:31 +0900 Subject: Filter neighbour dumps by interface. This might speed DumpNeighbours (and thus make it more likely for neighbour_test.py to pass) if there are many ND cache entries on the system when the test is run. Bug: 116671340 Test: neighbour_test.py passes on android-4.14 Change-Id: I5baa006d838ec8fd3629841da52e00cfbcd02008 Merged-in: I5baa006d838ec8fd3629841da52e00cfbcd02008 (cherry picked from commit a9c03cc882dc2b9a9a00fc2f4bf11bead3b5a306) --- net/test/iproute.py | 6 ++++-- net/test/neighbour_test.py | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/net/test/iproute.py b/net/test/iproute.py index 9cfafc6..a3310a2 100644 --- a/net/test/iproute.py +++ b/net/test/iproute.py @@ -147,6 +147,7 @@ NDA_DST = 1 NDA_LLADDR = 2 NDA_CACHEINFO = 3 NDA_PROBES = 4 +NDA_IFINDEX = 8 # Neighbour cache entry states. NUD_PERMANENT = 0x80 @@ -635,9 +636,10 @@ class IPRoute(netlink.NetlinkSocket): self._Neighbour(version, True, addr, lladdr, dev, state, flags=netlink.NLM_F_REPLACE) - def DumpNeighbours(self, version): + def DumpNeighbours(self, version, ifindex): ndmsg = NdMsg((self._AddressFamily(version), 0, 0, 0, 0)) - return self._Dump(RTM_GETNEIGH, ndmsg, NdMsg, "") + attrs = self._NlAttrU32(NDA_IFINDEX, ifindex) if ifindex else "" + return self._Dump(RTM_GETNEIGH, ndmsg, NdMsg, attrs) def ParseNeighbourMessage(self, msg): msg, _ = self._ParseNLMsg(msg, NdMsg) diff --git a/net/test/neighbour_test.py b/net/test/neighbour_test.py index 2caba44..caf2e6e 100755 --- a/net/test/neighbour_test.py +++ b/net/test/neighbour_test.py @@ -87,14 +87,14 @@ class NeighbourTest(multinetwork_base.MultiNetworkBaseTest): self.netid = random.choice(self.tuns.keys()) self.ifindex = self.ifindices[self.netid] - def GetNeighbour(self, addr): + def GetNeighbour(self, addr, ifindex): version = csocket.AddressVersion(addr) - for msg, args in self.iproute.DumpNeighbours(version): + for msg, args in self.iproute.DumpNeighbours(version, ifindex): if args["NDA_DST"] == addr: return msg, args def GetNdEntry(self, addr): - return self.GetNeighbour(addr) + return self.GetNeighbour(addr, self.ifindex) def CheckNoNdEvents(self): self.assertRaisesErrno(errno.EAGAIN, self.sock.recvfrom, 4096, MSG_PEEK) -- cgit v1.2.3 From 7c68a3c3c4593699167b6f3ef43a1eb63526fec6 Mon Sep 17 00:00:00 2001 From: Xu YiPing Date: Fri, 19 Oct 2018 17:55:06 +0800 Subject: Improve xfrm net test In kernel_net_test/xfrm_tunnel_test.XfrmVtiTest The testcases write packets to the tunnel, then check the count of rx/tx packets number on the interface. However, the packets are processed *asynchronously* in kernel. So, receive the packets before check the rx count Test: run vts/kernel_net_tests Bug: 117532237 Signed-off-by: Xu YiPing --- net/test/xfrm_tunnel_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/test/xfrm_tunnel_test.py b/net/test/xfrm_tunnel_test.py index 89df027..89b7c58 100755 --- a/net/test/xfrm_tunnel_test.py +++ b/net/test/xfrm_tunnel_test.py @@ -648,8 +648,8 @@ class XfrmTunnelBase(xfrm_base.XfrmBaseTest): self.assertRaisesErrno(EAGAIN, read_sock.recv, 4096) else: # Verify that the packet data and src are correct - self.assertReceivedPacket(tunnel, sa_info) data, src = read_sock.recvfrom(4096) + self.assertReceivedPacket(tunnel, sa_info) self.assertEquals(net_test.UDP_PAYLOAD, data) self.assertEquals((remote_inner, _TEST_REMOTE_PORT), src[:2]) -- cgit v1.2.3 From 7d4de3b1fc815f11c439d89639a39fb4db90057f Mon Sep 17 00:00:00 2001 From: Xu YiPing Date: Fri, 19 Oct 2018 17:55:06 +0800 Subject: Improve xfrm net test In kernel_net_test/xfrm_tunnel_test.XfrmVtiTest The testcases write packets to the tunnel, then check the count of rx/tx packets number on the interface. However, the packets are processed *asynchronously* in kernel. So, receive the packets before check the rx count Backport of I3b54c7fd401fc0f292269c1554d4ddc4128adf6d. Test: run vts/kernel_net_tests Bug: 117532237 Signed-off-by: Xu YiPing Change-Id: Id55c60c31976de97355456837216429737d79e7a Merged-In: I3b54c7fd401fc0f292269c1554d4ddc4128adf6d --- net/test/xfrm_tunnel_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/test/xfrm_tunnel_test.py b/net/test/xfrm_tunnel_test.py index ac340d9..77cc8b3 100755 --- a/net/test/xfrm_tunnel_test.py +++ b/net/test/xfrm_tunnel_test.py @@ -377,10 +377,10 @@ class XfrmVtiTest(xfrm_base.XfrmBaseTest): # underlying network. pkt = TunTwister.TwistPacket(pkt) self.ReceivePacketOn(vti.underlying_netid, pkt) - self.assertReceivedPacket(vti) # Receive the decrypted packet on the dest port number. read_packet = read_sock.recv(4096) self.assertEquals(read_packet, net_test.UDP_PAYLOAD) + self.assertReceivedPacket(vti) finally: # Unwind the switcheroo self._SwapInterfaceAddress(vti.iface, new_addr=local, old_addr=remote) -- cgit v1.2.3 From 34089b083e35d07023289e0aea5ea6351bbb0806 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Thu, 25 Oct 2018 13:30:21 -0700 Subject: build_rootfs.sh: Add support for arm64 Support building 'foreign' architecture images for wheezy and stretch. Because the other control files were already architecture agnostic, this change is very simple. Bug: 118446450 Test: ./build_rootfs.sh -a arm64 Change-Id: Ib62d1ed319224dd3d4861172fed998bb42ab7280 Signed-off-by: Alistair Strachan --- net/test/build_rootfs.sh | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/net/test/build_rootfs.sh b/net/test/build_rootfs.sh index e0788c1..ce09da1 100755 --- a/net/test/build_rootfs.sh +++ b/net/test/build_rootfs.sh @@ -20,14 +20,17 @@ set -e SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P) usage() { - echo "usage: $0 [-h] [-s wheezy|stretch] [-n net_test.rootfs.`date +%Y%m%d`]" + echo -n "usage: $0 [-h] [-s wheezy|stretch] [-a amd64|arm64] " + echo "[-m http://mirror/debian] [-n net_test.rootfs.`date +%Y%m%d`]" exit 1 } -name=net_test.rootfs.`date +%Y%m%d` +mirror=http://ftp.debian.org/debian +debootstrap=debootstrap suite=stretch +arch=amd64 -while getopts ":hs:n:" opt; do +while getopts ":hs:a:m:n:" opt; do case $opt in h) usage @@ -39,6 +42,16 @@ while getopts ":hs:n:" opt; do fi suite=$OPTARG ;; + a) + if [ "$OPTARG" != "amd64" -a "$OPTARG" != "arm64" ]; then + echo "Invalid arch: $OPTARG" >&2 + usage + fi + arch=$OPTARG + ;; + m) + mirror=$OPTARG + ;; n) name=$OPTARG ;; @@ -53,6 +66,13 @@ while getopts ":hs:n:" opt; do esac done +name=net_test.rootfs.$arch.`date +%Y%m%d` + +# Switch to qemu-debootstrap for incompatible architectures +if [ "$arch" = "arm64" ]; then + debootstrap=qemu-debootstrap +fi + # Sometimes it isn't obvious when the script fails failure() { echo "Filesystem generation process failed." >&2 @@ -72,8 +92,8 @@ trap workdir_remove EXIT # Run the debootstrap first cd $workdir -sudo debootstrap --arch=amd64 --variant=minbase --include=$packages \ - $suite . http://ftp.debian.org/debian +sudo $debootstrap --arch=$arch --variant=minbase --include=$packages \ + $suite . $mirror # Workarounds for bugs in the debootstrap suite scripts for mount in `cat /proc/mounts | cut -d' ' -f2 | grep -e ^$workdir`; do echo "Unmounting mountpoint $mount.." >&2 @@ -116,4 +136,4 @@ sudo cp -a $workdir/* $mount sudo dd if=/dev/zero of=$mount/sparse bs=1M 2>/dev/null || true sudo rm -f $mount/sparse -echo "Debian $suite filesystem generated at '$name'." +echo "Debian $suite for $arch filesystem generated at '$name'." -- cgit v1.2.3 From 9ea6092b527f9a490bb7d96bcb729a6dc06eb19c Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Thu, 25 Oct 2018 13:36:34 -0700 Subject: run_net_test.sh: Add support for arm64 Make the accommodations necessary for arm64 support: - The compressed kernel image is called something different; - Switched from isa-serial to pci-serial, as the ARM virt model has no support for the PC ISA bus, and it doesn't matter for x86_64; - Enabled hardware accelerated virtualization only on x86_64. To test, make sure you have the Android clang and gcc toolchains pathed for AArch64. Then, run something like the following: $ CROSS_COMPILE=aarch64-linux-androidkernel- ARCH=arm64 \ DEFCONFIG=cuttlefish_defconfig ROOTFS=net_test.rootfs.arm64.20181025 \ /path/to/run_net_test.sh all_tests.sh All of the changes are keyed off of ARCH=arm64, to minimize the impact this change has on ARCH=um or ARCH=x86_64. Bug: 118446450 Change-Id: Id52c4eb061d6da4d3ec3485cd9ca62ff8ef028c2 Signed-off-by: Alistair Strachan --- net/test/run_net_test.sh | 50 +++++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index 1b5c9f6..e82cba8 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -57,6 +57,7 @@ OPTIONS="$OPTIONS BLK_DEV_UBD HOSTFS" # QEMU specific options OPTIONS="$OPTIONS VIRTIO VIRTIO_PCI VIRTIO_BLK NET_9P NET_9P_VIRTIO 9P_FS" +OPTIONS="$OPTIONS SERIAL_8250 SERIAL_8250_PCI" # Obsolete options present at some time in Android kernels OPTIONS="$OPTIONS IP_NF_TARGET_REJECT_SKERR IP6_NF_TARGET_REJECT_SKERR" @@ -209,9 +210,10 @@ else # Set default KERNEL_BINARY location if it was not provided. if [ "$ARCH" == "um" ]; then KERNEL_BINARY=./linux - else - # Assume x86_64 bzImage for now + elif [ "$ARCH" == "i386" -o "$ARCH" == "x86_64" -o "$ARCH" == "x86" ]; then KERNEL_BINARY=./arch/x86/boot/bzImage + elif [ "$ARCH" == "arm64" ]; then + KERNEL_BINARY=./arch/arm64/boot/Image.gz fi fi @@ -301,18 +303,6 @@ else # The path is stripped by the 9p export; we don't need SCRIPT_DIR cmdline="$cmdline net_test=/host/$test" - # QEMU has no way to modify its exitcode; simulate it with a serial port. - # - # Choose to do it this way over writing a file to /host, because QEMU will - # initialize the 'exitcode' file for us, it avoids unnecessary writes to the - # host filesystem (which is normally not written to) and it allows us to - # communicate an exit code back in cases we do not have /host mounted. - # - # The assignment of 'ttyS1' here is magical -- we know 'ttyS0' will be our - # serial port from the hard-coded '-serial stdio' flag below, and so this - # second serial port will be 'ttyS1'. - cmdline="$cmdline net_test_exitcode=/dev/ttyS1" - # Map the --readonly flag to a QEMU block device flag if ((nowrite > 0)); then blockdevice=",readonly" @@ -322,14 +312,38 @@ else blockdevice="-drive file=$SCRIPT_DIR/$ROOTFS,format=raw,if=none,id=drive-virtio-disk0$blockdevice" blockdevice="$blockdevice -device virtio-blk-pci,drive=drive-virtio-disk0" - # Assume x86_64 PC emulation for now - qemu-system-x86_64 >&2 -name net_test -m 512 \ + # QEMU has no way to modify its exitcode; simulate it with a serial port. + # + # Choose to do it this way over writing a file to /host, because QEMU will + # initialize the 'exitcode' file for us, it avoids unnecessary writes to the + # host filesystem (which is normally not written to) and it allows us to + # communicate an exit code back in cases we do not have /host mounted. + # + if [ "$ARCH" == "i386" -o "$ARCH" == "x86_64" -o "$ARCH" == "x86" ]; then + # Assume we have hardware-accelerated virtualization support for amd64 + qemu="qemu-system-x86_64 -machine pc,accel=kvm -cpu host" + + # The assignment of 'ttyS1' here is magical -- we know 'ttyS0' will be our + # serial port from the hard-coded '-serial stdio' flag below, and so this + # second serial port will be 'ttyS1'. + cmdline="$cmdline net_test_exitcode=/dev/ttyS1" + elif [ "$ARCH" == "arm64" ]; then + # This uses a software model CPU, based on cortex-a57 + qemu="qemu-system-aarch64 -machine virt -cpu cortex-a57" + + # The kernel will print messages via a virtual ARM serial port (ttyAMA0), + # but for command line consistency with x86, we put the exitcode serial + # port on the PCI bus, and it will be the only one. + cmdline="$cmdline net_test_exitcode=/dev/ttyS0" + fi + + $qemu >&2 -name net_test -m 512 \ -kernel $KERNEL_BINARY \ -no-user-config -nodefaults -no-reboot -display none \ - -machine pc,accel=kvm -cpu host -smp 4,sockets=4,cores=1,threads=1 \ + -smp 4,sockets=4,cores=1,threads=1 \ -device virtio-rng-pci \ -chardev file,id=exitcode,path=exitcode \ - -device isa-serial,chardev=exitcode \ + -device pci-serial,chardev=exitcode \ -fsdev local,security_model=mapped-xattr,id=fsdev0,fmode=0644,dmode=0755,path=$SCRIPT_DIR \ -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=host \ $blockdevice $netconfig -serial stdio -append "$cmdline" -- cgit v1.2.3 From e369f75633738ec5341c4044e0229590d15acacf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Mon, 29 Oct 2018 15:20:23 -0700 Subject: run_net_test - further boost UML entropy Per: https://android-build.googleplex.com/builds/submitted/5097760/kernel_test/latest/view/logs/build.log we appear to still not have quite enough: ... random: fast init done adding entropy from hex string [fdf7357dcff34af28ec2ef7907fda944a9adbbd62e6d4cc4a4f36b0771bc1d75] ... ##### Found 23 tests. ##### ./csocket_test.py (1/23) random: csocket_test.py: uninitialized urandom read (16 bytes read) ... ##### ./bpf_test.py (10/23) ... random: bpf_test.py: uninitialized urandom read (16 bytes read) random: crng init done (although I cannot reproduce it on my desktop) Change-Id: I90a4d9f7a3433049a3a22f60ddb32f2875e250e4 --- net/test/run_net_test.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index e82cba8..028ed85 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -280,10 +280,10 @@ if [ "$ARCH" == "um" ]; then # (which is the amount of hex in a single random UUID) provided to it on # the kernel cmdline. # - # We'll pass in 256 bits just to be safe, ie. a random 64 hex char seed. - # We do this by getting *two* random UUIDs and concatenating their hex - # digits into an even length hex encoded string. - entropy="$(cat /proc/sys/kernel/random{/,/}uuid | tr -d '\n-')" + # We'll pass in 384 bits just to be safe, ie. a random 96 hex char seed. + # We do this by getting *triple* random UUIDs and concatenating their hex + # digits into an *even* length hex encoded string. + entropy="$(cat /proc/sys/kernel/random{/,/,/}uuid | tr -d '\n-')" cmdline="${cmdline} entropy=${entropy}" # Map the --readonly flag to UML block device names -- cgit v1.2.3 From 59b602c67c4426f4380b4b44dbf8f8ecade025d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Mon, 29 Oct 2018 17:38:02 -0700 Subject: net/test/OWNERS: passing the torch from ek@ to maze@ Change-Id: I7758590d0da24729d2d0eb205e97405b75fd1d30 --- net/test/OWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/test/OWNERS b/net/test/OWNERS index f002a84..cbbfa70 100644 --- a/net/test/OWNERS +++ b/net/test/OWNERS @@ -1,2 +1,2 @@ -ek@google.com lorenzo@google.com +maze@google.com -- cgit v1.2.3 From c2d677ff3214bb8a9e60856042d7a30ebfeabcca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Mon, 29 Oct 2018 14:43:15 -0700 Subject: net_test - extra debugging for ReadProcNetSocket() regexp match failures See: https://android-build.googleplex.com/builds/submitted/5097760/kernel_test/latest/view/logs/build.log ie.: ##### ./forwarding_test.py (5/23) E. ====================================================================== ERROR: testForwardingCrashTcp (__main__.ForwardingTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "./forwarding_test.py", line 169, in testForwardingCrashTcp self.CheckForwardingByProto(IPPROTO_TCP) File "./forwarding_test.py", line 161, in CheckForwardingByProto self.CheckForwardingHandlerByProto(proto, netid, iface1, iface2) File "./forwarding_test.py", line 150, in CheckForwardingHandlerByProto self.CheckForwardingCrashTcp(netid, iif, oif) File "./forwarding_test.py", line 124, in CheckForwardingCrashTcp sockets = self.ReadProcNetSocket("tcp6") File "/host/usr/local/google/buildbot/src/partner-android/common-android-4.19/kernel/tests/net/test/net_test.py", line 462, in ReadProcNetSocket _, _, uid, _, _, refcnt, _, extra) = regexp.match(line).groups() AttributeError: 'NoneType' object has no attribute 'groups' ---------------------------------------------------------------------- Ran 2 tests in 0.452s FAILED (errors=1) E. ...(and then it reruns and promptly fails again in the same way)... './forwarding_test.py' failed more than once, giving up ##### I've had no luck reproducing the failure locally... But I'm guessing one of the 8 hex digit regexps is not matching due to printing out a negative 64 bit integer (which requires 16 hex digits). I'm intentionally leaving this in a way it can still fail, so we get to the bottom of this and actually fix the regexps. (while we're at it remove a spurious second | operator) Bug: 118651133 Change-Id: I6fbdc563cedbba3648d74b6bea1911b94086d0f3 --- net/test/net_test.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/test/net_test.py b/net/test/net_test.py index 6b19f54..035ba60 100755 --- a/net/test/net_test.py +++ b/net/test/net_test.py @@ -433,7 +433,7 @@ class NetworkTest(unittest.TestCase): if protocol.startswith("tcp"): # Real sockets have 5 extra numbers, timewait sockets have none. - end_regexp = "(| +[0-9]+ [0-9]+ [0-9]+ [0-9]+ -?[0-9]+|)$" + end_regexp = "(| +[0-9]+ [0-9]+ [0-9]+ [0-9]+ -?[0-9]+)$" elif re.match("icmp|udp|raw", protocol): # Drops. end_regexp = " +([0-9]+) *$" @@ -458,8 +458,11 @@ class NetworkTest(unittest.TestCase): # TODO: consider returning a dict or namedtuple instead. out = [] for line in lines: + m = regexp.match(line) + if m is None: + raise ValueError("Failed match on [%s]" % line) (_, src, dst, state, mem, - _, _, uid, _, _, refcnt, _, extra) = regexp.match(line).groups() + _, _, uid, _, _, refcnt, _, extra) = m.groups() out.append([src, dst, state, mem, uid, refcnt, extra]) return out -- cgit v1.2.3 From 09a7c89b37d0f205fe7301fc07cdf2c31a58bad2 Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Wed, 24 Oct 2018 16:10:32 -0700 Subject: Test to verify cgroup socket filter Add a base test for cgroup socket filter to check the cgroup socket eBPF program can actually block socket creation for INET socket. This feature will be used in 4.14 and above kernel to replace paranoid network. Test: ./bpf_test.py Bug: 111560739 Change-Id: I10f5c0c6847ec033cf757b8ce9dfa1a6b80c50fb --- net/test/bpf.py | 1 + net/test/bpf_test.py | 46 +++++++++++++++++++++++++++++++++++++++++++++- net/test/net_test.py | 7 +++---- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/net/test/bpf.py b/net/test/bpf.py index aa50f3e..43502bd 100755 --- a/net/test/bpf.py +++ b/net/test/bpf.py @@ -152,6 +152,7 @@ BPF_FUNC_unspec = 0 BPF_FUNC_map_lookup_elem = 1 BPF_FUNC_map_update_elem = 2 BPF_FUNC_map_delete_elem = 3 +BPF_FUNC_get_current_uid_gid = 15 BPF_FUNC_get_socket_cookie = 46 BPF_FUNC_get_socket_uid = 47 diff --git a/net/test/bpf_test.py b/net/test/bpf_test.py index 270243e..823dcbe 100755 --- a/net/test/bpf_test.py +++ b/net/test/bpf_test.py @@ -30,10 +30,12 @@ import sock_diag libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True) HAVE_EBPF_ACCOUNTING = net_test.LINUX_VERSION >= (4, 9, 0) +HAVE_EBPF_SOCKET = net_test.LINUX_VERSION >= (4, 14, 0) KEY_SIZE = 8 VALUE_SIZE = 4 TOTAL_ENTRIES = 20 TEST_UID = 54321 +TEST_GID = 12345 # Offset to store the map key in stack register REG10 key_offset = -8 # Offset to store the map value in stack register REG10 @@ -350,6 +352,10 @@ class BpfCgroupTest(net_test.NetworkTest): BpfProgDetach(self._cg_fd, BPF_CGROUP_INET_INGRESS) except socket.error: pass + try: + BpfProgDetach(self._cg_fd, BPF_CGROUP_INET_SOCK_CREATE) + except socket.error: + pass def testCgroupBpfAttach(self): self.prog_fd = BpfProgLoad(BPF_PROG_TYPE_CGROUP_SKB, INS_BPF_EXIT_BLOCK) @@ -392,10 +398,48 @@ class BpfCgroupTest(net_test.NetworkTest): self.assertRaisesErrno(errno.ENOENT, LookupMap, self.map_fd, uid) SocketUDPLoopBack(packet_count, 4, None) self.assertEquals(packet_count, LookupMap(self.map_fd, uid).value) - DeleteMap(self.map_fd, uid); + DeleteMap(self.map_fd, uid) SocketUDPLoopBack(packet_count, 6, None) self.assertEquals(packet_count, LookupMap(self.map_fd, uid).value) BpfProgDetach(self._cg_fd, BPF_CGROUP_INET_INGRESS) + def checkSocketCreate(self, family, socktype, success): + try: + sock = socket.socket(family, socktype, 0) + except socket.error, e: + if success: + self.fail("Failed to create socket family=%d type=%d err=%s" % + (family, socktype, os.strerror(e.errno))) + return; + if not success: + self.fail("unexpected socket family=%d type=%d created, should be blocked" % + (family, socktype)) + + + def trySocketCreate(self, success): + for family in [socket.AF_INET, socket.AF_INET6]: + for socktype in [socket.SOCK_DGRAM, socket.SOCK_STREAM]: + self.checkSocketCreate(family, socktype, success) + + @unittest.skipUnless(HAVE_EBPF_SOCKET, + "Cgroup BPF socket is not supported") + def testCgroupSocketCreateBlock(self): + instructions = [ + BpfFuncCall(BPF_FUNC_get_current_uid_gid), + BpfAlu64Imm(BPF_AND, BPF_REG_0, 0xfffffff), + BpfJumpImm(BPF_JNE, BPF_REG_0, TEST_UID, 2), + ] + instructions += INS_BPF_EXIT_BLOCK + INS_CGROUP_ACCEPT; + self.prog_fd = BpfProgLoad(BPF_PROG_TYPE_CGROUP_SOCK, instructions) + BpfProgAttach(self.prog_fd, self._cg_fd, BPF_CGROUP_INET_SOCK_CREATE) + with net_test.RunAsUid(TEST_UID): + # Socket creation with target uid should fail + self.trySocketCreate(False); + # Socket create with different uid should success + self.trySocketCreate(True) + BpfProgDetach(self._cg_fd, BPF_CGROUP_INET_SOCK_CREATE) + with net_test.RunAsUid(TEST_UID): + self.trySocketCreate(True) + if __name__ == "__main__": unittest.main() diff --git a/net/test/net_test.py b/net/test/net_test.py index 035ba60..1c7f32f 100755 --- a/net/test/net_test.py +++ b/net/test/net_test.py @@ -369,17 +369,17 @@ class RunAsUidGid(object): def __enter__(self): if self.uid: - self.saved_uid = os.geteuid() + self.saved_uids = os.getresuid() self.saved_groups = os.getgroups() os.setgroups(self.saved_groups + [AID_INET]) - os.seteuid(self.uid) + os.setresuid(self.uid, self.uid, self.saved_uids[0]) if self.gid: self.saved_gid = os.getgid() os.setgid(self.gid) def __exit__(self, unused_type, unused_value, unused_traceback): if self.uid: - os.seteuid(self.saved_uid) + os.setresuid(*self.saved_uids) os.setgroups(self.saved_groups) if self.gid: os.setgid(self.saved_gid) @@ -390,7 +390,6 @@ class RunAsUid(RunAsUidGid): def __init__(self, uid): RunAsUidGid.__init__(self, uid, 0) - class NetworkTest(unittest.TestCase): def assertRaisesErrno(self, err_num, f=None, *args): -- cgit v1.2.3 From ae95d436ff51edda3be67b0c02ef516fcbf6948a Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Wed, 31 Oct 2018 12:40:02 -0700 Subject: Close the socket in socketCreateTest before exist The testCgroupSocketCreateBlock test left some socket open when test exist and might cause other net test flaky. This patch fix that before the test exits. Bug: 118753529 Test: ./all_test.sh Change-Id: I88be0b49b2dfa001bfc4fea699f9bf714e4ec8e3 --- net/test/bpf_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/net/test/bpf_test.py b/net/test/bpf_test.py index 823dcbe..ea3e56b 100755 --- a/net/test/bpf_test.py +++ b/net/test/bpf_test.py @@ -406,6 +406,7 @@ class BpfCgroupTest(net_test.NetworkTest): def checkSocketCreate(self, family, socktype, success): try: sock = socket.socket(family, socktype, 0) + sock.close() except socket.error, e: if success: self.fail("Failed to create socket family=%d type=%d err=%s" % -- cgit v1.2.3 From a563236b85d4beba4fa194468c87d356f2090544 Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Wed, 31 Oct 2018 17:52:14 -0700 Subject: Use blocking sockets with timeout for xfrm_tunnel_test Using non-blocking sockets appears to be causing flakiness due to the tests racing against the kernel's crypto/networking infrastructure. This commit allows these sockets to block for up to 500ms to allow time for the crypto and routing to complete before throwing errors. Bug: 118753529 Test: Run 20x locally, via QEMU; passing. Change-Id: I92a39f8890244ebe918f48bcf0971e85795b2a1e --- net/test/xfrm_tunnel_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/test/xfrm_tunnel_test.py b/net/test/xfrm_tunnel_test.py index 89b7c58..bf32ffc 100755 --- a/net/test/xfrm_tunnel_test.py +++ b/net/test/xfrm_tunnel_test.py @@ -112,7 +112,7 @@ def _CreateReceiveSock(version, port=0): # The second parameter of the tuple is the port number regardless of AF. local_port = read_sock.getsockname()[1] # Guard against the eventuality of the receive failing. - net_test.SetNonBlocking(read_sock.fileno()) + csocket.SetSocketTimeout(read_sock, 500) return read_sock, local_port -- cgit v1.2.3 From 2eacd483279af9c2296d7aea99594ce3425d5d7d Mon Sep 17 00:00:00 2001 From: Yongqin Liu Date: Wed, 30 May 2018 20:54:48 +0800 Subject: anycast_test.py: change to use thread.join to wait CloseFileDescriptorThread finished The original implementation closes fd in a separate thread, and the use time.sleep(0.1) to wait it finished, but there is the case that on platforms like hikey the thread is not finished even after the slept 0.1s, to make this test works for more platforms, we change to use thread.join(0.5) here to wait up to 0.5 seconds. And use thread.isActive to check if there is the hang problem. Test: run vts-kernel -m VtsKernelNetTest with 4.9 and 4.14 kernel There will be 1 or more failed in 5 iterations, after this change all will pass in 10 iterations. Change-Id: I24341667c9a889d79d38a9eb1d88d05325d7ffff Signed-off-by: Yongqin Liu (cherry picked from commit 4e398426edba77b1e4cbbf97df61efd5b0710ef6) --- net/test/anycast_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/test/anycast_test.py b/net/test/anycast_test.py index 82130db..62d874e 100755 --- a/net/test/anycast_test.py +++ b/net/test/anycast_test.py @@ -93,7 +93,9 @@ class AnycastTest(multinetwork_base.MultiNetworkBaseTest): # This will hang if the kernel has the bug. thread = CloseFileDescriptorThread(self.tuns[netid]) thread.start() - time.sleep(0.1) + # Wait up to 0.5 seconds for the thread to finish, but + # continue and fail the test if the thread hangs. + thread.join(0.5) # Make teardown work. del self.tuns[netid] -- cgit v1.2.3 From 86a0a4c2dd80d3f6e650ee210756d84e4ce7bb10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Wed, 31 Oct 2018 16:24:53 -0700 Subject: net-test: deflake 4.19 entropy installation Bug: 118651133 Change-Id: I00e0ab757d0776c425802b60f5d60d5195a3f2dc --- net/test/net_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/test/net_test.sh b/net/test/net_test.sh index 0c3a3ad..1444da3 100755 --- a/net/test/net_test.sh +++ b/net/test/net_test.sh @@ -4,7 +4,7 @@ if [[ -n "${entropy}" ]]; then # In kernel/include/uapi/linux/random.h RNDADDENTROPY is defined as # _IOW('R', 0x03, int[2]) =(R is 0x52)= 0x40085203 = 1074287107 - python 3>/dev/random </dev/random < Date: Tue, 31 Jul 2018 21:24:35 -0700 Subject: Revert "Implement a workaround for clang + PARAVIRT failure." This reverts commit 805d82cce067183af3dec98b4b04f0824cd1562f. Problem has been fixed in upstream Linux kernels, 4.4/4.9/4.14 and the code has been merged via LTS into kernel/common. Bug: 111396209 Change-Id: I397a3ca34ced1b8528fab6f795144f6d94fb8d7f Signed-off-by: Alistair Strachan --- net/test/run_net_test.sh | 8 -------- 1 file changed, 8 deletions(-) diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index 028ed85..7cb755e 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -228,14 +228,6 @@ if ((nobuild == 0)); then # The CC flag is *not* inherited from the environment, so it must be # passed in on the command line. make_flags="$make_flags CC=$CC" - # TODO: Remove this workaround for https://lkml.org/lkml/2018/5/7/534 - # Needs a change to clang to be merged, an updated toolchain, and - # a new __nostackprotector annotation of the affected PARAVIRT - # code in the affected kernel branches (android-4.4, android-4.9, - # android-4.14). This sidesteps the issue by disabling PARAVIRT. - if [ "$CC" == "clang" ]; then - DISABLE_OPTIONS="$DISABLE_OPTIONS PARAVIRT" - fi fi # If there's no kernel config at all, create one or UML won't work. -- cgit v1.2.3 From 38f75fa58ff755f372842d30394d2352e0765da8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Wed, 7 Nov 2018 05:10:48 -0800 Subject: net_test: workaround for 3.18 and 4.4 late urandom init By default it initializes 60 seconds after boot (if there's enough entropy at that point in time), which is too late (we don't want to simply wait 60 seconds, but we do want it initialized prior to running any tests). We also switch from hex (base 16) to base64 encoding of the entropy string just to make the kernel command line shorter. Change-Id: Ia4924576c550b2858a938873baef18893550fa04 --- net/test/net_test.sh | 23 +++++++++++++++++------ net/test/run_net_test.sh | 18 +++++++++++------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/net/test/net_test.sh b/net/test/net_test.sh index 1444da3..c0c8c47 100755 --- a/net/test/net_test.sh +++ b/net/test/net_test.sh @@ -1,4 +1,8 @@ #!/bin/bash +# This defaults to 60 which is needlessly long during boot +# (we will reset it back to the default later) +echo 0 > /proc/sys/kernel/random/urandom_min_reseed_secs + if [[ -n "${entropy}" ]]; then echo "adding entropy from hex string [${entropy}]" 1>&2 @@ -6,17 +10,24 @@ if [[ -n "${entropy}" ]]; then # _IOW('R', 0x03, int[2]) =(R is 0x52)= 0x40085203 = 1074287107 /usr/bin/python 3>/dev/random < /proc/sys/kernel/random/urandom_min_reseed_secs + + # In case IPv6 is compiled as a module. [ -f /proc/net/if_inet6 ] || insmod $DIR/kernel/net-next/net/ipv6/ipv6.ko diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index 7cb755e..0712655 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -267,15 +267,19 @@ if [ "$ARCH" == "um" ]; then # Use UML's /proc/exitcode feature to communicate errors on test failure cmdline="$cmdline net_test_exitcode=/proc/exitcode" - # Experience shows we need at least 128 bits of entropy for the kernel's - # crng init to complete, hence net_test.sh needs at least 32 hex chars - # (which is the amount of hex in a single random UUID) provided to it on - # the kernel cmdline. + # Experience shows that we need at least 128 bits of entropy for the + # kernel's crng init to complete (before it fully initializes stuff behaves + # *weirdly* and there's plenty of kernel warnings and some tests even fail), + # hence net_test.sh needs at least 32 hex chars (which is the amount of hex + # in a single random UUID) provided to it on the kernel cmdline. # - # We'll pass in 384 bits just to be safe, ie. a random 96 hex char seed. - # We do this by getting *triple* random UUIDs and concatenating their hex - # digits into an *even* length hex encoded string. + # Just to be safe, we'll pass in 384 bits, and we'll do this as a random + # 64 character base64 seed (because this is shorter than base16). + # We do this by getting *three* random UUIDs and concatenating their hex + # digits into an *even* length hex encoded string, which we then convert + # into base64. entropy="$(cat /proc/sys/kernel/random{/,/,/}uuid | tr -d '\n-')" + entropy="$(xxd -r -p <<< "${entropy}" | base64 -w 0)" cmdline="${cmdline} entropy=${entropy}" # Map the --readonly flag to UML block device names -- cgit v1.2.3 From 42f963407a4c6e6630f0e72b1a8eae741fbb3eef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Wed, 7 Nov 2018 02:16:37 -0800 Subject: net_test: fix job control in console's bash terminal This makes Ctrl+C and Ctrl+Z work. (while we're at it add some more debugging print out to ease debugging and better explain how the transition works) Change-Id: I5d6d82a53161f7ff1bc6f1b5329ba864d0759a48 --- net/test/net_test.sh | 105 ++++++++++++++++++++++++++++++++++++++++++++++- net/test/run_net_test.sh | 35 ++++++++++++++-- 2 files changed, 135 insertions(+), 5 deletions(-) diff --git a/net/test/net_test.sh b/net/test/net_test.sh index c0c8c47..72c67a9 100755 --- a/net/test/net_test.sh +++ b/net/test/net_test.sh @@ -1,10 +1,113 @@ #!/bin/bash +if [[ -n "${verbose}" ]]; then + echo 'Current working directory:' + echo " - according to builtin: [$(pwd)]" + echo " - according to /bin/pwd: [$(/bin/pwd)]" + echo + + echo 'Shell environment:' + env + echo + + echo -n "net_test.sh (pid $$, parent ${PPID}, tty $(tty)) running [$0] with args:" + for arg in "$@"; do + echo -n " [${arg}]" + done + echo + echo +fi + +if [[ "$(tty)" == '/dev/console' ]]; then + ARCH="$(uname -m)" + # Underscore is illegal in hostname, replace with hyphen + ARCH="${ARCH//_/-}" + + # setsid + /dev/tty{,AMA,S}0 allows bash's job control to work, ie. Ctrl+C/Z + if [[ -c '/dev/tty0' ]]; then + # exists in UML, does not exist on graphics/vga/curses-less QEMU + CON='/dev/tty0' + hostname "uml-${ARCH}" + elif [[ -c '/dev/ttyAMA0' ]]; then + # Qemu for arm (note: /dev/ttyS0 also exists for exitcode) + CON='/dev/ttyAMA0' + hostname "qemu-${ARCH}" + elif [[ -c '/dev/ttyS0' ]]; then + # Qemu for x86 (note: /dev/ttyS1 also exists for exitcode) + CON='/dev/ttyS0' + hostname "qemu-${ARCH}" + else + # Can't figure it out, job control won't work, tough luck + echo 'Unable to figure out proper console - job control will not work.' >&2 + CON='' + hostname "local-${ARCH}" + fi + + unset ARCH + + echo -n "$(hostname): Currently tty[/dev/console], but it should be [${CON}]..." + + if [[ -n "${CON}" ]]; then + # Redirect std{in,out,err} to the console equivalent tty + # which actually supports all standard tty ioctls + exec <"${CON}" >&"${CON}" + + # Bash wants to be session leader, hence need for setsid + echo " re-executing..." + exec /usr/bin/setsid "$0" "$@" + # If the above exec fails, we just fall through... + # (this implies failure to *find* setsid, not error return from bash, + # in practice due to image construction this cannot happen) + else + echo + fi + + # In case we fall through, clean up + unset CON +fi + +if [[ -n "${verbose}" ]]; then + echo 'TTY settings:' + stty + echo + + echo 'TTY settings (verbose):' + stty -a + echo + + echo 'Restoring TTY sanity...' +fi + +stty sane +stty 115200 +[[ -z "${console_cols}" ]] || stty columns "${console_cols}" +[[ -z "${console_rows}" ]] || stty rows "${console_rows}" + +if [[ -n "${verbose}" ]]; then + echo + + echo 'TTY settings:' + stty + echo + + echo 'TTY settings (verbose):' + stty -a + echo +fi + +# By the time we get here we should have a sane console: +# - 115200 baud rate +# - appropriate (and known) width and height (note: this assumes +# that the terminal doesn't get further resized) +# - it is no longer /dev/console, so job control should function +# (this means working ctrl+c [abort] and ctrl+z [suspend]) + + # This defaults to 60 which is needlessly long during boot # (we will reset it back to the default later) echo 0 > /proc/sys/kernel/random/urandom_min_reseed_secs if [[ -n "${entropy}" ]]; then - echo "adding entropy from hex string [${entropy}]" 1>&2 + echo "adding entropy from hex string [${entropy}]" >&2 # In kernel/include/uapi/linux/random.h RNDADDENTROPY is defined as # _IOW('R', 0x03, int[2]) =(R is 0x52)= 0x40085203 = 1074287107 diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index 0712655..17b44d9 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -105,6 +105,12 @@ nowrite=1 nobuild=0 norun=0 +if tty >/dev/null; then + verbose= +else + verbose=1 +fi + while [[ -n "$1" ]]; do if [[ "$1" == "--builder" ]]; then consolemode="con=null,fd:1" @@ -122,6 +128,12 @@ while [[ -n "$1" ]]; do elif [[ "$1" == "--norun" ]]; then norun=1 shift + elif [[ "$1" == "--verbose" ]]; then + verbose=1 + shift + elif [[ "$1" == "--noverbose" ]]; then + verbose= + shift else test=$1 break # Arguments after the test file are passed to the test itself. @@ -153,7 +165,7 @@ function isBuildOnly() { if ! isRunningTest && ! isBuildOnly; then echo "Usage:" >&2 - echo " $0 [--builder] [--readonly|--ro|--readwrite|--rw] [--nobuild] " >&2 + echo " $0 [--builder] [--readonly|--ro|--readwrite|--rw] [--nobuild] [--verbose] " >&2 echo " $0 --norun" >&2 exit 1 fi @@ -257,6 +269,11 @@ fi if (( nowrite == 1 )); then cmdline="ro" fi + +if (( verbose == 1 )); then + cmdline="$cmdline verbose=1" +fi + cmdline="$cmdline init=/sbin/net_test.sh" cmdline="$cmdline net_test_args=\"$test_args\" net_test_mode=$testmode" @@ -308,6 +325,12 @@ else blockdevice="-drive file=$SCRIPT_DIR/$ROOTFS,format=raw,if=none,id=drive-virtio-disk0$blockdevice" blockdevice="$blockdevice -device virtio-blk-pci,drive=drive-virtio-disk0" + # Pass through our current console/screen size to inner shell session + read rows cols < <(stty size 2>/dev/null) + [[ -z "${rows}" ]] || cmdline="${cmdline} console_rows=${rows}" + [[ -z "${cols}" ]] || cmdline="${cmdline} console_cols=${cols}" + unset rows cols + # QEMU has no way to modify its exitcode; simulate it with a serial port. # # Choose to do it this way over writing a file to /host, because QEMU will @@ -335,18 +358,22 @@ else $qemu >&2 -name net_test -m 512 \ -kernel $KERNEL_BINARY \ - -no-user-config -nodefaults -no-reboot -display none \ + -no-user-config -nodefaults -no-reboot \ + -display none -nographic -serial mon:stdio -parallel none \ -smp 4,sockets=4,cores=1,threads=1 \ -device virtio-rng-pci \ -chardev file,id=exitcode,path=exitcode \ -device pci-serial,chardev=exitcode \ -fsdev local,security_model=mapped-xattr,id=fsdev0,fmode=0644,dmode=0755,path=$SCRIPT_DIR \ -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=host \ - $blockdevice $netconfig -serial stdio -append "$cmdline" - [ -s exitcode ] && exitcode=`cat exitcode | tr -d '\r'` || exitcode=1 + $blockdevice $netconfig -append "$cmdline" + [[ -s exitcode ]] && exitcode=`cat exitcode | tr -d '\r'` || exitcode=1 rm -f exitcode fi # UML reliably screws up the ptys, QEMU probably can as well... fixup_ptys +stty sane || : + +echo "Returning exit code ${exitcode}." 1>&2 exit "${exitcode}" -- cgit v1.2.3 From a9d3dc801c500c888d1e0ef7061ca0cc68c75575 Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Thu, 1 Nov 2018 14:17:08 -0700 Subject: Test to check tcp initial rwnd size The backport for 4.19 kernel sets the initial receive window size to a new value greater then 64k. Use a connected TCP socket to check the patch is properly applied. Test: ./sock_diag_test.py Bug: 118591209 Change-Id: If21c6e951aede70c22699d1794dbc09ce91ba5e4 --- net/test/sock_diag_test.py | 56 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/net/test/sock_diag_test.py b/net/test/sock_diag_test.py index b195fe3..daa2fa4 100755 --- a/net/test/sock_diag_test.py +++ b/net/test/sock_diag_test.py @@ -25,16 +25,20 @@ import threading import time import unittest +import cstruct import multinetwork_base import net_test import packets import sock_diag import tcp_test +# Mostly empty structure definition containing only the fields we currently use. +TcpInfo = cstruct.Struct("TcpInfo", "64xI", "tcpi_rcv_ssthresh") NUM_SOCKETS = 30 NO_BYTECODE = "" -HAVE_SO_COOKIE_SUPPORT = net_test.LINUX_VERSION >= (4, 9, 0) +LINUX_4_9_OR_ABOVE = net_test.LINUX_VERSION >= (4, 9, 0) +LINUX_4_19_OR_ABOVE = net_test.LINUX_VERSION >= (4, 19, 0) IPPROTO_SCTP = 132 @@ -55,7 +59,7 @@ def HaveUdpDiag(): True if the kernel is 4.9 or above, or the CONFIG_INET_UDP_DIAG is enabled. False otherwise. """ - if HAVE_SO_COOKIE_SUPPORT: + if LINUX_4_9_OR_ABOVE: return True; s = socket(AF_INET6, SOCK_DGRAM, 0) s.bind(("::", 0)) @@ -386,7 +390,7 @@ class SockDiagTest(SockDiagBaseTest): cookie = sock.getsockopt(net_test.SOL_SOCKET, net_test.SO_COOKIE, 8) self.assertEqual(diag_msg.id.cookie, cookie) - @unittest.skipUnless(HAVE_SO_COOKIE_SUPPORT, "SO_COOKIE not supported") + @unittest.skipUnless(LINUX_4_9_OR_ABOVE, "SO_COOKIE not supported") def testGetsockoptcookie(self): self.CheckSocketCookie(AF_INET, "127.0.0.1") self.CheckSocketCookie(AF_INET6, "::1") @@ -410,7 +414,7 @@ class SockDiagTest(SockDiagBaseTest): # Create a fully-specified diag req from our socket, including cookie if # we can get it. req = self.sock_diag.DiagReqFromSocket(s) - if HAVE_SO_COOKIE_SUPPORT: + if LINUX_4_9_OR_ABOVE: req.id.cookie = s.getsockopt(net_test.SOL_SOCKET, net_test.SO_COOKIE, 8) else: req.id.cookie = "\xff" * 16 # INET_DIAG_NOCOOKIE[2] @@ -547,6 +551,50 @@ class SockDiagTcpTest(tcp_test.TcpBaseTest, SockDiagBaseTest): child.id.src) +class TcpRcvWindowTest(tcp_test.TcpBaseTest, SockDiagBaseTest): + + RWND_SIZE = 64000 if LINUX_4_19_OR_ABOVE else 42000 + TCP_DEFAULT_INIT_RWND = "/proc/sys/net/ipv4/tcp_default_init_rwnd" + + def setUp(self): + super(TcpRcvWindowTest, self).setUp() + if LINUX_4_19_OR_ABOVE: + self.assertRaisesErrno(ENOENT, open, self.TCP_DEFAULT_INIT_RWND, "w") + return + + f = open(self.TCP_DEFAULT_INIT_RWND, "w") + f.write("60") + + def checkInitRwndSize(self, version, netid): + self.IncomingConnection(version, tcp_test.TCP_ESTABLISHED, netid) + tcpInfo = TcpInfo(self.accepted.getsockopt(net_test.SOL_TCP, + net_test.TCP_INFO, len(TcpInfo))) + self.assertLess(self.RWND_SIZE, tcpInfo.tcpi_rcv_ssthresh, + "Tcp rwnd of netid=%d, version=%d is not enough. " + "Expect: %d, actual: %d" % (netid, version, self.RWND_SIZE, + tcpInfo.tcpi_rcv_ssthresh)) + + def checkSynPacketWindowSize(self, version, netid): + s = self.BuildSocket(version, net_test.TCPSocket, netid, "mark") + myaddr = self.MyAddress(version, netid) + dstaddr = self.GetRemoteAddress(version) + dstsockaddr = self.GetRemoteSocketAddress(version) + desc, expected = packets.SYN(53, version, myaddr, dstaddr, + sport=None, seq=None) + self.assertRaisesErrno(EINPROGRESS, s.connect, (dstsockaddr, 53)) + msg = "IPv%s TCP connect: expected %s on %s" % ( + version, desc, self.GetInterfaceName(netid)) + syn = self.ExpectPacketOn(netid, msg, expected) + self.assertLess(self.RWND_SIZE, syn.window) + s.close() + + def testTcpCwndSize(self): + for version in [4, 5, 6]: + for netid in self.NETIDS: + self.checkInitRwndSize(version, netid) + self.checkSynPacketWindowSize(version, netid) + + class SockDestroyTcpTest(tcp_test.TcpBaseTest, SockDiagBaseTest): def setUp(self): -- cgit v1.2.3 From 3172befe25a9628ad9a7dd94564683fc86bb533b Mon Sep 17 00:00:00 2001 From: "yunsik.lee" Date: Thu, 29 Nov 2018 13:25:50 +0900 Subject: anycast_test.py: increase waiting time to 3 sec to wait CloseFileDescriptorThread finished Because there are some platforms that take more time than 0.5 seconds, such as platforms that uses MPTCP implementation from https://www.multipath-tcp.org/, we change the waiting time to 3 seconds. For kernels that MPTCP is ported, it take more than 0.5 sec to close tun interface, because of DAD procedure within MPTCP fullmesh module. It seems to be the duplicate address-timer takes a refcount on the IPv6-address, preventing it from getting closed. Test: run vts-kernel -m VtsKernelNetTest with 4.9 and 4.14 kernel using MPTCP implementation Without this change, VtsKernelNetTest was failed with MPTCP enabled kernel every time. After this change all will pass in 5 iterations. Bug: 120248546 Change-Id: I69093d5aa55d2796eac1f6db10d8ba649b63ae9a Signed-off-by: Yunsik Lee --- net/test/anycast_test.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) mode change 100755 => 100644 net/test/anycast_test.py diff --git a/net/test/anycast_test.py b/net/test/anycast_test.py old mode 100755 new mode 100644 index 62d874e..6222580 --- a/net/test/anycast_test.py +++ b/net/test/anycast_test.py @@ -93,9 +93,14 @@ class AnycastTest(multinetwork_base.MultiNetworkBaseTest): # This will hang if the kernel has the bug. thread = CloseFileDescriptorThread(self.tuns[netid]) thread.start() - # Wait up to 0.5 seconds for the thread to finish, but + # Wait up to 3 seconds for the thread to finish, but # continue and fail the test if the thread hangs. - thread.join(0.5) + + # For kernels with MPTCP ported, closing tun interface need more + # than 0.5 sec. DAD procedure within MPTCP fullmesh module takes + # more time, because duplicate address-timer takes a refcount + # on the IPv6-address, preventing it from getting closed. + thread.join(3) # Make teardown work. del self.tuns[netid] -- cgit v1.2.3 From 4832bbb524111b0335d52b284b9566aeba668e3a Mon Sep 17 00:00:00 2001 From: "yunsik.lee" Date: Thu, 29 Nov 2018 13:25:50 +0900 Subject: anycast_test.py: increase waiting time to 3 sec to wait CloseFileDescriptorThread finished Because there are some platforms that take more time than 0.5 seconds, such as platforms that uses MPTCP implementation from https://www.multipath-tcp.org/, we change the waiting time to 3 seconds. For kernels that MPTCP is ported, it take more than 0.5 sec to close tun interface, because of DAD procedure within MPTCP fullmesh module. It seems to be the duplicate address-timer takes a refcount on the IPv6-address, preventing it from getting closed. Test: run vts-kernel -m VtsKernelNetTest with 4.9 and 4.14 kernel using MPTCP implementation Without this change, VtsKernelNetTest was failed with MPTCP enabled kernel every time. After this change all will pass in 5 iterations. Bug: 120248546 Change-Id: I69093d5aa55d2796eac1f6db10d8ba649b63ae9a Signed-off-by: Yunsik Lee (cherry picked from commit 3172befe25a9628ad9a7dd94564683fc86bb533b) --- net/test/anycast_test.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) mode change 100755 => 100644 net/test/anycast_test.py diff --git a/net/test/anycast_test.py b/net/test/anycast_test.py old mode 100755 new mode 100644 index 62d874e..6222580 --- a/net/test/anycast_test.py +++ b/net/test/anycast_test.py @@ -93,9 +93,14 @@ class AnycastTest(multinetwork_base.MultiNetworkBaseTest): # This will hang if the kernel has the bug. thread = CloseFileDescriptorThread(self.tuns[netid]) thread.start() - # Wait up to 0.5 seconds for the thread to finish, but + # Wait up to 3 seconds for the thread to finish, but # continue and fail the test if the thread hangs. - thread.join(0.5) + + # For kernels with MPTCP ported, closing tun interface need more + # than 0.5 sec. DAD procedure within MPTCP fullmesh module takes + # more time, because duplicate address-timer takes a refcount + # on the IPv6-address, preventing it from getting closed. + thread.join(3) # Make teardown work. del self.tuns[netid] -- cgit v1.2.3 From 4406bae0a2cb27d99b6fe5f9f751399d05e0bb49 Mon Sep 17 00:00:00 2001 From: Nathan Harold Date: Tue, 23 Jan 2018 16:15:39 -0800 Subject: Test Updating OUTPUT_MARK on Active SAs Test that the xfrm output mark is update-able on an Active SA, which means that we can dynamically reroute traffic to new underlying networks after an SA has been created. Bug: 71645364 Test: run_net_test.sh xfrm_test.py Change-Id: I561fdc27439d33807667c4a58a03bca3c468599b --- net/test/xfrm_test.py | 94 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/net/test/xfrm_test.py b/net/test/xfrm_test.py index 3a3d9b0..afcacde 100755 --- a/net/test/xfrm_test.py +++ b/net/test/xfrm_test.py @@ -38,6 +38,9 @@ ENCRYPTED_PAYLOAD = ("b1c74998efd6326faebe2061f00f2c750e90e76001664a80c287b150" TEST_ADDR1 = "2001:4860:4860::8888" TEST_ADDR2 = "2001:4860:4860::8844" +XFRM_STATS_PROCFILE = "/proc/net/xfrm_stat" +XFRM_STATS_OUT_NO_STATES = "XfrmOutNoStates" + # IP addresses to use for tunnel endpoints. For generality, these should be # different from the addresses we send packets to. TUNNEL_ENDPOINTS = {4: "8.8.4.4", 6: TEST_ADDR2} @@ -778,7 +781,7 @@ class XfrmOutputMarkTest(xfrm_base.XfrmLazyTest): xfrm_base._ALGO_HMAC_SHA1, None, None, None, 0) def testUpdateSaAddMark(self): - """Test that when an SA has no mark, it can be updated to add a mark.""" + """Test that an embryonic SA can be updated to add a mark.""" for version in [4, 6]: spi = 0xABCD # Test that an SA created with ALLOCSPI can be updated with the mark. @@ -798,7 +801,94 @@ class XfrmOutputMarkTest(xfrm_base.XfrmLazyTest): self.xfrm.DeleteSaInfo(net_test.GetWildcardAddress(version), spi, IPPROTO_ESP, mark) - # TODO: we might also need to update the mark for a VALID SA. + def getXfrmStat(self, statName): + stateVal = 0 + with open(XFRM_STATS_PROCFILE, 'r') as f: + for line in f: + if statName in line: + stateVal = int(line.split()[1]) + break + f.close() + return stateVal + + def testUpdateActiveSaMarks(self): + """Test that the OUTPUT_MARK can be updated on an ACTIVE SA.""" + for version in [4, 6]: + family = net_test.GetAddressFamily(version) + netid = self.RandomNetid() + remote = self.GetRemoteAddress(version) + local = self.MyAddress(version, netid) + s = socket(family, SOCK_DGRAM, 0) + self.SelectInterface(s, netid, "mark") + # Create a mark that we will apply to the policy and later the SA + mark = xfrm.ExactMatchMark(netid) + + # Create a global policy that selects using the mark. + sel = xfrm.EmptySelector(family) + policy = xfrm.UserPolicy(xfrm.XFRM_POLICY_OUT, sel) + tmpl = xfrm.UserTemplate(family, 0, 0, (local, remote)) + self.xfrm.AddPolicyInfo(policy, tmpl, mark) + + # Pull /proc/net/xfrm_stats for baseline + outNoStateCount = self.getXfrmStat(XFRM_STATS_OUT_NO_STATES); + + # should increment XfrmOutNoStates + s.sendto(net_test.UDP_PAYLOAD, (remote, 53)) + + # Check to make sure XfrmOutNoStates is incremented by exactly 1 + self.assertEquals(outNoStateCount + 1, + self.getXfrmStat(XFRM_STATS_OUT_NO_STATES)) + + length = xfrm_base.GetEspPacketLength(xfrm.XFRM_MODE_TUNNEL, + version, False, + net_test.UDP_PAYLOAD, + xfrm_base._ALGO_HMAC_SHA1, + xfrm_base._ALGO_CBC_AES_256) + + # Add a default SA with no mark that routes to nowhere. + self.xfrm.AddSaInfo(local, + remote, + TEST_SPI, xfrm.XFRM_MODE_TUNNEL, 0, + xfrm_base._ALGO_CBC_AES_256, + xfrm_base._ALGO_HMAC_SHA1, + None, None, None, 0, is_update=False) + self.assertRaisesErrno( + ENETUNREACH, + s.sendto, net_test.UDP_PAYLOAD, (remote, 53)) + + # Update the SA to route to a valid netid. + self.xfrm.AddSaInfo(local, + remote, + TEST_SPI, xfrm.XFRM_MODE_TUNNEL, 0, + xfrm_base._ALGO_CBC_AES_256, + xfrm_base._ALGO_HMAC_SHA1, + None, None, None, netid, is_update=True) + + # Now the payload routes to the updated netid. + s.sendto(net_test.UDP_PAYLOAD, (remote, 53)) + self._ExpectEspPacketOn(netid, TEST_SPI, 1, length, None, None) + + # Get a new netid and reroute the packets to the new netid. + reroute_netid = self.RandomNetid(netid) + # Update the SA to change the output mark. + self.xfrm.AddSaInfo(local, + remote, + TEST_SPI, xfrm.XFRM_MODE_TUNNEL, 0, + xfrm_base._ALGO_CBC_AES_256, + xfrm_base._ALGO_HMAC_SHA1, + None, None, None, reroute_netid, is_update=True) + + s.sendto(net_test.UDP_PAYLOAD, (remote, 53)) + self._ExpectEspPacketOn(reroute_netid, TEST_SPI, 2, length, None, None) + + dump = self.xfrm.DumpSaInfo() + + self.assertEquals(1, len(dump)) # check that update updated + sainfo, attributes = dump[0] + self.assertEquals(reroute_netid, attributes["XFRMA_OUTPUT_MARK"]) + + self.xfrm.DeleteSaInfo(remote, TEST_SPI, IPPROTO_ESP, None) + self.xfrm.DeletePolicyInfo(sel, xfrm.XFRM_POLICY_OUT, mark) if __name__ == "__main__": unittest.main() -- cgit v1.2.3 From dd2d31985f4ebabe78c9bb212fadd3b489c849fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Sat, 22 Dec 2018 07:29:42 +0000 Subject: Revert "Test Updating OUTPUT_MARK on Active SAs" This reverts commit 4406bae0a2cb27d99b6fe5f9f751399d05e0bb49. Reason for revert: Christmas should be Green not Red. Change-Id: Ia2209dc0d0c8c58918821178dcb0b9d73eddf271 --- net/test/xfrm_test.py | 94 ++------------------------------------------------- 1 file changed, 2 insertions(+), 92 deletions(-) diff --git a/net/test/xfrm_test.py b/net/test/xfrm_test.py index afcacde..3a3d9b0 100755 --- a/net/test/xfrm_test.py +++ b/net/test/xfrm_test.py @@ -38,9 +38,6 @@ ENCRYPTED_PAYLOAD = ("b1c74998efd6326faebe2061f00f2c750e90e76001664a80c287b150" TEST_ADDR1 = "2001:4860:4860::8888" TEST_ADDR2 = "2001:4860:4860::8844" -XFRM_STATS_PROCFILE = "/proc/net/xfrm_stat" -XFRM_STATS_OUT_NO_STATES = "XfrmOutNoStates" - # IP addresses to use for tunnel endpoints. For generality, these should be # different from the addresses we send packets to. TUNNEL_ENDPOINTS = {4: "8.8.4.4", 6: TEST_ADDR2} @@ -781,7 +778,7 @@ class XfrmOutputMarkTest(xfrm_base.XfrmLazyTest): xfrm_base._ALGO_HMAC_SHA1, None, None, None, 0) def testUpdateSaAddMark(self): - """Test that an embryonic SA can be updated to add a mark.""" + """Test that when an SA has no mark, it can be updated to add a mark.""" for version in [4, 6]: spi = 0xABCD # Test that an SA created with ALLOCSPI can be updated with the mark. @@ -801,94 +798,7 @@ class XfrmOutputMarkTest(xfrm_base.XfrmLazyTest): self.xfrm.DeleteSaInfo(net_test.GetWildcardAddress(version), spi, IPPROTO_ESP, mark) - def getXfrmStat(self, statName): - stateVal = 0 - with open(XFRM_STATS_PROCFILE, 'r') as f: - for line in f: - if statName in line: - stateVal = int(line.split()[1]) - break - f.close() - return stateVal - - def testUpdateActiveSaMarks(self): - """Test that the OUTPUT_MARK can be updated on an ACTIVE SA.""" - for version in [4, 6]: - family = net_test.GetAddressFamily(version) - netid = self.RandomNetid() - remote = self.GetRemoteAddress(version) - local = self.MyAddress(version, netid) - s = socket(family, SOCK_DGRAM, 0) - self.SelectInterface(s, netid, "mark") - # Create a mark that we will apply to the policy and later the SA - mark = xfrm.ExactMatchMark(netid) - - # Create a global policy that selects using the mark. - sel = xfrm.EmptySelector(family) - policy = xfrm.UserPolicy(xfrm.XFRM_POLICY_OUT, sel) - tmpl = xfrm.UserTemplate(family, 0, 0, (local, remote)) - self.xfrm.AddPolicyInfo(policy, tmpl, mark) - - # Pull /proc/net/xfrm_stats for baseline - outNoStateCount = self.getXfrmStat(XFRM_STATS_OUT_NO_STATES); - - # should increment XfrmOutNoStates - s.sendto(net_test.UDP_PAYLOAD, (remote, 53)) - - # Check to make sure XfrmOutNoStates is incremented by exactly 1 - self.assertEquals(outNoStateCount + 1, - self.getXfrmStat(XFRM_STATS_OUT_NO_STATES)) - - length = xfrm_base.GetEspPacketLength(xfrm.XFRM_MODE_TUNNEL, - version, False, - net_test.UDP_PAYLOAD, - xfrm_base._ALGO_HMAC_SHA1, - xfrm_base._ALGO_CBC_AES_256) - - # Add a default SA with no mark that routes to nowhere. - self.xfrm.AddSaInfo(local, - remote, - TEST_SPI, xfrm.XFRM_MODE_TUNNEL, 0, - xfrm_base._ALGO_CBC_AES_256, - xfrm_base._ALGO_HMAC_SHA1, - None, None, None, 0, is_update=False) - self.assertRaisesErrno( - ENETUNREACH, - s.sendto, net_test.UDP_PAYLOAD, (remote, 53)) - - # Update the SA to route to a valid netid. - self.xfrm.AddSaInfo(local, - remote, - TEST_SPI, xfrm.XFRM_MODE_TUNNEL, 0, - xfrm_base._ALGO_CBC_AES_256, - xfrm_base._ALGO_HMAC_SHA1, - None, None, None, netid, is_update=True) - - # Now the payload routes to the updated netid. - s.sendto(net_test.UDP_PAYLOAD, (remote, 53)) - self._ExpectEspPacketOn(netid, TEST_SPI, 1, length, None, None) - - # Get a new netid and reroute the packets to the new netid. - reroute_netid = self.RandomNetid(netid) - # Update the SA to change the output mark. - self.xfrm.AddSaInfo(local, - remote, - TEST_SPI, xfrm.XFRM_MODE_TUNNEL, 0, - xfrm_base._ALGO_CBC_AES_256, - xfrm_base._ALGO_HMAC_SHA1, - None, None, None, reroute_netid, is_update=True) - - s.sendto(net_test.UDP_PAYLOAD, (remote, 53)) - self._ExpectEspPacketOn(reroute_netid, TEST_SPI, 2, length, None, None) - - dump = self.xfrm.DumpSaInfo() - - self.assertEquals(1, len(dump)) # check that update updated - sainfo, attributes = dump[0] - self.assertEquals(reroute_netid, attributes["XFRMA_OUTPUT_MARK"]) - - self.xfrm.DeleteSaInfo(remote, TEST_SPI, IPPROTO_ESP, None) - self.xfrm.DeletePolicyInfo(sel, xfrm.XFRM_POLICY_OUT, mark) + # TODO: we might also need to update the mark for a VALID SA. if __name__ == "__main__": unittest.main() -- cgit v1.2.3 From d5512148a543e668508013882246c836637cab5b Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Wed, 2 Jan 2019 16:48:43 -0800 Subject: Add tests for unset output marks on floating policies This change adds tests to ensure backwards compatibility of floating policies with no output mark set, ensuring that they are still routed correctly (over the default network) in mark-based routing schemes. Bug: 122236988 Test: Ran on common-4.9, passing Change-Id: I49d291ad5b6a51bc4556643c96e37885c49dce5d --- net/test/xfrm_tunnel_test.py | 56 +++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/net/test/xfrm_tunnel_test.py b/net/test/xfrm_tunnel_test.py index bf32ffc..eb1a46e 100755 --- a/net/test/xfrm_tunnel_test.py +++ b/net/test/xfrm_tunnel_test.py @@ -172,7 +172,8 @@ class XfrmTunnelTest(xfrm_base.XfrmLazyTest): self.assertEquals(net_test.UDP_PAYLOAD, data) self.assertEquals((remote_inner, _TEST_REMOTE_PORT), src[:2]) - def _TestTunnel(self, inner_version, outer_version, func, direction): + def _TestTunnel(self, inner_version, outer_version, func, direction, + test_output_mark_unset): """Test a unidirectional XFRM Tunnel with explicit selectors""" # Select the underlying netid, which represents the external # interface from/to which to route ESP packets. @@ -186,33 +187,46 @@ class XfrmTunnelTest(xfrm_base.XfrmLazyTest): local_outer = self.MyAddress(outer_version, u_netid) remote_outer = _GetRemoteOuterAddress(outer_version) - # Create input/ouput SPs, SAs and sockets to simulate a more realistic - # environment. - self.xfrm.CreateTunnel( - xfrm.XFRM_POLICY_IN, xfrm.SrcDstSelector(remote_inner, local_inner), - remote_outer, local_outer, _TEST_IN_SPI, xfrm_base._ALGO_CRYPT_NULL, - xfrm_base._ALGO_AUTH_NULL, None, None, None, xfrm.MATCH_METHOD_ALL) + output_mark = u_netid + if test_output_mark_unset: + output_mark = None + self.SetDefaultNetwork(u_netid) - self.xfrm.CreateTunnel( - xfrm.XFRM_POLICY_OUT, xfrm.SrcDstSelector(local_inner, remote_inner), - local_outer, remote_outer, _TEST_OUT_SPI, xfrm_base._ALGO_CBC_AES_256, - xfrm_base._ALGO_HMAC_SHA1, None, u_netid, None, xfrm.MATCH_METHOD_ALL) - - write_sock = socket(net_test.GetAddressFamily(inner_version), SOCK_DGRAM, 0) - self.SelectInterface(write_sock, netid, "mark") - read_sock, _ = _CreateReceiveSock(inner_version) - - sock = write_sock if direction == xfrm.XFRM_POLICY_OUT else read_sock - func(inner_version, outer_version, u_netid, netid, local_inner, - remote_inner, local_outer, remote_outer, sock) + try: + # Create input/ouput SPs, SAs and sockets to simulate a more realistic + # environment. + self.xfrm.CreateTunnel( + xfrm.XFRM_POLICY_IN, xfrm.SrcDstSelector(remote_inner, local_inner), + remote_outer, local_outer, _TEST_IN_SPI, xfrm_base._ALGO_CRYPT_NULL, + xfrm_base._ALGO_AUTH_NULL, None, None, None, xfrm.MATCH_METHOD_ALL) + + self.xfrm.CreateTunnel( + xfrm.XFRM_POLICY_OUT, xfrm.SrcDstSelector(local_inner, remote_inner), + local_outer, remote_outer, _TEST_OUT_SPI, xfrm_base._ALGO_CBC_AES_256, + xfrm_base._ALGO_HMAC_SHA1, None, output_mark, None, xfrm.MATCH_METHOD_ALL) + + write_sock = socket(net_test.GetAddressFamily(inner_version), SOCK_DGRAM, 0) + self.SelectInterface(write_sock, netid, "mark") + read_sock, _ = _CreateReceiveSock(inner_version) + + sock = write_sock if direction == xfrm.XFRM_POLICY_OUT else read_sock + func(inner_version, outer_version, u_netid, netid, local_inner, + remote_inner, local_outer, remote_outer, sock) + finally: + if test_output_mark_unset: + self.ClearDefaultNetwork() def ParamTestTunnelInput(self, inner_version, outer_version): self._TestTunnel(inner_version, outer_version, self._CheckTunnelInput, - xfrm.XFRM_POLICY_IN) + xfrm.XFRM_POLICY_IN, False) def ParamTestTunnelOutput(self, inner_version, outer_version): self._TestTunnel(inner_version, outer_version, self._CheckTunnelOutput, - xfrm.XFRM_POLICY_OUT) + xfrm.XFRM_POLICY_OUT, False) + + def ParamTestTunnelOutputNoSetMark(self, inner_version, outer_version): + self._TestTunnel(inner_version, outer_version, self._CheckTunnelOutput, + xfrm.XFRM_POLICY_OUT, True) @unittest.skipUnless(net_test.LINUX_VERSION >= (3, 18, 0), "VTI Unsupported") -- cgit v1.2.3 From d8cd1253af486f5129cb52fb503d616dffc92794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 5 Feb 2019 15:43:34 -0800 Subject: net-test: try to detect vsyscall=none uml and warn about it. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: 123953923 Signed-off-by: Maciej Żenczykowski Change-Id: Ib05019a1db10fbaae4bf4aa5000d8c617b9837d5 --- net/test/run_net_test.sh | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index 17b44d9..a81ad33 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -306,9 +306,32 @@ if [ "$ARCH" == "um" ]; then blockdevice=ubdar fi + exitcode=0 $KERNEL_BINARY >&2 umid=net_test mem=512M \ - $blockdevice=$SCRIPT_DIR/$ROOTFS $netconfig $consolemode $cmdline - exitcode=$? + $blockdevice=$SCRIPT_DIR/$ROOTFS $netconfig $consolemode $cmdline \ + || exitcode=$? + + # UML is kind of crazy in how guest syscalls work. It requires host kernel + # to not be in vsyscall=none mode. + if [[ "${exitcode}" != '0' ]]; then + { + # Hopefully one of these exists + cat /proc/config || : + zcat /proc/config.gz || : + cat "/boot/config-$(uname -r)" || : + zcat "/boot/config-$(uname -r).gz" || : + } 2>/dev/null \ + | egrep -q '^CONFIG_LEGACY_VSYSCALL_NONE=y' \ + && ! egrep -q '(^| )vsyscall=(native|emulate)( |$)' /proc/cmdline \ + && { + echo '-----=====-----' + echo 'If above you saw a "net_test.sh[1]: segfault at ..." followed by' + echo '"Kernel panic - not syncing: Attempted to kill init!" then please' + echo 'set "vsyscall=emulate" on *host* kernel command line.' + echo '(for example via GRUB_CMDLINE_LINUX in /etc/default/grub)' + echo '-----=====-----' + } + fi else # We boot into the filesystem image directly in all cases cmdline="$cmdline root=/dev/vda" -- cgit v1.2.3