diff options
-rw-r--r-- | net/test/BUILD.bazel | 22 | ||||
-rwxr-xr-x | net/test/all_tests.py | 8 | ||||
-rwxr-xr-x | net/test/all_tests.sh | 4 | ||||
-rw-r--r-- | net/test/iproute.py | 1 | ||||
-rwxr-xr-x | net/test/leak_test.py | 1 | ||||
-rw-r--r-- | net/test/namespace.py | 33 | ||||
-rwxr-xr-x | net/test/pf_key_test.py | 25 | ||||
-rwxr-xr-x | net/test/ping6_test.py | 48 | ||||
-rwxr-xr-x | net/test/run_net_test.sh | 11 | ||||
-rwxr-xr-x | net/test/sock_diag_test.py | 47 | ||||
-rwxr-xr-x | net/test/srcaddr_selection_test.py | 28 | ||||
-rw-r--r-- | net/test/tcp_test.py | 9 | ||||
-rwxr-xr-x | net/test/xfrm_algorithm_test.py | 107 | ||||
-rwxr-xr-x | net/test/xfrm_tunnel_test.py | 25 | ||||
-rw-r--r-- | test_mappings/BUILD.bazel | 64 |
15 files changed, 225 insertions, 208 deletions
diff --git a/net/test/BUILD.bazel b/net/test/BUILD.bazel deleted file mode 100644 index f0fc0f9..0000000 --- a/net/test/BUILD.bazel +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (C) 2024 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. - -load("@rules_pkg//pkg:mappings.bzl", "pkg_files") - -pkg_files( - name = "test_mappings", - srcs = ["TEST_MAPPING"], - prefix = package_name(), - visibility = ["//kernel/tests/test_mappings:__pkg__"], -) diff --git a/net/test/all_tests.py b/net/test/all_tests.py index 2e2d487..f6ff469 100755 --- a/net/test/all_tests.py +++ b/net/test/all_tests.py @@ -15,10 +15,13 @@ # limitations under the License. import importlib +import os import sys import unittest +import gki import namespace +import net_test all_test_modules = [ 'anycast_test', @@ -49,6 +52,11 @@ all_test_modules = [ def RunTests(modules_to_test): + print('Running on %s %s %s %s-%sbit%s%s' + % (os.uname()[0], os.uname()[2], net_test.LINUX_VERSION, os.uname()[4], + '64' if sys.maxsize > 0x7FFFFFFF else '32', + ' GKI' if gki.IS_GKI else '', ' GSI' if net_test.IS_GSI else ''), + file=sys.stderr) namespace.EnterNewNetworkNamespace() # First, run InjectTests on all modules, to ensure that any parameterized diff --git a/net/test/all_tests.sh b/net/test/all_tests.sh index aa63cdd..63576b0 100755 --- a/net/test/all_tests.sh +++ b/net/test/all_tests.sh @@ -18,10 +18,6 @@ readonly PREFIX="#####" readonly RETRIES=2 test_prefix= -# The tests currently have hundreds of ResourceWarnings that make it hard -# to see errors/failures. Disable this warning for now. -export PYTHONWARNINGS="ignore::ResourceWarning" - function checkArgOrExit() { if [[ $# -lt 2 ]]; then echo "Missing argument for option $1" >&2 diff --git a/net/test/iproute.py b/net/test/iproute.py index 6822fa5..307f836 100644 --- a/net/test/iproute.py +++ b/net/test/iproute.py @@ -596,6 +596,7 @@ class IPRoute(netlink.NetlinkSocket): if version == 6: self._WaitForAddress(sock, address, ifindex) + sock.close() def DelAddress(self, address, prefixlen, ifindex): self._Address(csocket.AddressVersion(address), diff --git a/net/test/leak_test.py b/net/test/leak_test.py index 54bbe73..9b7d2c6 100755 --- a/net/test/leak_test.py +++ b/net/test/leak_test.py @@ -45,6 +45,7 @@ class LeakTest(net_test.NetworkTest): data, addr = csocket.Recvfrom(s, 4096) self.assertEqual(b"", data) self.assertEqual(None, addr) + s.close() class ForceSocketBufferOptionTest(net_test.NetworkTest): diff --git a/net/test/namespace.py b/net/test/namespace.py index d16a1ad..7ebcbde 100644 --- a/net/test/namespace.py +++ b/net/test/namespace.py @@ -18,7 +18,6 @@ import ctypes import ctypes.util -import errno import os import socket import sys @@ -27,6 +26,8 @@ import net_test import sock_diag import tcp_test +# pylint: disable=bad-whitespace + # //include/linux/fs.h MNT_FORCE = 1 # Attempt to forcibily umount MNT_DETACH = 2 # Just detach from the tree @@ -66,6 +67,8 @@ CLONE_NEWUSER = 0x10000000 # New user namespace CLONE_NEWPID = 0x20000000 # New pid namespace CLONE_NEWNET = 0x40000000 # New network namespace +# pylint: enable=bad-whitespace + libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True) # See the relevant system call's man pages and: @@ -81,9 +84,9 @@ def Mount(src, tgt, fs, flags=MS_NODEV|MS_NOEXEC|MS_NOSUID|MS_RELATIME): ret = libc.mount(src.encode(), tgt.encode(), fs.encode() if fs else None, flags, None) if ret < 0: - errno = ctypes.get_errno() - raise OSError(errno, '%s mounting %s on %s (fs=%s flags=0x%x)' - % (os.strerror(errno), src, tgt, fs, flags)) + err = ctypes.get_errno() + raise OSError(err, '%s mounting %s on %s (fs=%s flags=0x%x)' + % (os.strerror(err), src, tgt, fs, flags)) def ReMountProc(): @@ -92,9 +95,9 @@ def ReMountProc(): def ReMountSys(): - libc.umount2(b'/sys/fs/cgroup', MNT_DETACH) # Ignore failure: might not be mounted - libc.umount2(b'/sys/fs/bpf', MNT_DETACH) # Ignore failure: might not be mounted - libc.umount2(b'/sys', MNT_DETACH) # Ignore failure: might not be mounted + libc.umount2(b'/sys/fs/cgroup', MNT_DETACH) # Ign. fail: might not be mounted + libc.umount2(b'/sys/fs/bpf', MNT_DETACH) # Ignore fail: might not be mounted + libc.umount2(b'/sys', MNT_DETACH) # Ignore fail: might not be mounted Mount('sysfs', '/sys', 'sysfs') Mount('bpf', '/sys/fs/bpf', 'bpf') Mount('cgroup2', '/sys/fs/cgroup', 'cgroup2') @@ -109,15 +112,15 @@ def SetHostname(s): hostname = s.encode() ret = libc.sethostname(hostname, len(hostname)) if ret < 0: - errno = ctypes.get_errno() - raise OSError(errno, '%s while sethostname(%s)' % (os.strerror(errno), s)) + err = ctypes.get_errno() + raise OSError(err, '%s while sethostname(%s)' % (os.strerror(err), s)) def UnShare(flags): ret = libc.unshare(flags) if ret < 0: - errno = ctypes.get_errno() - raise OSError(errno, '%s while unshare(0x%x)' % (os.strerror(errno), flags)) + err = ctypes.get_errno() + raise OSError(err, '%s while unshare(0x%x)' % (os.strerror(err), flags)) def DumpMounts(hdr): @@ -135,8 +138,6 @@ def DumpMounts(hdr): def EnterNewNetworkNamespace(): """Instantiate and transition into a fresh new network namespace.""" - sys.stdout.write('Creating clean namespace... ') - try: UnShare(CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWNET) except OSError as err: @@ -163,12 +164,12 @@ def EnterNewNetworkNamespace(): def HasEstablishedTcpSessionOnPort(port): sd = sock_diag.SockDiag() - sock_id = sd._EmptyInetDiagSockId() + sock_id = sd._EmptyInetDiagSockId() # pylint: disable=protected-access sock_id.sport = port states = 1 << tcp_test.TCP_ESTABLISHED - matches = sd.DumpAllInetSockets(socket.IPPROTO_TCP, b"", + matches = sd.DumpAllInetSockets(socket.IPPROTO_TCP, b'', sock_id=sock_id, states=states) - return len(matches) > 0 + return True if matches else False diff --git a/net/test/pf_key_test.py b/net/test/pf_key_test.py index 7791bd1..7a2b7ef 100755 --- a/net/test/pf_key_test.py +++ b/net/test/pf_key_test.py @@ -14,11 +14,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -# pylint: disable=g-bad-todo,g-bad-file-header,wildcard-import -from socket import * +import binascii +from socket import * # pylint: disable=g-importing-member,wildcard-import import unittest -import binascii import csocket import pf_key import xfrm @@ -30,11 +29,11 @@ AUTH_KEY = binascii.unhexlify("af442892cdcd0ef650e9c299f9a8436a") class PfKeyTest(unittest.TestCase): - def setUp(self): + def setUp(self): # pylint: disable=g-missing-super-call self.pf_key = pf_key.PfKey() self.xfrm = xfrm.Xfrm() - def tearDown(self): + def tearDown(self): # pylint: disable=g-missing-super-call self.pf_key.close() self.pf_key = None @@ -92,24 +91,24 @@ class PfKeyTest(unittest.TestCase): self.assertEqual(256, attrs6["XFRMA_ALG_AUTH_TRUNC"].key_len) if attrs4["XFRMA_ALG_AUTH_TRUNC"].trunc_len == 96: - missing4 = True + missing4 = True else: - self.assertEqual(128, attrs4["XFRMA_ALG_AUTH_TRUNC"].trunc_len) - missing4 = False + self.assertEqual(128, attrs4["XFRMA_ALG_AUTH_TRUNC"].trunc_len) + missing4 = False if attrs6["XFRMA_ALG_AUTH_TRUNC"].trunc_len == 96: - missing6 = True + missing6 = True else: - self.assertEqual(128, attrs6["XFRMA_ALG_AUTH_TRUNC"].trunc_len) - missing6 = False + self.assertEqual(128, attrs6["XFRMA_ALG_AUTH_TRUNC"].trunc_len) + missing6 = False self.pf_key.DelSa(src4, dst4, 0xdeadbeef, pf_key.SADB_TYPE_ESP) self.assertEqual(1, len(self.xfrm.DumpSaInfo())) self.pf_key.DelSa(src6, dst6, 0xbeefdead, pf_key.SADB_TYPE_ESP) self.assertEqual(0, len(self.xfrm.DumpSaInfo())) - if missing4 or missing6: - self.assertFalse("missing b8a72fd7c4e9 ANDROID: net: xfrm: make PF_KEY SHA256 use RFC-compliant truncation.") + self.assertFalse(missing4 or missing6, "missing b8a72fd7c4e9 ANDROID: net" + + ": xfrm: make PF_KEY SHA256 use RFC-compliant truncation.") if __name__ == "__main__": diff --git a/net/test/ping6_test.py b/net/test/ping6_test.py index af2e4c5..59021e2 100755 --- a/net/test/ping6_test.py +++ b/net/test/ping6_test.py @@ -21,18 +21,17 @@ import errno import os import posix import random -from socket import * # pylint: disable=wildcard-import +from socket import * # pylint: disable=g-importing-member,wildcard-import import struct import sys import threading import time import unittest -from scapy import all as scapy - import csocket import multinetwork_base import net_test +from scapy import all as scapy ICMP_ECHO = 8 @@ -116,12 +115,12 @@ class PingReplyThread(threading.Thread): packet) def SendPacketTooBig(self, packet): - src = packet.getlayer(scapy.IPv6).src - datalen = IPV6_MIN_MTU - ICMPV6_HEADER_LEN - self.SendPacket( - scapy.IPv6(src=self.INTERMEDIATE_IPV6, dst=src) / - scapy.ICMPv6PacketTooBig(mtu=self.LINK_MTU) / - bytes(packet)[:datalen]) + src = packet.getlayer(scapy.IPv6).src + datalen = IPV6_MIN_MTU - ICMPV6_HEADER_LEN + self.SendPacket( + scapy.IPv6(src=self.INTERMEDIATE_IPV6, dst=src) / + scapy.ICMPv6PacketTooBig(mtu=self.LINK_MTU) / + bytes(packet)[:datalen]) def IPv4Packet(self, ip): icmp = ip.getlayer(scapy.ICMP) @@ -184,7 +183,7 @@ class PingReplyThread(threading.Thread): packet = scapy.Ether(src=self._routermac, dst=self._mymac) / packet try: posix.write(self._tun.fileno(), bytes(packet)) - except Exception as e: + except Exception as e: # pylint: disable=broad-exception-caught if not self._stopped_flag: raise e @@ -217,15 +216,15 @@ class Ping6Test(multinetwork_base.MultiNetworkBaseTest): # that would cause tearDownClass not to be called and thus not clean up # routing configuration, breaking subsequent tests. Instead, just let these # tests fail. - _INTERVAL = 0.1 - _ATTEMPTS = 20 - for i in range(0, _ATTEMPTS): - for netid in cls.NETIDS: - if all(thread.IsStarted() for thread in list(cls.reply_threads.values())): + interval = 0.1 + attempts = 20 + for _ in range(attempts): + for _ in cls.NETIDS: + if all(thrd.IsStarted() for thrd in list(cls.reply_threads.values())): return - time.sleep(_INTERVAL) + time.sleep(interval) msg = "WARNING: reply threads not all started after %.1f seconds\n" % ( - _ATTEMPTS * _INTERVAL) + attempts * interval) sys.stderr.write(msg) @classmethod @@ -239,10 +238,10 @@ class Ping6Test(multinetwork_base.MultiNetworkBaseTest): cls.reply_threads = {} for netid in cls.NETIDS: cls.reply_threads[netid] = PingReplyThread( - cls.tuns[netid], - cls.MyMacAddress(netid), - cls.RouterMacAddress(netid), - cls._RouterAddress(netid, 6)) + cls.tuns[netid], + cls.MyMacAddress(netid), + cls.RouterMacAddress(netid), + cls._RouterAddress(netid, 6)) cls.reply_threads[netid].start() cls.WaitForReplyThreads() cls.netid = random.choice(cls.NETIDS) @@ -255,6 +254,7 @@ class Ping6Test(multinetwork_base.MultiNetworkBaseTest): super(Ping6Test, cls).tearDownClass() def setUp(self): + super(Ping6Test, self).setUp() self.ifname = self.GetInterfaceName(self.netid) self.ifindex = self.ifindices[self.netid] self.lladdr = net_test.GetLinkAddress(self.ifname, True) @@ -294,7 +294,7 @@ class Ping6Test(multinetwork_base.MultiNetworkBaseTest): # Check that the flow label is zero and that the scope ID is sane. self.assertEqual(flowlabel, 0) if addr.startswith("fe80::"): - self.assertTrue(scope_id in list(self.ifindices.values())) + self.assertIn(scope_id, list(self.ifindices.values())) else: self.assertEqual(0, scope_id) @@ -326,7 +326,7 @@ class Ping6Test(multinetwork_base.MultiNetworkBaseTest): # Check all the parameters except rxmem and txmem. expected[3] = actual[3] - # also do not check ref, it's always 2 on older kernels, but 1 for 'raw6' on 6.0+ + # Don't check ref, it's always 2 on old kernels, but 1 for 'raw6' on 6.0+ expected[5] = actual[5] if expected == actual: return @@ -735,7 +735,7 @@ class Ping6Test(multinetwork_base.MultiNetworkBaseTest): # that is not registered with the flow manager should return EINVAL... s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_FLOWINFO_SEND, 1) # ... but this doesn't work yet. - if False: + if False: # pylint: disable=using-constant-test self.assertRaisesErrno(errno.EINVAL, s.sendto, net_test.IPV6_PING, (net_test.IPV6_ADDR, 93, 0xdead, 0)) diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index 8d44cf3..8635eab 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh @@ -88,9 +88,6 @@ OPTIONS="$OPTIONS INIT_STACK_NONE" # These two break the flo kernel due to differences in -Werror on recent GCC. DISABLE_OPTIONS=" REISERFS_FS ANDROID_PMEM" -# Disable frame size warning on arm64. GCC 10 generates >1k stack frames. -DISABLE_OPTIONS="$DISABLE_OPTIONS FRAME_WARN" - # 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. # @@ -128,6 +125,11 @@ nowrite=1 nobuild=0 norun=0 +if [[ ! -f "${KERNEL_DIR}/Makefile" ]]; then + echo "No kernel Makefile found. Are you running this from a kernel directory?" + exit 1 +fi + KVER_MAJOR="$(sed -rn 's@^ *VERSION *= *([0-9]+)$@\1@p' < "${KERNEL_DIR}/Makefile")" KVER_MINOR="$(sed -rn 's@^ *PATCHLEVEL *= *([0-9]+)$@\1@p' < "${KERNEL_DIR}/Makefile")" KVER_LEVEL="$(sed -rn 's@^ *SUBLEVEL *= *([0-9]+)$@\1@p' < "${KERNEL_DIR}/Makefile")" @@ -303,6 +305,9 @@ if ((nobuild == 0)); then # Enable the kernel config options listed in $OPTIONS. $CONFIG_SCRIPT --file $CONFIG_FILE ${OPTIONS// / -e } + # Increase acceptable frame size. + $CONFIG_SCRIPT --file $CONFIG_FILE --set-val FRAME_WARN 3172 + # Disable the kernel config options listed in $DISABLE_OPTIONS. $CONFIG_SCRIPT --file $CONFIG_FILE ${DISABLE_OPTIONS// / -d } diff --git a/net/test/sock_diag_test.py b/net/test/sock_diag_test.py index 8aac69d..58e8f01 100755 --- a/net/test/sock_diag_test.py +++ b/net/test/sock_diag_test.py @@ -179,6 +179,9 @@ class SockDiagTest(SockDiagBaseTest): self.sock_diag.GetSockInfo(diag_req) # No errors? Good. + for sock in socketpair: + sock.close() + def CheckFindsAllMySockets(self, socktype, proto): """Tests that basic socket dumping works.""" self.socketpairs = self._CreateLotsOfSockets(socktype) @@ -220,6 +223,10 @@ class SockDiagTest(SockDiagBaseTest): info = self.sock_diag.GetSockInfo(req) self.assertSockInfoMatchesSocket(sock, info) + for socketpair in socketpairs: + for sock in socketpair: + sock.close() + def assertItemsEqual(self, expected, actual): try: super(SockDiagTest, self).assertItemsEqual(expected, actual) @@ -332,6 +339,15 @@ class SockDiagTest(SockDiagBaseTest): self.assertTrue(all(d in v4socks for d in diag_msgs)) self.assertTrue(all(d in v6socks for d in diag_msgs)) + for sock in unused_pair4: + sock.close() + + for sock in unused_pair6: + sock.close() + + for sock in pair5: + sock.close() + def testPortComparisonValidation(self): """Checks for a bug in validating port comparison bytecode. @@ -365,6 +381,9 @@ class SockDiagTest(SockDiagBaseTest): cookie = sock.getsockopt(net_test.SOL_SOCKET, net_test.SO_COOKIE, 8) self.assertEqual(diag_msg.id.cookie, cookie) + for sock in socketpair: + sock.close() + def testGetsockoptcookie(self): self.CheckSocketCookie(AF_INET, "127.0.0.1") self.CheckSocketCookie(AF_INET6, "::1") @@ -399,6 +418,8 @@ class SockDiagTest(SockDiagBaseTest): self.assertSockInfoMatchesSocket(s, self.sock_diag.GetSockInfo(req)) + s.close() + class SockDestroyTest(SockDiagBaseTest): """Tests that SOCK_DESTROY works correctly. @@ -538,6 +559,7 @@ class TcpRcvWindowTest(tcp_test.TcpBaseTest, SockDiagBaseTest): "Tcp rwnd of netid=%d, version=%d is not enough. " "Expect: %d, actual: %d" % (netid, version, self.RWND_SIZE, tcpInfo.tcpi_rcv_ssthresh)) + self.CloseSockets() def checkSynPacketWindowSize(self, version, netid): s = self.BuildSocket(version, net_test.TCPSocket, netid, "mark") @@ -595,6 +617,7 @@ class SockDestroyTcpTest(tcp_test.TcpBaseTest, SockDiagBaseTest): if state != tcp_test.TCP_LISTEN: msg = "Closing accepted IPv%d %s socket" % (version, statename) self.CheckRstOnClose(self.accepted, None, True, msg) + self.CloseSockets() def testTcpResets(self): """Checks that closing sockets in appropriate states sends a RST.""" @@ -613,6 +636,7 @@ class SockDestroyTcpTest(tcp_test.TcpBaseTest, SockDiagBaseTest): # Close the socket and check that it goes into FIN_WAIT1 and sends a FIN. net_test.EnableFinWait(self.accepted) self.accepted.close() + del self.accepted diag_req.states = 1 << tcp_test.TCP_FIN_WAIT1 diag_msg, attrs = self.sock_diag.GetSockInfo(diag_req) self.assertEqual(tcp_test.TCP_FIN_WAIT1, diag_msg.state) @@ -640,6 +664,8 @@ class SockDestroyTcpTest(tcp_test.TcpBaseTest, SockDiagBaseTest): for diag_msg, attrs in infos), "Expected to find FIN_WAIT2 socket in %s" % infos) + self.CloseSockets() + def FindChildSockets(self, s): """Finds the SYN_RECV child sockets of a given listening socket.""" d = self.sock_diag.FindSockDiagFromFd(self.s) @@ -703,11 +729,11 @@ class SockDestroyTcpTest(tcp_test.TcpBaseTest, SockDiagBaseTest): else: CloseChildren() CheckChildrenClosed() - self.s.close() else: CloseChildren() CloseParent(False) - self.s.close() + + self.CloseSockets() def testChildSockets(self): for version in [4, 5, 6]: @@ -726,6 +752,7 @@ class SockDestroyTcpTest(tcp_test.TcpBaseTest, SockDiagBaseTest): self.assertRaisesErrno(EINVAL, self.s.accept) # TODO: this should really return an error such as ENOTCONN... self.assertEqual(b"", self.s.recv(4096)) + self.CloseSockets() def testReadInterrupted(self): """Tests that read() is interrupted by SOCK_DESTROY.""" @@ -737,6 +764,7 @@ class SockDestroyTcpTest(tcp_test.TcpBaseTest, SockDiagBaseTest): self.assertRaisesErrno(EPIPE, self.accepted.send, b"foo") self.assertEqual(b"", self.accepted.recv(4096)) self.assertEqual(b"", self.accepted.recv(4096)) + self.CloseSockets() def testConnectInterrupted(self): """Tests that connect() is interrupted by SOCK_DESTROY.""" @@ -756,6 +784,7 @@ class SockDestroyTcpTest(tcp_test.TcpBaseTest, SockDiagBaseTest): self.ExpectPacketOn(self.netid, desc, syn) msg = "SOCK_DESTROY of socket in connect, expected no RST" self.ExpectNoPacketsOn(self.netid, msg) + s.close() class PollOnCloseTest(tcp_test.TcpBaseTest, SockDiagBaseTest): @@ -817,6 +846,7 @@ class PollOnCloseTest(tcp_test.TcpBaseTest, SockDiagBaseTest): lambda sock: self.BlockingPoll(sock, mask, expected, ignoremask), None) self.assertSocketErrors(ECONNABORTED) + self.CloseSockets() def CheckPollRst(self, mask, expected, ignoremask): """Interrupts a poll() by receiving a TCP RST.""" @@ -827,6 +857,7 @@ class PollOnCloseTest(tcp_test.TcpBaseTest, SockDiagBaseTest): lambda sock: self.BlockingPoll(sock, mask, expected, ignoremask), None) self.assertSocketErrors(ECONNRESET) + self.CloseSockets() def testReadPollRst(self): self.CheckPollRst(select.POLLIN, self.POLLIN_ERR_HUP, 0) @@ -899,6 +930,7 @@ class SockDestroyUdpTest(SockDiagBaseTest): s.connect((dst, 53)) self.sock_diag.CloseSocketFromFd(s) self.assertEqual((unspec, 0), s.getsockname()[:2]) + s.close() # Closing a socket bound to an IP address leaves the address as is. s = self.BuildSocket(version, net_test.UDPSocket, netid, "mark") @@ -908,6 +940,7 @@ class SockDestroyUdpTest(SockDiagBaseTest): port = s.getsockname()[1] self.sock_diag.CloseSocketFromFd(s) self.assertEqual((src, 0), s.getsockname()[:2]) + s.close() # Closing a socket bound to a port leaves the port as is. s = self.BuildSocket(version, net_test.UDPSocket, netid, "mark") @@ -915,6 +948,7 @@ class SockDestroyUdpTest(SockDiagBaseTest): s.connect((dst, 53)) self.sock_diag.CloseSocketFromFd(s) self.assertEqual((unspec, port), s.getsockname()[:2]) + s.close() # Closing a socket bound to IP address and port leaves both as is. s = self.BuildSocket(version, net_test.UDPSocket, netid, "mark") @@ -922,6 +956,7 @@ class SockDestroyUdpTest(SockDiagBaseTest): port = self.BindToRandomPort(s, src) self.sock_diag.CloseSocketFromFd(s) self.assertEqual((src, port), s.getsockname()[:2]) + s.close() def testReadInterrupted(self): """Tests that read() is interrupted by SOCK_DESTROY.""" @@ -945,6 +980,8 @@ class SockDestroyUdpTest(SockDiagBaseTest): self.CloseDuringBlockingCall(s, lambda sock: sock.recv(4096), ECONNABORTED) + s.close() + class SockDestroyPermissionTest(SockDiagBaseTest): def CheckPermissions(self, socktype): @@ -964,6 +1001,8 @@ class SockDestroyPermissionTest(SockDiagBaseTest): self.sock_diag.CloseSocketFromFd(s) self.assertRaises(ValueError, self.sock_diag.CloseSocketFromFd, s) + s.close() + def testUdp(self): self.CheckPermissions(SOCK_DGRAM) @@ -1050,6 +1089,9 @@ class SockDiagMarkTest(tcp_test.TcpBaseTest, SockDiagBaseTest): self.assertRaisesErrno(EPERM, self.FilterEstablishedSockets, 0xfff0000, 0xf0fed00) + s1.close() + s2.close() + @staticmethod def SetRandomMark(s): # Python doesn't like marks that don't fit into a signed int. @@ -1091,6 +1133,7 @@ class SockDiagMarkTest(tcp_test.TcpBaseTest, SockDiagBaseTest): self.assertSocketMarkIs(accepted, accepted_mark) self.assertSocketMarkIs(server, server_mark) + accepted.close() server.close() client.close() diff --git a/net/test/srcaddr_selection_test.py b/net/test/srcaddr_selection_test.py index f515c47..8c327c3 100755 --- a/net/test/srcaddr_selection_test.py +++ b/net/test/srcaddr_selection_test.py @@ -83,6 +83,7 @@ class IPv6SourceAddressSelectionTest(multinetwork_base.MultiNetworkBaseTest): s.connect((net_test.IPV6_ADDR, 123)) src_addr = s.getsockname()[0] self.assertTrue(src_addr) + s.close() return src_addr def assertAddressNotPresent(self, address): @@ -103,13 +104,19 @@ class IPv6SourceAddressSelectionTest(multinetwork_base.MultiNetworkBaseTest): def BindToAddress(self, address): s = net_test.UDPSocket(AF_INET6) - s.bind((address, 0, 0, 0)) + try: + s.bind((address, 0, 0, 0)) + finally: + s.close() def SendWithSourceAddress(self, address, netid, dest=net_test.IPV6_ADDR): pktinfo = multinetwork_base.MakePktInfo(6, address, 0) cmsgs = [(net_test.SOL_IPV6, IPV6_PKTINFO, pktinfo)] s = self.BuildSocket(6, net_test.UDPSocket, netid, "mark") - return csocket.Sendmsg(s, (dest, 53), b"Hello", cmsgs, 0) + try: + return csocket.Sendmsg(s, (dest, 53), b"Hello", cmsgs, 0) + finally: + s.close() def assertAddressUsable(self, address, netid): self.BindToAddress(address) @@ -132,7 +139,19 @@ class IPv6SourceAddressSelectionTest(multinetwork_base.MultiNetworkBaseTest): if not self.AddressIsTentative(address): return time.sleep(0.1) - raise AssertionError("%s did not complete DAD after 2 seconds") + raise AssertionError(f"{address} did not complete DAD after 2 seconds") + + def WaitForDadFailure(self, address): + # Address should be either deleted or set IFA_F_DADFAILED flag after DAD failure + for _ in range(20): + try: + ifa_msg = self.iproute.GetAddress(address)[0] + except OSError: + return + if ifa_msg.flags & iproute.IFA_F_DADFAILED: + return + time.sleep(0.1) + raise AssertionError(f"{address} did not complete DAD failure after 2 seconds") class MultiInterfaceSourceAddressSelectionTest(IPv6SourceAddressSelectionTest): @@ -279,6 +298,7 @@ class DadFailureTest(MultiInterfaceSourceAddressSelectionTest): self.SetUseOptimistic(self.test_ifname, 1) # Send a RA to start SLAAC and subsequent DAD. self.SendRA(self.test_netid, retranstimer=RETRANS_TIMER) + time.sleep(0.1) # Give the kernel time to notice our RA # Prove optimism and usability. self.assertAddressHasExpectedAttributes( self.test_ip, self.test_ifindex, iproute.IFA_F_OPTIMISTIC) @@ -293,7 +313,7 @@ class DadFailureTest(MultiInterfaceSourceAddressSelectionTest): scapy.ICMPv6ND_NA(tgt=self.test_ip, R=0, S=0, O=1) / scapy.ICMPv6NDOptDstLLAddr(lladdr=conflict_macaddr)) self.ReceiveEtherPacketOn(self.test_netid, dad_defense) - self.WaitForDad(self.test_lladdr) + self.WaitForDadFailure(self.test_ip) # The address should have failed DAD, and therefore no longer be usable. self.assertAddressNotUsable(self.test_ip, self.test_netid) diff --git a/net/test/tcp_test.py b/net/test/tcp_test.py index 5a073e6..f3ee291 100644 --- a/net/test/tcp_test.py +++ b/net/test/tcp_test.py @@ -40,9 +40,16 @@ TCP_NOT_YET_ACCEPTED = -1 class TcpBaseTest(multinetwork_base.MultiNetworkBaseTest): - def tearDown(self): + def CloseSockets(self): + if hasattr(self, "accepted"): + self.accepted.close() + del self.accepted if hasattr(self, "s"): self.s.close() + del self.s + + def tearDown(self): + self.CloseSockets() super(TcpBaseTest, self).tearDown() def OpenListenSocket(self, version, netid): diff --git a/net/test/xfrm_algorithm_test.py b/net/test/xfrm_algorithm_test.py index 8466953..5fa5352 100755 --- a/net/test/xfrm_algorithm_test.py +++ b/net/test/xfrm_algorithm_test.py @@ -14,17 +14,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -# pylint: disable=g-bad-todo,g-bad-file-header,wildcard-import -from errno import * # pylint: disable=wildcard-import -import os +from errno import * # pylint: disable=wildcard-import,g-importing-member import itertools -from scapy import all as scapy -from socket import * # pylint: disable=wildcard-import +import os +from socket import * # pylint: disable=wildcard-import,g-importing-member import threading +import time import unittest -import multinetwork_base import net_test +from scapy import all as scapy from tun_twister import TapTwister import util import xfrm @@ -87,14 +86,17 @@ AEAD_ALGOS = [ # 4 bytes (32 bits) of nonce. A fresh nonce value MUST be assigned for # each SA. RFC 7634 also specifies that ICV length must be 16 bytes. # ChaCha20-Poly1305 is enforced since kernel version 5.8 - (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_CHACHA20_POLY1305, 256+32, 16*8)), (5, 8)), + (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_CHACHA20_POLY1305, 256+32, 16*8)), + (5, 8)), ] + def GenerateKey(key_len): if key_len % 8 != 0: raise ValueError("Invalid key length in bits: " + str(key_len)) return os.urandom(key_len // 8) + # Does the kernel support this algorithm? def HaveAlgo(crypt_algo, auth_algo, aead_algo): try: @@ -133,28 +135,33 @@ def HaveAlgo(crypt_algo, auth_algo, aead_algo): # False. algoState = {} + def AlgoEnforcedOrEnabled(crypt, auth, aead, target_algo, target_kernel): if algoState.get(target_algo) is None: - algoState[target_algo] = net_test.LINUX_VERSION >= target_kernel or HaveAlgo( - crypt, auth, aead) + algoState[target_algo] = (net_test.LINUX_VERSION >= target_kernel + or HaveAlgo(crypt, auth, aead)) return algoState.get(target_algo) + # Return true if this algorithm should be enforced or is enabled on this kernel -def AuthEnforcedOrEnabled(authCase): - auth = authCase[0] +def AuthEnforcedOrEnabled(auth_case): + auth = auth_case[0] crypt = xfrm.XfrmAlgo((b"ecb(cipher_null)", 0)) - return AlgoEnforcedOrEnabled(crypt, auth, None, auth.name, authCase[1]) + return AlgoEnforcedOrEnabled(crypt, auth, None, auth.name, auth_case[1]) + # Return true if this algorithm should be enforced or is enabled on this kernel -def CryptEnforcedOrEnabled(cryptCase): - crypt = cryptCase[0] +def CryptEnforcedOrEnabled(crypt_case): + crypt = crypt_case[0] auth = xfrm.XfrmAlgoAuth((b"digest_null", 0, 0)) - return AlgoEnforcedOrEnabled(crypt, auth, None, crypt.name, cryptCase[1]) + return AlgoEnforcedOrEnabled(crypt, auth, None, crypt.name, crypt_case[1]) + # Return true if this algorithm should be enforced or is enabled on this kernel -def AeadEnforcedOrEnabled(aeadCase): - aead = aeadCase[0] - return AlgoEnforcedOrEnabled(None, None, aead, aead.name, aeadCase[1]) +def AeadEnforcedOrEnabled(aead_case): + aead = aead_case[0] + return AlgoEnforcedOrEnabled(None, None, aead, aead.name, aead_case[1]) + def InjectTests(): XfrmAlgorithmTest.InjectTests() @@ -163,66 +170,67 @@ def InjectTests(): class XfrmAlgorithmTest(xfrm_base.XfrmLazyTest): @classmethod def InjectTests(cls): - VERSIONS = (4, 6) - TYPES = (SOCK_DGRAM, SOCK_STREAM) + versions = (4, 6) + types = (SOCK_DGRAM, SOCK_STREAM) # Tests all combinations of auth & crypt. Mutually exclusive with aead. - param_list = itertools.product(VERSIONS, TYPES, AUTH_ALGOS, CRYPT_ALGOS, + 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. - param_list = itertools.product(VERSIONS, TYPES, [None], [None], AEAD_ALGOS) + param_list = itertools.product(versions, types, [None], [None], AEAD_ALGOS) util.InjectParameterizedTest(cls, param_list, cls.TestNameGenerator) @staticmethod - def TestNameGenerator(version, proto, authCase, cryptCase, aeadCase): + def TestNameGenerator(version, proto, auth_case, crypt_case, aead_case): # Produce a unique and readable name for each test. e.g. # testSocketPolicySimple_cbc-aes_256_hmac-sha512_512_256_IPv6_UDP param_string = "" - if cryptCase is not None: - crypt = cryptCase[0] + if crypt_case is not None: + crypt = crypt_case[0] param_string += "%s_%d_" % (crypt.name.decode(), crypt.key_len) - if authCase is not None: - auth = authCase[0] + if auth_case is not None: + auth = auth_case[0] param_string += "%s_%d_%d_" % (auth.name.decode(), auth.key_len, - auth.trunc_len) + auth.trunc_len) - if aeadCase is not None: - aead = aeadCase[0] + if aead_case is not None: + aead = aead_case[0] param_string += "%s_%d_%d_" % (aead.name.decode(), aead.key_len, - aead.icv_len) + aead.icv_len) param_string += "%s_%s" % ("IPv4" if version == 4 else "IPv6", - "UDP" if proto == SOCK_DGRAM else "TCP") + "UDP" if proto == SOCK_DGRAM else "TCP") return param_string - def ParamTestSocketPolicySimple(self, version, proto, authCase, cryptCase, aeadCase): + def ParamTestSocketPolicySimple(self, version, proto, auth_case, crypt_case, + aead_case): """Test two-way traffic using transport mode and socket policies.""" # Bypass the test if any algorithm going to be tested is not enforced # or enabled on this kernel - if authCase is not None and not AuthEnforcedOrEnabled(authCase): + if auth_case is not None and not AuthEnforcedOrEnabled(auth_case): return - if cryptCase is not None and not CryptEnforcedOrEnabled(cryptCase): + if crypt_case is not None and not CryptEnforcedOrEnabled(crypt_case): return - if aeadCase is not None and not AeadEnforcedOrEnabled(aeadCase): + if aead_case is not None and not AeadEnforcedOrEnabled(aead_case): return - auth = authCase[0] if authCase else None - crypt = cryptCase[0] if cryptCase else None - aead = aeadCase[0] if aeadCase else None + auth = auth_case[0] if auth_case else None + crypt = crypt_case[0] if crypt_case else None + aead = aead_case[0] if aead_case else None def AssertEncrypted(packet): # This gives a free pass to ICMP and ICMPv6 packets, which show up # nondeterministically in tests. self.assertEqual(None, - packet.getlayer(scapy.UDP), - "UDP packet sent in the clear") + packet.getlayer(scapy.UDP), + "UDP packet sent in the clear") self.assertEqual(None, - packet.getlayer(scapy.TCP), - "TCP packet sent in the clear") + packet.getlayer(scapy.TCP), + "TCP packet sent in the clear") # We create a pair of sockets, "left" and "right", that will talk to each # other using transport mode ESP. Because of TapTwister, both sockets @@ -342,7 +350,10 @@ class XfrmAlgorithmTest(xfrm_base.XfrmLazyTest): data = accepted.recv(2048) self.assertEqual(b"hello request", data) accepted.send(b"hello response") - except Exception as e: + time.sleep(0.1) + accepted.close() + except Exception as e: # pylint: disable=broad-exception-caught + nonlocal server_error server_error = e finally: sock.close() @@ -355,7 +366,8 @@ class XfrmAlgorithmTest(xfrm_base.XfrmLazyTest): self.assertEqual(client_port, peer[1]) self.assertEqual(b"hello request", data) sock.sendto(b"hello response", peer) - except Exception as e: + except Exception as e: # pylint: disable=broad-exception-caught + nonlocal server_error server_error = e finally: sock.close() @@ -377,7 +389,8 @@ class XfrmAlgorithmTest(xfrm_base.XfrmLazyTest): # Wait for server to be ready before attempting to connect. TCP retries # hide this problem, but UDP will fail outright if the server socket has # not bound when we send. - self.assertTrue(server_ready.wait(2.0), "Timed out waiting for server thread") + self.assertTrue(server_ready.wait(3.0), + "Timed out waiting for server thread") with TapTwister(fd=self.tuns[netid].fileno(), validator=AssertEncrypted): sock_left.connect((remote_addr, right_port)) @@ -385,7 +398,7 @@ class XfrmAlgorithmTest(xfrm_base.XfrmLazyTest): data = sock_left.recv(2048) self.assertEqual(b"hello response", data) sock_left.close() - server.join(timeout=2.0) + server.join(timeout=3.0) self.assertFalse(server.is_alive(), "Timed out waiting for server exit") if server_error: raise server_error diff --git a/net/test/xfrm_tunnel_test.py b/net/test/xfrm_tunnel_test.py index 715b559..3d0aa53 100755 --- a/net/test/xfrm_tunnel_test.py +++ b/net/test/xfrm_tunnel_test.py @@ -162,6 +162,7 @@ def _SendPacket(testInstance, netid, version, remote, remote_port): testInstance.SelectInterface(write_sock, netid, "mark") write_sock.sendto(net_test.UDP_PAYLOAD, (remote, remote_port)) local_port = write_sock.getsockname()[1] + write_sock.close() return local_port @@ -260,6 +261,9 @@ class XfrmTunnelTest(xfrm_base.XfrmLazyTest): 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) + + write_sock.close() + read_sock.close() finally: if test_output_mark_unset: self.ClearDefaultNetwork() @@ -731,14 +735,17 @@ 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) - if expect_fail: - self.assertRaisesErrno(EAGAIN, read_sock.recv, 4096) - else: - # Verify that the packet data and src are correct - data, src = read_sock.recvfrom(4096) - self.assertReceivedPacket(tunnel, sa_info) - self.assertEqual(net_test.UDP_PAYLOAD, data) - self.assertEqual((remote_inner, _TEST_REMOTE_PORT), src[:2]) + try: + if expect_fail: + self.assertRaisesErrno(EAGAIN, read_sock.recv, 4096) + else: + # Verify that the packet data and src are correct + data, src = read_sock.recvfrom(4096) + self.assertReceivedPacket(tunnel, sa_info) + self.assertEqual(net_test.UDP_PAYLOAD, data) + self.assertEqual((remote_inner, _TEST_REMOTE_PORT), src[:2]) + finally: + read_sock.close() def _CheckTunnelOutput(self, tunnel, inner_version, local_inner, remote_inner, sa_info=None): @@ -826,6 +833,8 @@ class XfrmTunnelBase(xfrm_base.XfrmBaseTest): # Check that the interface statistics recorded the inbound packet self.assertReceivedPacket(tunnel, tunnel.in_sa) + + read_sock.close() finally: # Swap the interface addresses to pretend we are the remote self._SwapInterfaceAddress( diff --git a/test_mappings/BUILD.bazel b/test_mappings/BUILD.bazel deleted file mode 100644 index fb6f994..0000000 --- a/test_mappings/BUILD.bazel +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (C) 2024 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. - -load("@rules_pkg//pkg:pkg.bzl", "pkg_zip") - -# Collect into a .zip file the TEST_MAPPING files for packages whose tests -# should be run in kernel presubmit and postsubmit testing. -# This rule is in this directory -# because these TEST_MAPPING files are used when testing kernel code and -# kernel/tests is the good place for that. -# -# Conventions: -# - Each package declares a :test_mappings pkg_files() target that contains -# all TEST_MAPPING files in that package. Avoid -# exports_files(["TEST_MAPPING"]) -# because it requires this target to poke into the implementation -# detail of each package, and it is less flexible if the package has -# more TEST_MAPPING files in the future. -# - Be careful about package boundaries, especially if you are using a -# glob() expression. See -# https://bazel.build/reference/be/functions#glob -# -# Example for a package with a single TEST_MAPPING file at the top level: -# -# pkg_files( -# name = "test_mappings", -# srcs = ["TEST_MAPPING"], -# prefix = package_name(), -# visibility = ["//kernel/tests/test_mappings:__pkg__"], -# ) -# -# Example for a package with multiple TEST_MAPPING files. Use `renames` -# to preserve the directory structure within the archive: -# -# _TEST_MAPPINGS = glob(["**/TEST_MAPPING"]) -# pkg_files( -# name = "test_mappings", -# srcs = _TEST_MAPPINGS, -# prefix = package_name(), -# renames = {file: file for file in _TEST_MAPPINGS}, -# visibility = ["//kernel/tests/test_mappings:__pkg__"], -# ) -pkg_zip( - name = "test_mappings_zip", - srcs = [ - "//common:test_mappings", - "//external/zlib:test_mappings", - "//kernel/tests/net/test:test_mappings", - "//prebuilts/rust:test_mappings", - ], - out = "test_mappings.zip", - visibility = ["//visibility:public"], -) |