From: Lorenzo Colitti Date: Thu, 25 Feb 2016 13:37:48 +0000 (+0900) Subject: Delete net_test from system/extras now that it's in kernel/tests. X-Git-Tag: android-x86-8.1-r1~68^2~360^2~13^2~3^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=ad77956e91e23fa47440275c6d40a9a5369bc4a2;p=android-x86%2Fsystem-extras.git Delete net_test from system/extras now that it's in kernel/tests. The directories are identical. This commit was generated by: $ diff -u system/extras/tests/net_test kernel/tests/net/test/ $ cd system/extras/tests/net_test $ git rm * Bug: 27228782 Change-Id: I08ca870049c8e91bb0848314bf647f6e1ad2dd09 --- diff --git a/tests/net_test/README b/tests/net_test/README deleted file mode 100644 index f45c3d56..00000000 --- a/tests/net_test/README +++ /dev/null @@ -1,77 +0,0 @@ - net_test v0.1 - ============= - -A simple framework for blackbox testing of kernel networking code. - - -Why use it? -=========== - -- Fast test / boot cycle. -- Access to host filesystem and networking via L2 bridging. -- Full Linux userland including Python, etc. -- Kernel bugs don't crash the system. - - -How to use it -============= - -cd -path/to/net_test/run_net_test.sh - -where is the name of a test binary in the net_test directory. This can -be an x86 binary, a shell script, a Python script. etc. - - -How it works -============ - -net_test compiles the kernel to a user-mode linux binary, which runs as a -process on the host machine. It runs the binary to start a Linux "virtual -machine" whose root filesystem is the supplied Debian disk image. The machine -boots, mounts the root filesystem read-only, runs the specified test from init, and then drops to a shell. - - -Access to host filesystem -========================= - -The VM mounts the host filesystem at /host, so the test can be modified and -re-run without rebooting the VM. - - -Access to host networking -========================= - -Access to host networking is provided by tap interfaces. On the host, the -interfaces are named TAP0, TAP1, etc., where is the first -10 characters of the username running net_test. (10 characters because -IFNAMSIZ = 16). On the guest, they are named eth0, eth1, etc. - -net_test does not do any networking setup beyond creating the tap interfaces. -IP connectivity can be provided on the host side by setting up a DHCP server -and NAT, sending IPv6 router advertisements, etc. By default, the VM has IPv6 -privacy addresses disabled, so its IPv6 addresses can be predicted using a tool -such as ipv6calc. - -The provided filesystem contains a DHCPv4 client and simple networking -utilities such as ping[6], traceroute[6], and wget. - -The number of tap interfaces is currently hardcoded to two. To change this -number, modify run_net_test.sh. - - -Logging into the VM, installing packages, etc. -============================================== - -net_test mounts the root filesystem read-only, and runs the test from init, but -since the filesystem contains a full Linux userland, it's possible to boot into -userland and modify the filesystem, for example to install packages using -apt-get install. Log in as root with no password. By default, the filesystem is -configured to perform DHCPv4 on eth0 and listen to RAs. - - -Bugs -==== - -Since the test mounts the filesystem read-only, tests cannot modify -/etc/resolv.conf and the system resolver is hardcoded to 8.8.8.8. diff --git a/tests/net_test/all_tests.sh b/tests/net_test/all_tests.sh deleted file mode 100755 index ce147d35..00000000 --- a/tests/net_test/all_tests.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash - -# Copyright 2014 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. - -readonly PREFIX="#####" - -function maybePlural() { - # $1 = integer to use for plural check - # $2 = singular string - # $3 = plural string - if [ $1 -ne 1 ]; then - echo "$3" - else - echo "$2" - fi -} - - -readonly tests=$(find . -name '*_test.py' -type f -executable) -readonly count=$(echo $tests | wc -w) -echo "$PREFIX Found $count $(maybePlural $count test tests)." - -exit_code=0 - -i=0 -for test in $tests; do - i=$((i + 1)) - echo "" - echo "$PREFIX $test ($i/$count)" - echo "" - $test || exit_code=$(( exit_code + 1 )) - echo "" -done - -echo "$PREFIX $exit_code failed $(maybePlural $exit_code test tests)." -exit $exit_code diff --git a/tests/net_test/anycast_test.py b/tests/net_test/anycast_test.py deleted file mode 100755 index 82130dbb..00000000 --- a/tests/net_test/anycast_test.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2014 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 os -from socket import * # pylint: disable=wildcard-import -import threading -import time -import unittest - -import cstruct -import multinetwork_base -import net_test - -IPV6_JOIN_ANYCAST = 27 -IPV6_LEAVE_ANYCAST = 28 - -# pylint: disable=invalid-name -IPv6Mreq = cstruct.Struct("IPv6Mreq", "=16si", "multiaddr ifindex") - - -_CLOSE_HUNG = False - - -def CauseOops(): - open("/proc/sysrq-trigger", "w").write("c") - - -class CloseFileDescriptorThread(threading.Thread): - - def __init__(self, fd): - super(CloseFileDescriptorThread, self).__init__() - self.daemon = True - self._fd = fd - self.finished = False - - def run(self): - global _CLOSE_HUNG - _CLOSE_HUNG = True - self._fd.close() - _CLOSE_HUNG = False - self.finished = True - - -class AnycastTest(multinetwork_base.MultiNetworkBaseTest): - """Tests for IPv6 anycast addresses. - - Relevant kernel commits: - upstream net-next: - 381f4dc ipv6: clean up anycast when an interface is destroyed - - android-3.10: - 86a47ad ipv6: clean up anycast when an interface is destroyed - """ - _TEST_NETID = 123 - - def AnycastSetsockopt(self, s, is_add, netid, addr): - ifindex = self.ifindices[netid] - self.assertTrue(ifindex) - ipv6mreq = IPv6Mreq((addr, ifindex)) - option = IPV6_JOIN_ANYCAST if is_add else IPV6_LEAVE_ANYCAST - s.setsockopt(IPPROTO_IPV6, option, ipv6mreq.Pack()) - - def testAnycastNetdeviceUnregister(self): - netid = self._TEST_NETID - self.assertNotIn(netid, self.tuns) - self.tuns[netid] = self.CreateTunInterface(netid) - self.SendRA(netid) - iface = self.GetInterfaceName(netid) - self.ifindices[netid] = net_test.GetInterfaceIndex(iface) - - s = socket(AF_INET6, SOCK_DGRAM, 0) - addr = self.MyAddress(6, netid) - self.assertIsNotNone(addr) - - addr = inet_pton(AF_INET6, addr) - addr = addr[:8] + os.urandom(8) - self.AnycastSetsockopt(s, True, netid, addr) - - # Close the tun fd in the background. - # This will hang if the kernel has the bug. - thread = CloseFileDescriptorThread(self.tuns[netid]) - thread.start() - time.sleep(0.1) - - # Make teardown work. - del self.tuns[netid] - # Check that the interface is gone. - try: - self.assertIsNone(self.MyAddress(6, netid)) - finally: - # This doesn't seem to help, but still. - self.AnycastSetsockopt(s, False, netid, addr) - self.assertTrue(thread.finished) - - -if __name__ == "__main__": - unittest.main(exit=False) - if _CLOSE_HUNG: - time.sleep(3) - CauseOops() diff --git a/tests/net_test/csocket.py b/tests/net_test/csocket.py deleted file mode 100644 index 5dc495cc..00000000 --- a/tests/net_test/csocket.py +++ /dev/null @@ -1,182 +0,0 @@ -# Copyright 2014 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. - -"""Python wrapper for C socket calls and data structures.""" - -import ctypes -import ctypes.util -import os -import socket -import struct - -import cstruct - - -# Data structures. -# These aren't constants, they're classes. So, pylint: disable=invalid-name -CMsgHdr = cstruct.Struct("cmsghdr", "@Lii", "len level type") -Iovec = cstruct.Struct("iovec", "@LL", "base len") -MsgHdr = cstruct.Struct("msghdr", "@LLLLLLi", - "name namelen iov iovlen control msg_controllen flags") -SockaddrIn = cstruct.Struct("sockaddr_in", "=HH4sxxxxxxxx", "family port addr") -SockaddrIn6 = cstruct.Struct("sockaddr_in6", "=HHI16sI", - "family port flowinfo addr scope_id") - -# Constants. -CMSG_ALIGNTO = struct.calcsize("@L") # The kernel defines this as sizeof(long). -MSG_CONFIRM = 0X800 - -# Find the C library. -libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True) - - -def PaddedLength(length): - return CMSG_ALIGNTO * ((length / CMSG_ALIGNTO) + (length % CMSG_ALIGNTO != 0)) - - -def MaybeRaiseSocketError(ret): - if ret < 0: - errno = ctypes.get_errno() - raise socket.error(errno, os.strerror(errno)) - - -def Sockaddr(addr): - if ":" in addr[0]: - family = socket.AF_INET6 - if len(addr) == 4: - addr, port, flowinfo, scope_id = addr - else: - (addr, port), flowinfo, scope_id = addr, 0, 0 - addr = socket.inet_pton(family, addr) - return SockaddrIn6((family, socket.ntohs(port), socket.ntohl(flowinfo), - addr, scope_id)) - else: - family = socket.AF_INET - addr, port = addr - addr = socket.inet_pton(family, addr) - return SockaddrIn((family, socket.ntohs(port), addr)) - - -def _MakeMsgControl(optlist): - """Creates a msg_control blob from a list of cmsg attributes. - - Takes a list of cmsg attributes. Each attribute is a tuple of: - - level: An integer, e.g., SOL_IPV6. - - type: An integer, the option identifier, e.g., IPV6_HOPLIMIT. - - data: The option data. This is either a string or an integer. If it's an - integer it will be written as an unsigned integer in host byte order. If - it's a string, it's used as is. - - Data is padded to an integer multiple of CMSG_ALIGNTO. - - Args: - optlist: A list of tuples describing cmsg options. - - Returns: - A string, a binary blob usable as the control data for a sendmsg call. - - Raises: - TypeError: Option data is neither an integer nor a string. - """ - msg_control = "" - - for i, opt in enumerate(optlist): - msg_level, msg_type, data = opt - if isinstance(data, int): - data = struct.pack("=I", data) - elif not isinstance(data, str): - raise TypeError("unknown data type for opt %i: %s" % (i, type(data))) - - datalen = len(data) - msg_len = len(CMsgHdr) + datalen - padding = "\x00" * (PaddedLength(datalen) - datalen) - msg_control += CMsgHdr((msg_len, msg_level, msg_type)).Pack() - msg_control += data + padding - - return msg_control - - -def Bind(s, to): - """Python wrapper for connect.""" - ret = libc.bind(s.fileno(), to.CPointer(), len(to)) - MaybeRaiseSocketError(ret) - return ret - - -def Connect(s, to): - """Python wrapper for connect.""" - ret = libc.connect(s.fileno(), to.CPointer(), len(to)) - MaybeRaiseSocketError(ret) - return ret - - -def Sendmsg(s, to, data, control, flags): - """Python wrapper for sendmsg. - - Args: - s: A Python socket object. Becomes sockfd. - to: An address tuple, or a SockaddrIn[6] struct. Becomes msg->msg_name. - data: A string, the data to write. Goes into msg->msg_iov. - control: A list of cmsg options. Becomes msg->msg_control. - flags: An integer. Becomes msg->msg_flags. - - Returns: - If sendmsg succeeds, returns the number of bytes written as an integer. - - Raises: - socket.error: If sendmsg fails. - """ - # Create ctypes buffers and pointers from our structures. We need to hang on - # to the underlying Python objects, because we don't want them to be garbage - # collected and freed while we have C pointers to them. - - # Convert the destination address into a struct sockaddr. - if to: - if isinstance(to, tuple): - to = Sockaddr(to) - msg_name = to.CPointer() - msg_namelen = len(to) - else: - msg_name = 0 - msg_namelen = 0 - - # Convert the data to a data buffer and a struct iovec pointing at it. - if data: - databuf = ctypes.create_string_buffer(data) - iov = Iovec((ctypes.addressof(databuf), len(data))) - msg_iov = iov.CPointer() - msg_iovlen = 1 - else: - msg_iov = 0 - msg_iovlen = 0 - - # Marshal the cmsg options. - if control: - control = _MakeMsgControl(control) - controlbuf = ctypes.create_string_buffer(control) - msg_control = ctypes.addressof(controlbuf) - msg_controllen = len(control) - else: - msg_control = 0 - msg_controllen = 0 - - # Assemble the struct msghdr. - msghdr = MsgHdr((msg_name, msg_namelen, msg_iov, msg_iovlen, - msg_control, msg_controllen, flags)).Pack() - - # Call sendmsg. - ret = libc.sendmsg(s.fileno(), msghdr, 0) - MaybeRaiseSocketError(ret) - - return ret diff --git a/tests/net_test/cstruct.py b/tests/net_test/cstruct.py deleted file mode 100644 index 91cd72ec..00000000 --- a/tests/net_test/cstruct.py +++ /dev/null @@ -1,194 +0,0 @@ -# Copyright 2014 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. - -"""A simple module for declaring C-like structures. - -Example usage: - ->>> # Declare a struct type by specifying name, field formats and field names. -... # Field formats are the same as those used in the struct module. -... import cstruct ->>> NLMsgHdr = cstruct.Struct("NLMsgHdr", "=LHHLL", "length type flags seq pid") ->>> ->>> ->>> # Create instances from tuples or raw bytes. Data past the end is ignored. -... n1 = NLMsgHdr((44, 32, 0x2, 0, 491)) ->>> print n1 -NLMsgHdr(length=44, type=32, flags=2, seq=0, pid=491) ->>> ->>> n2 = NLMsgHdr("\x2c\x00\x00\x00\x21\x00\x02\x00" -... "\x00\x00\x00\x00\xfe\x01\x00\x00" + "junk at end") ->>> print n2 -NLMsgHdr(length=44, type=33, flags=2, seq=0, pid=510) ->>> ->>> # Serialize to raw bytes. -... print n1.Pack().encode("hex") -2c0000002000020000000000eb010000 ->>> ->>> # Parse the beginning of a byte stream as a struct, and return the struct -... # and the remainder of the stream for further reading. -... data = ("\x2c\x00\x00\x00\x21\x00\x02\x00" -... "\x00\x00\x00\x00\xfe\x01\x00\x00" -... "more data") ->>> cstruct.Read(data, NLMsgHdr) -(NLMsgHdr(length=44, type=33, flags=2, seq=0, pid=510), 'more data') ->>> -""" - -import ctypes -import string -import struct - - -def CalcNumElements(fmt): - size = struct.calcsize(fmt) - elements = struct.unpack(fmt, "\x00" * size) - return len(elements) - - -def Struct(name, fmt, fieldnames, substructs={}): - """Function that returns struct classes.""" - - class Meta(type): - - def __len__(cls): - return cls._length - - def __init__(cls, unused_name, unused_bases, namespace): - # Make the class object have the name that's passed in. - type.__init__(cls, namespace["_name"], unused_bases, namespace) - - class CStruct(object): - """Class representing a C-like structure.""" - - __metaclass__ = Meta - - # Name of the struct. - _name = name - # List of field names. - _fieldnames = fieldnames - # Dict mapping field indices to nested struct classes. - _nested = {} - - if isinstance(_fieldnames, str): - _fieldnames = _fieldnames.split(" ") - - # Parse fmt into _format, converting any S format characters to "XXs", - # where XX is the length of the struct type's packed representation. - _format = "" - laststructindex = 0 - for i in xrange(len(fmt)): - if fmt[i] == "S": - # Nested struct. Record the index in our struct it should go into. - index = CalcNumElements(fmt[:i]) - _nested[index] = substructs[laststructindex] - laststructindex += 1 - _format += "%ds" % len(_nested[index]) - else: - # Standard struct format character. - _format += fmt[i] - - _length = struct.calcsize(_format) - - def _SetValues(self, values): - super(CStruct, self).__setattr__("_values", list(values)) - - def _Parse(self, data): - data = data[:self._length] - values = list(struct.unpack(self._format, data)) - for index, value in enumerate(values): - if isinstance(value, str) and index in self._nested: - values[index] = self._nested[index](value) - self._SetValues(values) - - def __init__(self, values): - # Initializing from a string. - if isinstance(values, str): - if len(values) < self._length: - raise TypeError("%s requires string of length %d, got %d" % - (self._name, self._length, len(values))) - self._Parse(values) - else: - # Initializing from a tuple. - if len(values) != len(self._fieldnames): - raise TypeError("%s has exactly %d fieldnames (%d given)" % - (self._name, len(self._fieldnames), len(values))) - self._SetValues(values) - - def _FieldIndex(self, attr): - try: - return self._fieldnames.index(attr) - except ValueError: - raise AttributeError("'%s' has no attribute '%s'" % - (self._name, attr)) - - def __getattr__(self, name): - return self._values[self._FieldIndex(name)] - - def __setattr__(self, name, value): - self._values[self._FieldIndex(name)] = value - - @classmethod - def __len__(cls): - return cls._length - - def __ne__(self, other): - return not self.__eq__(other) - - def __eq__(self, other): - return (isinstance(other, self.__class__) and - self._name == other._name and - self._fieldnames == other._fieldnames and - self._values == other._values) - - @staticmethod - def _MaybePackStruct(value): - if hasattr(value, "__metaclass__"):# and value.__metaclass__ == Meta: - return value.Pack() - else: - return value - - def Pack(self): - values = [self._MaybePackStruct(v) for v in self._values] - return struct.pack(self._format, *values) - - def __str__(self): - def FieldDesc(index, name, value): - if isinstance(value, str) and any( - c not in string.printable for c in value): - value = value.encode("hex") - return "%s=%s" % (name, value) - - descriptions = [ - FieldDesc(i, n, v) for i, (n, v) in - enumerate(zip(self._fieldnames, self._values))] - - return "%s(%s)" % (self._name, ", ".join(descriptions)) - - def __repr__(self): - return str(self) - - def CPointer(self): - """Returns a C pointer to the serialized structure.""" - buf = ctypes.create_string_buffer(self.Pack()) - # Store the C buffer in the object so it doesn't get garbage collected. - super(CStruct, self).__setattr__("_buffer", buf) - return ctypes.addressof(self._buffer) - - return CStruct - - -def Read(data, struct_type): - length = len(struct_type) - return struct_type(data), data[length:] diff --git a/tests/net_test/cstruct_test.py b/tests/net_test/cstruct_test.py deleted file mode 100755 index 2d5a4081..00000000 --- a/tests/net_test/cstruct_test.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2016 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 cstruct - - -# These aren't constants, they're classes. So, pylint: disable=invalid-name -TestStructA = cstruct.Struct("TestStructA", "=BI", "byte1 int2") -TestStructB = cstruct.Struct("TestStructB", "=BI", "byte1 int2") - - -class CstructTest(unittest.TestCase): - - def CheckEquals(self, a, b): - self.assertEquals(a, b) - self.assertEquals(b, a) - assert a == b - assert b == a - assert not (a != b) # pylint: disable=g-comparison-negation,superfluous-parens - assert not (b != a) # pylint: disable=g-comparison-negation,superfluous-parens - - def CheckNotEquals(self, a, b): - self.assertNotEquals(a, b) - self.assertNotEquals(b, a) - assert a != b - assert b != a - assert not (a == b) # pylint: disable=g-comparison-negation,superfluous-parens - assert not (b == a) # pylint: disable=g-comparison-negation,superfluous-parens - - def testEqAndNe(self): - a1 = TestStructA((1, 2)) - a2 = TestStructA((2, 3)) - a3 = TestStructA((1, 2)) - b = TestStructB((1, 2)) - self.CheckNotEquals(a1, b) - self.CheckNotEquals(a2, b) - self.CheckNotEquals(a1, a2) - self.CheckNotEquals(a2, a3) - for i in [a1, a2, a3, b]: - self.CheckEquals(i, i) - self.CheckEquals(a1, a3) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/net_test/forwarding_test.py b/tests/net_test/forwarding_test.py deleted file mode 100755 index 185e477c..00000000 --- a/tests/net_test/forwarding_test.py +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2015 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 random -import unittest - -from socket import * - -import iproute -import multinetwork_base -import net_test -import packets - - -class ForwardingTest(multinetwork_base.MultiNetworkBaseTest): - - TCP_TIME_WAIT = 6 - - def ForwardBetweenInterfaces(self, enabled, iface1, iface2): - for iif, oif in itertools.permutations([iface1, iface2]): - self.iproute.IifRule(6, enabled, self.GetInterfaceName(iif), - self._TableForNetid(oif), self.PRIORITY_IIF) - - def setUp(self): - self.SetSysctl("/proc/sys/net/ipv6/conf/all/forwarding", 1) - - def tearDown(self): - self.SetSysctl("/proc/sys/net/ipv6/conf/all/forwarding", 0) - - def CheckForwardingCrash(self, netid, iface1, iface2): - listenport = packets.RandomPort() - listensocket = net_test.IPv6TCPSocket() - listensocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) - listensocket.bind(("::", listenport)) - listensocket.listen(100) - self.SetSocketMark(listensocket, netid) - - version = 6 - remoteaddr = self.GetRemoteAddress(version) - myaddr = self.MyAddress(version, netid) - - desc, syn = packets.SYN(listenport, version, remoteaddr, myaddr) - synack_desc, synack = packets.SYNACK(version, myaddr, remoteaddr, syn) - msg = "Sent %s, expected %s" % (desc, synack_desc) - reply = self._ReceiveAndExpectResponse(netid, syn, synack, msg) - - establishing_ack = packets.ACK(version, remoteaddr, myaddr, reply)[1] - self.ReceivePacketOn(netid, establishing_ack) - accepted, peer = listensocket.accept() - remoteport = accepted.getpeername()[1] - - accepted.close() - desc, fin = packets.FIN(version, myaddr, remoteaddr, establishing_ack) - self.ExpectPacketOn(netid, msg + ": expecting %s after close" % desc, fin) - - desc, finack = packets.FIN(version, remoteaddr, myaddr, fin) - self.ReceivePacketOn(netid, finack) - - # Check our socket is now in TIME_WAIT. - sockets = self.ReadProcNetSocket("tcp6") - mysrc = "%s:%04X" % (net_test.FormatSockStatAddress(myaddr), listenport) - mydst = "%s:%04X" % (net_test.FormatSockStatAddress(remoteaddr), remoteport) - state = None - sockets = [s for s in sockets if s[0] == mysrc and s[1] == mydst] - self.assertEquals(1, len(sockets)) - self.assertEquals("%02X" % self.TCP_TIME_WAIT, sockets[0][2]) - - # Remove our IP address. - try: - self.iproute.DelAddress(myaddr, 64, self.ifindices[netid]) - - self.ReceivePacketOn(iface1, finack) - self.ReceivePacketOn(iface1, establishing_ack) - self.ReceivePacketOn(iface1, establishing_ack) - # No crashes? Good. - - finally: - # Put back our IP address. - self.SendRA(netid) - listensocket.close() - - def testCrash(self): - # Run the test a few times as it doesn't crash/hang the first time. - for netids in itertools.permutations(self.tuns): - # Pick an interface to send traffic on and two to forward traffic between. - netid, iface1, iface2 = random.sample(netids, 3) - self.ForwardBetweenInterfaces(True, iface1, iface2) - try: - self.CheckForwardingCrash(netid, iface1, iface2) - finally: - self.ForwardBetweenInterfaces(False, iface1, iface2) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/net_test/iproute.py b/tests/net_test/iproute.py deleted file mode 100644 index 91014793..00000000 --- a/tests/net_test/iproute.py +++ /dev/null @@ -1,548 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2014 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. - -"""Partial Python implementation of iproute functionality.""" - -# pylint: disable=g-bad-todo - -import errno -import os -import socket -import struct -import sys - -import cstruct -import netlink - - -### Base netlink constants. See include/uapi/linux/netlink.h. -NETLINK_ROUTE = 0 - -# Request constants. -NLM_F_REQUEST = 1 -NLM_F_ACK = 4 -NLM_F_REPLACE = 0x100 -NLM_F_EXCL = 0x200 -NLM_F_CREATE = 0x400 -NLM_F_DUMP = 0x300 - -# Message types. -NLMSG_ERROR = 2 -NLMSG_DONE = 3 - -# Data structure formats. -# These aren't constants, they're classes. So, pylint: disable=invalid-name -NLMsgHdr = cstruct.Struct("NLMsgHdr", "=LHHLL", "length type flags seq pid") -NLMsgErr = cstruct.Struct("NLMsgErr", "=i", "error") -NLAttr = cstruct.Struct("NLAttr", "=HH", "nla_len nla_type") - -# Alignment / padding. -NLA_ALIGNTO = 4 - - -### rtnetlink constants. See include/uapi/linux/rtnetlink.h. -# Message types. -RTM_NEWLINK = 16 -RTM_DELLINK = 17 -RTM_GETLINK = 18 -RTM_NEWADDR = 20 -RTM_DELADDR = 21 -RTM_GETADDR = 22 -RTM_NEWROUTE = 24 -RTM_DELROUTE = 25 -RTM_GETROUTE = 26 -RTM_NEWNEIGH = 28 -RTM_DELNEIGH = 29 -RTM_GETNEIGH = 30 -RTM_NEWRULE = 32 -RTM_DELRULE = 33 -RTM_GETRULE = 34 - -# Routing message type values (rtm_type). -RTN_UNSPEC = 0 -RTN_UNICAST = 1 -RTN_UNREACHABLE = 7 - -# Routing protocol values (rtm_protocol). -RTPROT_UNSPEC = 0 -RTPROT_STATIC = 4 - -# Route scope values (rtm_scope). -RT_SCOPE_UNIVERSE = 0 -RT_SCOPE_LINK = 253 - -# Named routing tables. -RT_TABLE_UNSPEC = 0 - -# Routing attributes. -RTA_DST = 1 -RTA_SRC = 2 -RTA_OIF = 4 -RTA_GATEWAY = 5 -RTA_PRIORITY = 6 -RTA_PREFSRC = 7 -RTA_METRICS = 8 -RTA_CACHEINFO = 12 -RTA_TABLE = 15 -RTA_MARK = 16 -RTA_UID = 18 - -# Route metric attributes. -RTAX_MTU = 2 -RTAX_HOPLIMIT = 10 - -# Data structure formats. -IfinfoMsg = cstruct.Struct( - "IfinfoMsg", "=BBHiII", "family pad type index flags change") -RTMsg = cstruct.Struct( - "RTMsg", "=BBBBBBBBI", - "family dst_len src_len tos table protocol scope type flags") -RTACacheinfo = cstruct.Struct( - "RTACacheinfo", "=IIiiI", "clntref lastuse expires error used") - - -### Interface address constants. See include/uapi/linux/if_addr.h. -# Interface address attributes. -IFA_ADDRESS = 1 -IFA_LOCAL = 2 -IFA_LABEL = 3 -IFA_CACHEINFO = 6 - -# Address flags. -IFA_F_SECONDARY = 0x01 -IFA_F_TEMPORARY = IFA_F_SECONDARY -IFA_F_NODAD = 0x02 -IFA_F_OPTIMISTIC = 0x04 -IFA_F_DADFAILED = 0x08 -IFA_F_HOMEADDRESS = 0x10 -IFA_F_DEPRECATED = 0x20 -IFA_F_TENTATIVE = 0x40 -IFA_F_PERMANENT = 0x80 - -# Data structure formats. -IfAddrMsg = cstruct.Struct( - "IfAddrMsg", "=BBBBI", - "family prefixlen flags scope index") -IFACacheinfo = cstruct.Struct( - "IFACacheinfo", "=IIII", "prefered valid cstamp tstamp") -NDACacheinfo = cstruct.Struct( - "NDACacheinfo", "=IIII", "confirmed used updated refcnt") - - -### Neighbour table entry constants. See include/uapi/linux/neighbour.h. -# Neighbour cache entry attributes. -NDA_DST = 1 -NDA_LLADDR = 2 -NDA_CACHEINFO = 3 -NDA_PROBES = 4 - -# Neighbour cache entry states. -NUD_PERMANENT = 0x80 - -# Data structure formats. -NdMsg = cstruct.Struct( - "NdMsg", "=BxxxiHBB", - "family ifindex state flags type") - - -### FIB rule constants. See include/uapi/linux/fib_rules.h. -FRA_IIFNAME = 3 -FRA_PRIORITY = 6 -FRA_FWMARK = 10 -FRA_SUPPRESS_PREFIXLEN = 14 -FRA_TABLE = 15 -FRA_FWMASK = 16 -FRA_OIFNAME = 17 -FRA_UID_START = 18 -FRA_UID_END = 19 - - -# Link constants. See include/uapi/linux/if_link.h. -IFLA_ADDRESS = 1 -IFLA_BROADCAST = 2 -IFLA_IFNAME = 3 -IFLA_MTU = 4 -IFLA_QDISC = 6 -IFLA_STATS = 7 -IFLA_TXQLEN = 13 -IFLA_MAP = 14 -IFLA_OPERSTATE = 16 -IFLA_LINKMODE = 17 -IFLA_STATS64 = 23 -IFLA_AF_SPEC = 26 -IFLA_GROUP = 27 -IFLA_EXT_MASK = 29 -IFLA_PROMISCUITY = 30 -IFLA_NUM_TX_QUEUES = 31 -IFLA_NUM_RX_QUEUES = 32 -IFLA_CARRIER = 33 - - -def CommandVerb(command): - return ["NEW", "DEL", "GET", "SET"][command % 4] - - -def CommandSubject(command): - return ["LINK", "ADDR", "ROUTE", "NEIGH", "RULE"][(command - 16) / 4] - - -def CommandName(command): - try: - return "RTM_%s%s" % (CommandVerb(command), CommandSubject(command)) - except IndexError: - return "RTM_%d" % command - - -class IPRoute(netlink.NetlinkSocket): - """Provides a tiny subset of iproute functionality.""" - - FAMILY = NETLINK_ROUTE - - def _NlAttrIPAddress(self, nla_type, family, address): - return self._NlAttr(nla_type, socket.inet_pton(family, address)) - - def _NlAttrInterfaceName(self, nla_type, interface): - return self._NlAttr(nla_type, interface + "\x00") - - def _GetConstantName(self, value, prefix): - return super(IPRoute, self)._GetConstantName(__name__, value, prefix) - - def _Decode(self, command, msg, nla_type, nla_data): - """Decodes netlink attributes to Python types. - - Values for which the code knows the type (e.g., the fwmark ID in a - RTM_NEWRULE command) are decoded to Python integers, strings, etc. Values - of unknown type are returned as raw byte strings. - - Args: - command: An integer. - - If positive, the number of the rtnetlink command being carried out. - This is used to interpret the attributes. For example, for an - RTM_NEWROUTE command, attribute type 3 is the incoming interface and - is an integer, but for a RTM_NEWRULE command, attribute type 3 is the - incoming interface name and is a string. - - If negative, one of the following (negative) values: - - RTA_METRICS: Interpret as nested route metrics. - family: The address family. Used to convert IP addresses into strings. - nla_type: An integer, then netlink attribute type. - nla_data: A byte string, the netlink attribute data. - - Returns: - A tuple (name, data): - - name is a string (e.g., "FRA_PRIORITY") if we understood the attribute, - or an integer if we didn't. - - data can be an integer, a string, a nested dict of attributes as - returned by _ParseAttributes (e.g., for RTA_METRICS), a cstruct.Struct - (e.g., RTACacheinfo), etc. If we didn't understand the attribute, it - will be the raw byte string. - """ - if command == -RTA_METRICS: - name = self._GetConstantName(nla_type, "RTAX_") - elif CommandSubject(command) == "ADDR": - name = self._GetConstantName(nla_type, "IFA_") - elif CommandSubject(command) == "LINK": - name = self._GetConstantName(nla_type, "IFLA_") - elif CommandSubject(command) == "RULE": - name = self._GetConstantName(nla_type, "FRA_") - elif CommandSubject(command) == "ROUTE": - name = self._GetConstantName(nla_type, "RTA_") - elif CommandSubject(command) == "NEIGH": - name = self._GetConstantName(nla_type, "NDA_") - else: - # Don't know what this is. Leave it as an integer. - name = nla_type - - if name in ["FRA_PRIORITY", "FRA_FWMARK", "FRA_TABLE", "FRA_FWMASK", - "FRA_UID_START", "FRA_UID_END", - "RTA_OIF", "RTA_PRIORITY", "RTA_TABLE", "RTA_MARK", - "IFLA_MTU", "IFLA_TXQLEN", "IFLA_GROUP", "IFLA_EXT_MASK", - "IFLA_PROMISCUITY", "IFLA_NUM_RX_QUEUES", - "IFLA_NUM_TX_QUEUES", "NDA_PROBES", "RTAX_MTU", - "RTAX_HOPLIMIT"]: - 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"]: - data = ord(nla_data) - elif name in ["IFA_ADDRESS", "IFA_LOCAL", "RTA_DST", "RTA_SRC", - "RTA_GATEWAY", "RTA_PREFSRC", "RTA_UID", - "NDA_DST"]: - data = socket.inet_ntop(msg.family, nla_data) - elif name in ["FRA_IIFNAME", "FRA_OIFNAME", "IFLA_IFNAME", "IFLA_QDISC", - "IFA_LABEL"]: - data = nla_data.strip("\x00") - elif name == "RTA_METRICS": - data = self._ParseAttributes(-RTA_METRICS, msg.family, None, nla_data) - elif name == "RTA_CACHEINFO": - data = RTACacheinfo(nla_data) - elif name == "IFA_CACHEINFO": - data = IFACacheinfo(nla_data) - elif name == "NDA_CACHEINFO": - data = NDACacheinfo(nla_data) - elif name in ["NDA_LLADDR", "IFLA_ADDRESS"]: - data = ":".join(x.encode("hex") for x in nla_data) - else: - data = nla_data - - return name, data - - def __init__(self): - super(IPRoute, self).__init__() - - def _AddressFamily(self, version): - return {4: socket.AF_INET, 6: socket.AF_INET6}[version] - - def _SendNlRequest(self, command, data, flags=0): - """Sends a netlink request and expects an ack.""" - - flags |= NLM_F_REQUEST - if CommandVerb(command) != "GET": - flags |= NLM_F_ACK - if CommandVerb(command) == "NEW": - if not flags & NLM_F_REPLACE: - flags |= (NLM_F_EXCL | NLM_F_CREATE) - - super(IPRoute, self)._SendNlRequest(command, data, flags) - - def _Rule(self, version, is_add, rule_type, table, match_nlattr, priority): - """Python equivalent of "ip rule lookup ". - - Args: - version: An integer, 4 or 6. - is_add: True to add a rule, False to delete it. - rule_type: Type of rule, e.g., RTN_UNICAST or RTN_UNREACHABLE. - table: If nonzero, rule looks up this table. - match_nlattr: A blob of struct nlattrs that express the match condition. - If None, match everything. - priority: An integer, the priority. - - Raises: - IOError: If the netlink request returns an error. - ValueError: If the kernel's response could not be parsed. - """ - # Create a struct rtmsg specifying the table and the given match attributes. - family = self._AddressFamily(version) - rtmsg = RTMsg((family, 0, 0, 0, RT_TABLE_UNSPEC, - RTPROT_STATIC, RT_SCOPE_UNIVERSE, rule_type, 0)).Pack() - rtmsg += self._NlAttrU32(FRA_PRIORITY, priority) - if match_nlattr: - rtmsg += match_nlattr - if table: - rtmsg += self._NlAttrU32(FRA_TABLE, table) - - # Create a netlink request containing the rtmsg. - command = RTM_NEWRULE if is_add else RTM_DELRULE - self._SendNlRequest(command, rtmsg) - - def DeleteRulesAtPriority(self, version, priority): - family = self._AddressFamily(version) - rtmsg = RTMsg((family, 0, 0, 0, RT_TABLE_UNSPEC, - RTPROT_STATIC, RT_SCOPE_UNIVERSE, RTN_UNICAST, 0)).Pack() - rtmsg += self._NlAttrU32(FRA_PRIORITY, priority) - while True: - try: - self._SendNlRequest(RTM_DELRULE, rtmsg) - except IOError, e: - if e.errno == -errno.ENOENT: - break - else: - raise - - def FwmarkRule(self, version, is_add, fwmark, table, priority): - nlattr = self._NlAttrU32(FRA_FWMARK, fwmark) - return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority) - - def IifRule(self, version, is_add, iif, table, priority): - nlattr = self._NlAttrInterfaceName(FRA_IIFNAME, iif) - return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority) - - def OifRule(self, version, is_add, oif, table, priority): - nlattr = self._NlAttrInterfaceName(FRA_OIFNAME, oif) - return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority) - - def UidRangeRule(self, version, is_add, start, end, table, priority): - nlattr = (self._NlAttrInterfaceName(FRA_IIFNAME, "lo") + - self._NlAttrU32(FRA_UID_START, start) + - self._NlAttrU32(FRA_UID_END, end)) - return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority) - - def UnreachableRule(self, version, is_add, priority): - return self._Rule(version, is_add, RTN_UNREACHABLE, None, None, priority) - - def DefaultRule(self, version, is_add, table, priority): - return self.FwmarkRule(version, is_add, 0, table, priority) - - def CommandToString(self, command, data): - try: - name = CommandName(command) - subject = CommandSubject(command) - struct_type = { - "ADDR": IfAddrMsg, - "LINK": IfinfoMsg, - "NEIGH": NdMsg, - "ROUTE": RTMsg, - "RULE": RTMsg, - }[subject] - parsed = self._ParseNLMsg(data, struct_type) - return "%s %s" % (name, str(parsed)) - except IndexError: - raise ValueError("Don't know how to print command type %s" % name) - - def MaybeDebugCommand(self, command, data): - subject = CommandSubject(command) - if "ALL" not in self.NL_DEBUG and subject not in self.NL_DEBUG: - return - print self.CommandToString(command, data) - - def MaybeDebugMessage(self, message): - hdr = NLMsgHdr(message) - self.MaybeDebugCommand(hdr.type, message) - - def PrintMessage(self, message): - hdr = NLMsgHdr(message) - print self.CommandToString(hdr.type, message) - - def DumpRules(self, version): - """Returns the IP rules for the specified IP version.""" - # Create a struct rtmsg specifying the table and the given match attributes. - family = self._AddressFamily(version) - rtmsg = RTMsg((family, 0, 0, 0, 0, 0, 0, 0, 0)) - return self._Dump(RTM_GETRULE, rtmsg, RTMsg, "") - - def DumpLinks(self): - ifinfomsg = IfinfoMsg((0, 0, 0, 0, 0, 0)) - return self._Dump(RTM_GETLINK, ifinfomsg, IfinfoMsg, "") - - def DumpAddresses(self, version): - family = self._AddressFamily(version) - ifaddrmsg = IfAddrMsg((family, 0, 0, 0, 0)) - return self._Dump(RTM_GETADDR, ifaddrmsg, IfAddrMsg, "") - - def _Address(self, version, command, addr, prefixlen, flags, scope, ifindex): - """Adds or deletes an IP address.""" - family = self._AddressFamily(version) - ifaddrmsg = IfAddrMsg((family, prefixlen, flags, scope, ifindex)).Pack() - ifaddrmsg += self._NlAttrIPAddress(IFA_ADDRESS, family, addr) - if version == 4: - ifaddrmsg += self._NlAttrIPAddress(IFA_LOCAL, family, addr) - self._SendNlRequest(command, ifaddrmsg) - - def AddAddress(self, address, prefixlen, ifindex): - self._Address(6 if ":" in address else 4, - RTM_NEWADDR, address, prefixlen, - IFA_F_PERMANENT, RT_SCOPE_UNIVERSE, ifindex) - - def DelAddress(self, address, prefixlen, ifindex): - self._Address(6 if ":" in address else 4, - RTM_DELADDR, address, prefixlen, 0, 0, ifindex) - - def GetAddress(self, address, ifindex=0): - """Returns an ifaddrmsg for the requested address.""" - if ":" not in address: - # The address is likely an IPv4 address. RTM_GETADDR without the - # NLM_F_DUMP flag is not supported by the kernel. We do not currently - # implement parsing dump results. - raise NotImplementedError("IPv4 RTM_GETADDR not implemented.") - self._Address(6, RTM_GETADDR, address, 0, 0, RT_SCOPE_UNIVERSE, ifindex) - return self._GetMsg(IfAddrMsg) - - def _Route(self, version, command, table, dest, prefixlen, nexthop, dev, - mark, uid): - """Adds, deletes, or queries a route.""" - family = self._AddressFamily(version) - scope = RT_SCOPE_UNIVERSE if nexthop else RT_SCOPE_LINK - rtmsg = RTMsg((family, prefixlen, 0, 0, RT_TABLE_UNSPEC, - RTPROT_STATIC, scope, RTN_UNICAST, 0)).Pack() - if command == RTM_NEWROUTE and not table: - # Don't allow setting routes in table 0, since its behaviour is confusing - # and differs between IPv4 and IPv6. - raise ValueError("Cowardly refusing to add a route to table 0") - if table: - rtmsg += self._NlAttrU32(FRA_TABLE, table) - if dest != "default": # The default is the default route. - rtmsg += self._NlAttrIPAddress(RTA_DST, family, dest) - if nexthop: - rtmsg += self._NlAttrIPAddress(RTA_GATEWAY, family, nexthop) - if dev: - rtmsg += self._NlAttrU32(RTA_OIF, dev) - if mark is not None: - rtmsg += self._NlAttrU32(RTA_MARK, mark) - if uid is not None: - rtmsg += self._NlAttrU32(RTA_UID, uid) - self._SendNlRequest(command, rtmsg) - - def AddRoute(self, version, table, dest, prefixlen, nexthop, dev): - self._Route(version, RTM_NEWROUTE, table, dest, prefixlen, nexthop, dev, - None, None) - - def DelRoute(self, version, table, dest, prefixlen, nexthop, dev): - self._Route(version, RTM_DELROUTE, table, dest, prefixlen, nexthop, dev, - None, None) - - def GetRoutes(self, dest, oif, mark, uid): - version = 6 if ":" in dest else 4 - prefixlen = {4: 32, 6: 128}[version] - self._Route(version, RTM_GETROUTE, 0, dest, prefixlen, None, oif, mark, uid) - data = self._Recv() - # The response will either be an error or a list of routes. - if NLMsgHdr(data).type == NLMSG_ERROR: - self._ParseAck(data) - routes = self._GetMsgList(RTMsg, data, False) - return routes - - def _Neighbour(self, version, is_add, addr, lladdr, dev, state, flags=0): - """Adds or deletes a neighbour cache entry.""" - family = self._AddressFamily(version) - - # Convert the link-layer address to a raw byte string. - if is_add and lladdr: - lladdr = lladdr.split(":") - if len(lladdr) != 6: - raise ValueError("Invalid lladdr %s" % ":".join(lladdr)) - lladdr = "".join(chr(int(hexbyte, 16)) for hexbyte in lladdr) - - ndmsg = NdMsg((family, dev, state, 0, RTN_UNICAST)).Pack() - ndmsg += self._NlAttrIPAddress(NDA_DST, family, addr) - if is_add and lladdr: - ndmsg += self._NlAttr(NDA_LLADDR, lladdr) - command = RTM_NEWNEIGH if is_add else RTM_DELNEIGH - self._SendNlRequest(command, ndmsg, flags) - - def AddNeighbour(self, version, addr, lladdr, dev): - self._Neighbour(version, True, addr, lladdr, dev, NUD_PERMANENT) - - def DelNeighbour(self, version, addr, lladdr, dev): - self._Neighbour(version, False, addr, lladdr, dev, 0) - - def UpdateNeighbour(self, version, addr, lladdr, dev, state): - self._Neighbour(version, True, addr, lladdr, dev, state, - flags=NLM_F_REPLACE) - - def DumpNeighbours(self, version): - ndmsg = NdMsg((self._AddressFamily(version), 0, 0, 0, 0)) - return self._Dump(RTM_GETNEIGH, ndmsg, NdMsg, "") - - def ParseNeighbourMessage(self, msg): - msg, _ = self._ParseNLMsg(msg, NdMsg) - return msg - - -if __name__ == "__main__": - iproute = IPRoute() - iproute.DEBUG = True - iproute.DumpRules(6) - iproute.DumpLinks() - print iproute.GetRoutes("2001:4860:4860::8888", 0, 0, None) diff --git a/tests/net_test/multinetwork_base.py b/tests/net_test/multinetwork_base.py deleted file mode 100644 index 31fcc4c6..00000000 --- a/tests/net_test/multinetwork_base.py +++ /dev/null @@ -1,642 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2014 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. - -"""Base module for multinetwork tests.""" - -import errno -import fcntl -import os -import posix -import random -import re -from socket import * # pylint: disable=wildcard-import -import struct - -from scapy import all as scapy - -import csocket -import cstruct -import iproute -import net_test - - -IFF_TUN = 1 -IFF_TAP = 2 -IFF_NO_PI = 0x1000 -TUNSETIFF = 0x400454ca - -SO_BINDTODEVICE = 25 - -# Setsockopt values. -IP_UNICAST_IF = 50 -IPV6_MULTICAST_IF = 17 -IPV6_UNICAST_IF = 76 - -# Cmsg values. -IP_TTL = 2 -IP_PKTINFO = 8 -IPV6_2292PKTOPTIONS = 6 -IPV6_FLOWINFO = 11 -IPV6_PKTINFO = 50 -IPV6_HOPLIMIT = 52 # Different from IPV6_UNICAST_HOPS, this is cmsg only. - -# Data structures. -# These aren't constants, they're classes. So, pylint: disable=invalid-name -InPktinfo = cstruct.Struct("in_pktinfo", "@i4s4s", "ifindex spec_dst addr") -In6Pktinfo = cstruct.Struct("in6_pktinfo", "@16si", "addr ifindex") - - -def HaveUidRouting(): - """Checks whether the kernel supports UID routing.""" - # Create a rule with the UID range selector. If the kernel doesn't understand - # the selector, it will create a rule with no selectors. - try: - iproute.IPRoute().UidRangeRule(6, True, 1000, 2000, 100, 10000) - except IOError: - return False - - # Dump all the rules. If we find a rule using the UID range selector, then the - # kernel supports UID range routing. - rules = iproute.IPRoute().DumpRules(6) - result = any("FRA_UID_START" in attrs for rule, attrs in rules) - - # Delete the rule. - iproute.IPRoute().UidRangeRule(6, False, 1000, 2000, 100, 10000) - return result - -AUTOCONF_TABLE_SYSCTL = "/proc/sys/net/ipv6/conf/default/accept_ra_rt_table" - -HAVE_AUTOCONF_TABLE = os.path.isfile(AUTOCONF_TABLE_SYSCTL) -HAVE_UID_ROUTING = HaveUidRouting() - - -class UnexpectedPacketError(AssertionError): - pass - - -def MakePktInfo(version, addr, ifindex): - family = {4: AF_INET, 6: AF_INET6}[version] - if not addr: - addr = {4: "0.0.0.0", 6: "::"}[version] - if addr: - addr = inet_pton(family, addr) - if version == 6: - return In6Pktinfo((addr, ifindex)).Pack() - else: - return InPktinfo((ifindex, addr, "\x00" * 4)).Pack() - - -class MultiNetworkBaseTest(net_test.NetworkTest): - """Base class for all multinetwork tests. - - This class does not contain any test code, but contains code to set up and - tear a multi-network environment using multiple tun interfaces. The - environment is designed to be similar to a real Android device in terms of - rules and routes, and supports IPv4 and IPv6. - - Tests wishing to use this environment should inherit from this class and - ensure that any setupClass, tearDownClass, setUp, and tearDown methods they - implement also call the superclass versions. - """ - - # Must be between 1 and 256, since we put them in MAC addresses and IIDs. - NETIDS = [100, 150, 200, 250] - - # Stores sysctl values to write back when the test completes. - saved_sysctls = {} - - # Wether to output setup commands. - DEBUG = False - - # The size of our UID ranges. - UID_RANGE_SIZE = 1000 - - # Rule priorities. - PRIORITY_UID = 100 - PRIORITY_OIF = 200 - PRIORITY_FWMARK = 300 - PRIORITY_IIF = 400 - PRIORITY_DEFAULT = 999 - PRIORITY_UNREACHABLE = 1000 - - # For convenience. - IPV4_ADDR = net_test.IPV4_ADDR - IPV6_ADDR = net_test.IPV6_ADDR - IPV4_PING = net_test.IPV4_PING - IPV6_PING = net_test.IPV6_PING - - @classmethod - def UidRangeForNetid(cls, netid): - return ( - cls.UID_RANGE_SIZE * netid, - cls.UID_RANGE_SIZE * (netid + 1) - 1 - ) - - @classmethod - def UidForNetid(cls, netid): - return random.randint(*cls.UidRangeForNetid(netid)) - - @classmethod - def _TableForNetid(cls, netid): - if cls.AUTOCONF_TABLE_OFFSET and netid in cls.ifindices: - return cls.ifindices[netid] + (-cls.AUTOCONF_TABLE_OFFSET) - else: - return netid - - @staticmethod - def GetInterfaceName(netid): - return "nettest%d" % netid - - @staticmethod - def RouterMacAddress(netid): - return "02:00:00:00:%02x:00" % netid - - @staticmethod - def MyMacAddress(netid): - return "02:00:00:00:%02x:01" % netid - - @staticmethod - def _RouterAddress(netid, version): - if version == 6: - return "fe80::%02x00" % netid - elif version == 4: - return "10.0.%d.1" % netid - else: - raise ValueError("Don't support IPv%s" % version) - - @classmethod - def _MyIPv4Address(cls, netid): - return "10.0.%d.2" % netid - - @classmethod - def _MyIPv6Address(cls, netid): - return net_test.GetLinkAddress(cls.GetInterfaceName(netid), False) - - @classmethod - def MyAddress(cls, version, netid): - return {4: cls._MyIPv4Address(netid), - 5: "::ffff:" + cls._MyIPv4Address(netid), - 6: cls._MyIPv6Address(netid)}[version] - - @classmethod - def MyLinkLocalAddress(cls, netid): - return net_test.GetLinkAddress(cls.GetInterfaceName(netid), True) - - @staticmethod - def IPv6Prefix(netid): - return "2001:db8:%02x::" % netid - - @staticmethod - def GetRandomDestination(prefix): - if "." in prefix: - return prefix + "%d.%d" % (random.randint(0, 31), random.randint(0, 255)) - else: - return prefix + "%x:%x" % (random.randint(0, 65535), - random.randint(0, 65535)) - - def GetProtocolFamily(self, version): - return {4: AF_INET, 6: AF_INET6}[version] - - @classmethod - def CreateTunInterface(cls, netid): - iface = cls.GetInterfaceName(netid) - f = open("/dev/net/tun", "r+b") - ifr = struct.pack("16sH", iface, IFF_TAP | IFF_NO_PI) - ifr += "\x00" * (40 - len(ifr)) - fcntl.ioctl(f, TUNSETIFF, ifr) - # Give ourselves a predictable MAC address. - net_test.SetInterfaceHWAddr(iface, cls.MyMacAddress(netid)) - # Disable DAD so we don't have to wait for it. - cls.SetSysctl("/proc/sys/net/ipv6/conf/%s/accept_dad" % iface, 0) - # Set accept_ra to 2, because that's what we use. - cls.SetSysctl("/proc/sys/net/ipv6/conf/%s/accept_ra" % iface, 2) - net_test.SetInterfaceUp(iface) - net_test.SetNonBlocking(f) - return f - - @classmethod - def SendRA(cls, netid, retranstimer=None, reachabletime=0): - validity = 300 # seconds - macaddr = cls.RouterMacAddress(netid) - lladdr = cls._RouterAddress(netid, 6) - - if retranstimer is None: - # If no retrans timer was specified, pick one that's as long as the - # router lifetime. This ensures that no spurious ND retransmits - # will interfere with test expectations. - retranstimer = validity - - # We don't want any routes in the main table. If the kernel doesn't support - # putting RA routes into per-interface tables, configure routing manually. - routerlifetime = validity if HAVE_AUTOCONF_TABLE else 0 - - ra = (scapy.Ether(src=macaddr, dst="33:33:00:00:00:01") / - scapy.IPv6(src=lladdr, hlim=255) / - scapy.ICMPv6ND_RA(reachabletime=reachabletime, - retranstimer=retranstimer, - routerlifetime=routerlifetime) / - scapy.ICMPv6NDOptSrcLLAddr(lladdr=macaddr) / - scapy.ICMPv6NDOptPrefixInfo(prefix=cls.IPv6Prefix(netid), - prefixlen=64, - L=1, A=1, - validlifetime=validity, - preferredlifetime=validity)) - posix.write(cls.tuns[netid].fileno(), str(ra)) - - @classmethod - def _RunSetupCommands(cls, netid, is_add): - for version in [4, 6]: - # Find out how to configure things. - iface = cls.GetInterfaceName(netid) - ifindex = cls.ifindices[netid] - macaddr = cls.RouterMacAddress(netid) - router = cls._RouterAddress(netid, version) - table = cls._TableForNetid(netid) - - # Set up routing rules. - if HAVE_UID_ROUTING: - start, end = cls.UidRangeForNetid(netid) - cls.iproute.UidRangeRule(version, is_add, start, end, table, - cls.PRIORITY_UID) - cls.iproute.OifRule(version, is_add, iface, table, cls.PRIORITY_OIF) - cls.iproute.FwmarkRule(version, is_add, netid, table, - cls.PRIORITY_FWMARK) - - # Configure routing and addressing. - # - # IPv6 uses autoconf for everything, except if per-device autoconf routing - # tables are not supported, in which case the default route (only) is - # configured manually. For IPv4 we have to manually configure addresses, - # routes, and neighbour cache entries (since we don't reply to ARP or ND). - # - # Since deleting addresses also causes routes to be deleted, we need to - # be careful with ordering or the delete commands will fail with ENOENT. - do_routing = (version == 4 or cls.AUTOCONF_TABLE_OFFSET is None) - if is_add: - if version == 4: - cls.iproute.AddAddress(cls._MyIPv4Address(netid), 24, ifindex) - cls.iproute.AddNeighbour(version, router, macaddr, ifindex) - if do_routing: - cls.iproute.AddRoute(version, table, "default", 0, router, ifindex) - if version == 6: - cls.iproute.AddRoute(version, table, - cls.IPv6Prefix(netid), 64, None, ifindex) - else: - if do_routing: - cls.iproute.DelRoute(version, table, "default", 0, router, ifindex) - if version == 6: - cls.iproute.DelRoute(version, table, - cls.IPv6Prefix(netid), 64, None, ifindex) - if version == 4: - cls.iproute.DelNeighbour(version, router, macaddr, ifindex) - cls.iproute.DelAddress(cls._MyIPv4Address(netid), 24, ifindex) - - @classmethod - def SetDefaultNetwork(cls, netid): - table = cls._TableForNetid(netid) if netid else None - for version in [4, 6]: - is_add = table is not None - cls.iproute.DefaultRule(version, is_add, table, cls.PRIORITY_DEFAULT) - - @classmethod - def ClearDefaultNetwork(cls): - cls.SetDefaultNetwork(None) - - @classmethod - def GetSysctl(cls, sysctl): - return open(sysctl, "r").read() - - @classmethod - def SetSysctl(cls, sysctl, value): - # Only save each sysctl value the first time we set it. This is so we can - # set it to arbitrary values multiple times and still write it back - # correctly at the end. - if sysctl not in cls.saved_sysctls: - cls.saved_sysctls[sysctl] = cls.GetSysctl(sysctl) - open(sysctl, "w").write(str(value) + "\n") - - @classmethod - def SetIPv6SysctlOnAllIfaces(cls, sysctl, value): - for netid in cls.tuns: - iface = cls.GetInterfaceName(netid) - name = "/proc/sys/net/ipv6/conf/%s/%s" % (iface, sysctl) - cls.SetSysctl(name, value) - - @classmethod - def _RestoreSysctls(cls): - for sysctl, value in cls.saved_sysctls.iteritems(): - try: - open(sysctl, "w").write(value) - except IOError: - pass - - @classmethod - def _ICMPRatelimitFilename(cls, version): - return "/proc/sys/net/" + {4: "ipv4/icmp_ratelimit", - 6: "ipv6/icmp/ratelimit"}[version] - - @classmethod - def _SetICMPRatelimit(cls, version, limit): - cls.SetSysctl(cls._ICMPRatelimitFilename(version), limit) - - @classmethod - def setUpClass(cls): - # This is per-class setup instead of per-testcase setup because shelling out - # to ip and iptables is slow, and because routing configuration doesn't - # change during the test. - cls.iproute = iproute.IPRoute() - cls.tuns = {} - cls.ifindices = {} - if HAVE_AUTOCONF_TABLE: - cls.SetSysctl(AUTOCONF_TABLE_SYSCTL, -1000) - cls.AUTOCONF_TABLE_OFFSET = -1000 - else: - cls.AUTOCONF_TABLE_OFFSET = None - - # Disable ICMP rate limits. These will be restored by _RestoreSysctls. - for version in [4, 6]: - cls._SetICMPRatelimit(version, 0) - - for netid in cls.NETIDS: - cls.tuns[netid] = cls.CreateTunInterface(netid) - iface = cls.GetInterfaceName(netid) - cls.ifindices[netid] = net_test.GetInterfaceIndex(iface) - - cls.SendRA(netid) - cls._RunSetupCommands(netid, True) - - for version in [4, 6]: - cls.iproute.UnreachableRule(version, True, 1000) - - # Uncomment to look around at interface and rule configuration while - # running in the background. (Once the test finishes running, all the - # interfaces and rules are gone.) - # time.sleep(30) - - @classmethod - def tearDownClass(cls): - for version in [4, 6]: - try: - cls.iproute.UnreachableRule(version, False, 1000) - except IOError: - pass - - for netid in cls.tuns: - cls._RunSetupCommands(netid, False) - cls.tuns[netid].close() - cls._RestoreSysctls() - - def setUp(self): - self.ClearTunQueues() - - def SetSocketMark(self, s, netid): - if netid is None: - netid = 0 - s.setsockopt(SOL_SOCKET, net_test.SO_MARK, netid) - - def GetSocketMark(self, s): - return s.getsockopt(SOL_SOCKET, net_test.SO_MARK) - - def ClearSocketMark(self, s): - self.SetSocketMark(s, 0) - - def BindToDevice(self, s, iface): - if not iface: - iface = "" - s.setsockopt(SOL_SOCKET, SO_BINDTODEVICE, iface) - - def SetUnicastInterface(self, s, ifindex): - # Otherwise, Python thinks it's a 1-byte option. - ifindex = struct.pack("!I", ifindex) - - # Always set the IPv4 interface, because it will be used even on IPv6 - # sockets if the destination address is a mapped address. - s.setsockopt(net_test.SOL_IP, IP_UNICAST_IF, ifindex) - if s.family == AF_INET6: - s.setsockopt(net_test.SOL_IPV6, IPV6_UNICAST_IF, ifindex) - - def GetRemoteAddress(self, version): - return {4: self.IPV4_ADDR, - 5: "::ffff:" + self.IPV4_ADDR, - 6: self.IPV6_ADDR}[version] - - def SelectInterface(self, s, netid, mode): - if mode == "uid": - raise ValueError("Can't change UID on an existing socket") - elif mode == "mark": - self.SetSocketMark(s, netid) - elif mode == "oif": - iface = self.GetInterfaceName(netid) if netid else "" - self.BindToDevice(s, iface) - elif mode == "ucast_oif": - self.SetUnicastInterface(s, self.ifindices.get(netid, 0)) - else: - raise ValueError("Unknown interface selection mode %s" % mode) - - def BuildSocket(self, version, constructor, netid, routing_mode): - s = constructor(self.GetProtocolFamily(version)) - - if routing_mode not in [None, "uid"]: - self.SelectInterface(s, netid, routing_mode) - elif routing_mode == "uid": - os.fchown(s.fileno(), self.UidForNetid(netid), -1) - - return s - - def SendOnNetid(self, version, s, dstaddr, dstport, netid, payload, cmsgs): - if netid is not None: - pktinfo = MakePktInfo(version, None, self.ifindices[netid]) - cmsg_level, cmsg_name = { - 4: (net_test.SOL_IP, IP_PKTINFO), - 6: (net_test.SOL_IPV6, IPV6_PKTINFO)}[version] - cmsgs.append((cmsg_level, cmsg_name, pktinfo)) - csocket.Sendmsg(s, (dstaddr, dstport), payload, cmsgs, csocket.MSG_CONFIRM) - - def ReceiveEtherPacketOn(self, netid, packet): - posix.write(self.tuns[netid].fileno(), str(packet)) - - def ReceivePacketOn(self, netid, ip_packet): - routermac = self.RouterMacAddress(netid) - mymac = self.MyMacAddress(netid) - packet = scapy.Ether(src=routermac, dst=mymac) / ip_packet - self.ReceiveEtherPacketOn(netid, packet) - - def ReadAllPacketsOn(self, netid, include_multicast=False): - packets = [] - while True: - try: - packet = posix.read(self.tuns[netid].fileno(), 4096) - if not packet: - break - ether = scapy.Ether(packet) - # Multicast frames are frames where the first byte of the destination - # MAC address has 1 in the least-significant bit. - if include_multicast or not int(ether.dst.split(":")[0], 16) & 0x1: - packets.append(ether.payload) - except OSError, e: - # EAGAIN means there are no more packets waiting. - if re.match(e.message, os.strerror(errno.EAGAIN)): - break - # Anything else is unexpected. - else: - raise e - return packets - - def ClearTunQueues(self): - # Keep reading packets on all netids until we get no packets on any of them. - waiting = None - while waiting != 0: - waiting = sum(len(self.ReadAllPacketsOn(netid)) for netid in self.NETIDS) - - def assertPacketMatches(self, expected, actual): - # The expected packet is just a rough sketch of the packet we expect to - # receive. For example, it doesn't contain fields we can't predict, such as - # initial TCP sequence numbers, or that depend on the host implementation - # and settings, such as TCP options. To check whether the packet matches - # what we expect, instead of just checking all the known fields one by one, - # we blank out fields in the actual packet and then compare the whole - # packets to each other as strings. Because we modify the actual packet, - # make a copy here. - actual = actual.copy() - - # Blank out IPv4 fields that we can't predict, like ID and the DF bit. - actualip = actual.getlayer("IP") - expectedip = expected.getlayer("IP") - if actualip and expectedip: - actualip.id = expectedip.id - actualip.flags &= 5 - actualip.chksum = None # Change the header, recalculate the checksum. - - # Blank out the flow label, since new kernels randomize it by default. - actualipv6 = actual.getlayer("IPv6") - expectedipv6 = expected.getlayer("IPv6") - if actualipv6 and expectedipv6: - actualipv6.fl = expectedipv6.fl - - # Blank out UDP fields that we can't predict (e.g., the source port for - # kernel-originated packets). - actualudp = actual.getlayer("UDP") - expectedudp = expected.getlayer("UDP") - if actualudp and expectedudp: - if expectedudp.sport is None: - actualudp.sport = None - actualudp.chksum = None - - # Since the TCP code below messes with options, recalculate the length. - if actualip: - actualip.len = None - if actualipv6: - actualipv6.plen = None - - # Blank out TCP fields that we can't predict. - actualtcp = actual.getlayer("TCP") - expectedtcp = expected.getlayer("TCP") - if actualtcp and expectedtcp: - actualtcp.dataofs = expectedtcp.dataofs - actualtcp.options = expectedtcp.options - actualtcp.window = expectedtcp.window - if expectedtcp.sport is None: - actualtcp.sport = None - if expectedtcp.seq is None: - actualtcp.seq = None - if expectedtcp.ack is None: - actualtcp.ack = None - actualtcp.chksum = None - - # Serialize the packet so that expected packet fields that are only set when - # a packet is serialized e.g., the checksum) are filled in. - expected_real = expected.__class__(str(expected)) - actual_real = actual.__class__(str(actual)) - # repr() can be expensive. Call it only if the test is going to fail and we - # want to see the error. - if expected_real != actual_real: - self.assertEquals(repr(expected_real), repr(actual_real)) - - def PacketMatches(self, expected, actual): - try: - self.assertPacketMatches(expected, actual) - return True - except AssertionError: - return False - - def ExpectNoPacketsOn(self, netid, msg): - packets = self.ReadAllPacketsOn(netid) - if packets: - firstpacket = repr(packets[0]) - else: - firstpacket = "" - self.assertFalse(packets, msg + ": unexpected packet: " + firstpacket) - - def ExpectPacketOn(self, netid, msg, expected): - # To avoid confusion due to lots of ICMPv6 ND going on all the time, drop - # multicast packets unless the packet we expect to see is a multicast - # packet. For now the only tests that use this are IPv6. - ipv6 = expected.getlayer("IPv6") - if ipv6 and ipv6.dst.startswith("ff"): - include_multicast = True - else: - include_multicast = False - - packets = self.ReadAllPacketsOn(netid, include_multicast=include_multicast) - self.assertTrue(packets, msg + ": received no packets") - - # If we receive a packet that matches what we expected, return it. - for packet in packets: - if self.PacketMatches(expected, packet): - return packet - - # None of the packets matched. Call assertPacketMatches to output a diff - # between the expected packet and the last packet we received. In theory, - # we'd output a diff to the packet that's the best match for what we - # expected, but this is good enough for now. - try: - self.assertPacketMatches(expected, packets[-1]) - except Exception, e: - raise UnexpectedPacketError( - "%s: diff with last packet:\n%s" % (msg, e.message)) - - def Combinations(self, version): - """Produces a list of combinations to test.""" - combinations = [] - - # Check packets addressed to the IP addresses of all our interfaces... - for dest_ip_netid in self.tuns: - ip_if = self.GetInterfaceName(dest_ip_netid) - myaddr = self.MyAddress(version, dest_ip_netid) - remoteaddr = self.GetRemoteAddress(version) - - # ... coming in on all our interfaces. - for netid in self.tuns: - iif = self.GetInterfaceName(netid) - combinations.append((netid, iif, ip_if, myaddr, remoteaddr)) - - return combinations - - def _FormatMessage(self, iif, ip_if, extra, desc, reply_desc): - msg = "Receiving %s on %s to %s IP, %s" % (desc, iif, ip_if, extra) - if reply_desc: - msg += ": Expecting %s on %s" % (reply_desc, iif) - else: - msg += ": Expecting no packets on %s" % iif - return msg - - def _ReceiveAndExpectResponse(self, netid, packet, reply, msg): - self.ReceivePacketOn(netid, packet) - if reply: - return self.ExpectPacketOn(netid, msg, reply) - else: - self.ExpectNoPacketsOn(netid, msg) - return None diff --git a/tests/net_test/multinetwork_test.py b/tests/net_test/multinetwork_test.py deleted file mode 100755 index 660fdf6c..00000000 --- a/tests/net_test/multinetwork_test.py +++ /dev/null @@ -1,926 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2014 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 errno -import os -import random -from socket import * # pylint: disable=wildcard-import -import struct -import time # pylint: disable=unused-import -import unittest - -from scapy import all as scapy - -import iproute -import multinetwork_base -import net_test -import packets - -# For brevity. -UDP_PAYLOAD = net_test.UDP_PAYLOAD - -IPV6_FLOWINFO = 11 - -IPV4_MARK_REFLECT_SYSCTL = "/proc/sys/net/ipv4/fwmark_reflect" -IPV6_MARK_REFLECT_SYSCTL = "/proc/sys/net/ipv6/fwmark_reflect" -SYNCOOKIES_SYSCTL = "/proc/sys/net/ipv4/tcp_syncookies" -TCP_MARK_ACCEPT_SYSCTL = "/proc/sys/net/ipv4/tcp_fwmark_accept" - -# The IP[V6]UNICAST_IF socket option was added between 3.1 and 3.4. -HAVE_UNICAST_IF = net_test.LINUX_VERSION >= (3, 4, 0) - - -class ConfigurationError(AssertionError): - pass - - -class InboundMarkingTest(multinetwork_base.MultiNetworkBaseTest): - - @classmethod - def _SetInboundMarking(cls, netid, is_add): - for version in [4, 6]: - # Run iptables to set up incoming packet marking. - iface = cls.GetInterfaceName(netid) - add_del = "-A" if is_add else "-D" - iptables = {4: "iptables", 6: "ip6tables"}[version] - args = "%s %s INPUT -t mangle -i %s -j MARK --set-mark %d" % ( - iptables, add_del, iface, netid) - iptables = "/sbin/" + iptables - ret = os.spawnvp(os.P_WAIT, iptables, args.split(" ")) - if ret: - raise ConfigurationError("Setup command failed: %s" % args) - - @classmethod - def setUpClass(cls): - super(InboundMarkingTest, cls).setUpClass() - for netid in cls.tuns: - cls._SetInboundMarking(netid, True) - - @classmethod - def tearDownClass(cls): - for netid in cls.tuns: - cls._SetInboundMarking(netid, False) - super(InboundMarkingTest, cls).tearDownClass() - - @classmethod - def SetMarkReflectSysctls(cls, value): - cls.SetSysctl(IPV4_MARK_REFLECT_SYSCTL, value) - try: - cls.SetSysctl(IPV6_MARK_REFLECT_SYSCTL, value) - except IOError: - # This does not exist if we use the version of the patch that uses a - # common sysctl for IPv4 and IPv6. - pass - - -class OutgoingTest(multinetwork_base.MultiNetworkBaseTest): - - # How many times to run outgoing packet tests. - ITERATIONS = 5 - - def CheckPingPacket(self, version, netid, routing_mode, dstaddr, packet): - s = self.BuildSocket(version, net_test.PingSocket, netid, routing_mode) - - myaddr = self.MyAddress(version, netid) - s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) - s.bind((myaddr, packets.PING_IDENT)) - net_test.SetSocketTos(s, packets.PING_TOS) - - desc, expected = packets.ICMPEcho(version, myaddr, dstaddr) - msg = "IPv%d ping: expected %s on %s" % ( - version, desc, self.GetInterfaceName(netid)) - - s.sendto(packet + packets.PING_PAYLOAD, (dstaddr, 19321)) - - self.ExpectPacketOn(netid, msg, expected) - - def CheckTCPSYNPacket(self, version, netid, routing_mode, dstaddr): - s = self.BuildSocket(version, net_test.TCPSocket, netid, routing_mode) - - if version == 6 and dstaddr.startswith("::ffff"): - version = 4 - myaddr = self.MyAddress(version, netid) - desc, expected = packets.SYN(53, version, myaddr, dstaddr, - sport=None, seq=None) - - # Non-blocking TCP connects always return EINPROGRESS. - self.assertRaisesErrno(errno.EINPROGRESS, s.connect, (dstaddr, 53)) - msg = "IPv%s TCP connect: expected %s on %s" % ( - version, desc, self.GetInterfaceName(netid)) - self.ExpectPacketOn(netid, msg, expected) - s.close() - - def CheckUDPPacket(self, version, netid, routing_mode, dstaddr): - s = self.BuildSocket(version, net_test.UDPSocket, netid, routing_mode) - - if version == 6 and dstaddr.startswith("::ffff"): - version = 4 - myaddr = self.MyAddress(version, netid) - desc, expected = packets.UDP(version, myaddr, dstaddr, sport=None) - msg = "IPv%s UDP %%s: expected %s on %s" % ( - version, desc, self.GetInterfaceName(netid)) - - s.sendto(UDP_PAYLOAD, (dstaddr, 53)) - self.ExpectPacketOn(netid, msg % "sendto", expected) - - # IP_UNICAST_IF doesn't seem to work on connected sockets, so no TCP. - if routing_mode != "ucast_oif": - s.connect((dstaddr, 53)) - s.send(UDP_PAYLOAD) - self.ExpectPacketOn(netid, msg % "connect/send", expected) - s.close() - - def CheckRawGrePacket(self, version, netid, routing_mode, dstaddr): - s = self.BuildSocket(version, net_test.RawGRESocket, netid, routing_mode) - - inner_version = {4: 6, 6: 4}[version] - inner_src = self.MyAddress(inner_version, netid) - inner_dst = self.GetRemoteAddress(inner_version) - inner = str(packets.UDP(inner_version, inner_src, inner_dst, sport=None)[1]) - - ethertype = {4: net_test.ETH_P_IP, 6: net_test.ETH_P_IPV6}[inner_version] - # A GRE header can be as simple as two zero bytes and the ethertype. - packet = struct.pack("!i", ethertype) + inner - myaddr = self.MyAddress(version, netid) - - s.sendto(packet, (dstaddr, IPPROTO_GRE)) - desc, expected = packets.GRE(version, myaddr, dstaddr, ethertype, inner) - msg = "Raw IPv%d GRE with inner IPv%d UDP: expected %s on %s" % ( - version, inner_version, desc, self.GetInterfaceName(netid)) - self.ExpectPacketOn(netid, msg, expected) - - def CheckOutgoingPackets(self, routing_mode): - v4addr = self.IPV4_ADDR - v6addr = self.IPV6_ADDR - v4mapped = "::ffff:" + v4addr - - for _ in xrange(self.ITERATIONS): - for netid in self.tuns: - - self.CheckPingPacket(4, netid, routing_mode, v4addr, self.IPV4_PING) - # Kernel bug. - if routing_mode != "oif": - self.CheckPingPacket(6, netid, routing_mode, v6addr, self.IPV6_PING) - - # IP_UNICAST_IF doesn't seem to work on connected sockets, so no TCP. - if routing_mode != "ucast_oif": - self.CheckTCPSYNPacket(4, netid, routing_mode, v4addr) - self.CheckTCPSYNPacket(6, netid, routing_mode, v6addr) - self.CheckTCPSYNPacket(6, netid, routing_mode, v4mapped) - - self.CheckUDPPacket(4, netid, routing_mode, v4addr) - self.CheckUDPPacket(6, netid, routing_mode, v6addr) - self.CheckUDPPacket(6, netid, routing_mode, v4mapped) - - # Creating raw sockets on non-root UIDs requires properly setting - # capabilities, which is hard to do from Python. - # IP_UNICAST_IF is not supported on raw sockets. - if routing_mode not in ["uid", "ucast_oif"]: - self.CheckRawGrePacket(4, netid, routing_mode, v4addr) - self.CheckRawGrePacket(6, netid, routing_mode, v6addr) - - def testMarkRouting(self): - """Checks that socket marking selects the right outgoing interface.""" - self.CheckOutgoingPackets("mark") - - @unittest.skipUnless(multinetwork_base.HAVE_UID_ROUTING, "no UID routes") - def testUidRouting(self): - """Checks that UID routing selects the right outgoing interface.""" - self.CheckOutgoingPackets("uid") - - def testOifRouting(self): - """Checks that oif routing selects the right outgoing interface.""" - self.CheckOutgoingPackets("oif") - - @unittest.skipUnless(HAVE_UNICAST_IF, "no support for UNICAST_IF") - def testUcastOifRouting(self): - """Checks that ucast oif routing selects the right outgoing interface.""" - self.CheckOutgoingPackets("ucast_oif") - - def CheckRemarking(self, version, use_connect): - # Remarking or resetting UNICAST_IF on connected sockets does not work. - if use_connect: - modes = ["oif"] - else: - modes = ["mark", "oif"] - if HAVE_UNICAST_IF: - modes += ["ucast_oif"] - - for mode in modes: - s = net_test.UDPSocket(self.GetProtocolFamily(version)) - - # Figure out what packets to expect. - unspec = {4: "0.0.0.0", 6: "::"}[version] - sport = packets.RandomPort() - s.bind((unspec, sport)) - dstaddr = {4: self.IPV4_ADDR, 6: self.IPV6_ADDR}[version] - desc, expected = packets.UDP(version, unspec, dstaddr, sport) - - # If we're testing connected sockets, connect the socket on the first - # netid now. - if use_connect: - netid = self.tuns.keys()[0] - self.SelectInterface(s, netid, mode) - s.connect((dstaddr, 53)) - expected.src = self.MyAddress(version, netid) - - # For each netid, select that network without closing the socket, and - # check that the packets sent on that socket go out on the right network. - for netid in self.tuns: - self.SelectInterface(s, netid, mode) - if not use_connect: - expected.src = self.MyAddress(version, netid) - s.sendto(UDP_PAYLOAD, (dstaddr, 53)) - connected_str = "Connected" if use_connect else "Unconnected" - msg = "%s UDPv%d socket remarked using %s: expecting %s on %s" % ( - connected_str, version, mode, desc, self.GetInterfaceName(netid)) - self.ExpectPacketOn(netid, msg, expected) - self.SelectInterface(s, None, mode) - - def testIPv4Remarking(self): - """Checks that updating the mark on an IPv4 socket changes routing.""" - self.CheckRemarking(4, False) - self.CheckRemarking(4, True) - - def testIPv6Remarking(self): - """Checks that updating the mark on an IPv6 socket changes routing.""" - self.CheckRemarking(6, False) - self.CheckRemarking(6, True) - - def testIPv6StickyPktinfo(self): - for _ in xrange(self.ITERATIONS): - for netid in self.tuns: - s = net_test.UDPSocket(AF_INET6) - - # Set a flowlabel. - net_test.SetFlowLabel(s, net_test.IPV6_ADDR, 0xdead) - s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_FLOWINFO_SEND, 1) - - # Set some destination options. - nonce = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c" - dstopts = "".join([ - "\x11\x02", # Next header=UDP, 24 bytes of options. - "\x01\x06", "\x00" * 6, # PadN, 6 bytes of padding. - "\x8b\x0c", # ILNP nonce, 12 bytes. - nonce - ]) - s.setsockopt(net_test.SOL_IPV6, IPV6_DSTOPTS, dstopts) - s.setsockopt(net_test.SOL_IPV6, IPV6_UNICAST_HOPS, 255) - - pktinfo = multinetwork_base.MakePktInfo(6, None, self.ifindices[netid]) - - # Set the sticky pktinfo option. - s.setsockopt(net_test.SOL_IPV6, IPV6_PKTINFO, pktinfo) - - # Specify the flowlabel in the destination address. - s.sendto(UDP_PAYLOAD, (net_test.IPV6_ADDR, 53, 0xdead, 0)) - - sport = s.getsockname()[1] - srcaddr = self.MyAddress(6, netid) - expected = (scapy.IPv6(src=srcaddr, dst=net_test.IPV6_ADDR, - fl=0xdead, hlim=255) / - scapy.IPv6ExtHdrDestOpt( - options=[scapy.PadN(optdata="\x00\x00\x00\x00\x00\x00"), - scapy.HBHOptUnknown(otype=0x8b, - optdata=nonce)]) / - scapy.UDP(sport=sport, dport=53) / - UDP_PAYLOAD) - msg = "IPv6 UDP using sticky pktinfo: expected UDP packet on %s" % ( - self.GetInterfaceName(netid)) - self.ExpectPacketOn(netid, msg, expected) - - def CheckPktinfoRouting(self, version): - for _ in xrange(self.ITERATIONS): - for netid in self.tuns: - family = self.GetProtocolFamily(version) - s = net_test.UDPSocket(family) - - if version == 6: - # Create a flowlabel so we can use it. - net_test.SetFlowLabel(s, net_test.IPV6_ADDR, 0xbeef) - - # Specify some arbitrary options. - cmsgs = [ - (net_test.SOL_IPV6, IPV6_HOPLIMIT, 39), - (net_test.SOL_IPV6, IPV6_TCLASS, 0x83), - (net_test.SOL_IPV6, IPV6_FLOWINFO, int(htonl(0xbeef))), - ] - else: - # Support for setting IPv4 TOS and TTL via cmsg only appeared in 3.13. - cmsgs = [] - s.setsockopt(net_test.SOL_IP, IP_TTL, 39) - s.setsockopt(net_test.SOL_IP, IP_TOS, 0x83) - - dstaddr = self.GetRemoteAddress(version) - self.SendOnNetid(version, s, dstaddr, 53, netid, UDP_PAYLOAD, cmsgs) - - sport = s.getsockname()[1] - srcaddr = self.MyAddress(version, netid) - - desc, expected = packets.UDPWithOptions(version, srcaddr, dstaddr, - sport=sport) - - msg = "IPv%d UDP using pktinfo routing: expected %s on %s" % ( - version, desc, self.GetInterfaceName(netid)) - self.ExpectPacketOn(netid, msg, expected) - - def testIPv4PktinfoRouting(self): - self.CheckPktinfoRouting(4) - - def testIPv6PktinfoRouting(self): - self.CheckPktinfoRouting(6) - - -class MarkTest(InboundMarkingTest): - - def CheckReflection(self, version, gen_packet, gen_reply): - """Checks that replies go out on the same interface as the original. - - For each combination: - - Calls gen_packet to generate a packet to that IP address. - - Writes the packet generated by gen_packet on the given tun - interface, causing the kernel to receive it. - - Checks that the kernel's reply matches the packet generated by - gen_reply. - - Args: - version: An integer, 4 or 6. - gen_packet: A function taking an IP version (an integer), a source - address and a destination address (strings), and returning a scapy - packet. - gen_reply: A function taking the same arguments as gen_packet, - plus a scapy packet, and returning a scapy packet. - """ - for netid, iif, ip_if, myaddr, remoteaddr in self.Combinations(version): - # Generate a test packet. - desc, packet = gen_packet(version, remoteaddr, myaddr) - - # Test with mark reflection enabled and disabled. - for reflect in [0, 1]: - self.SetMarkReflectSysctls(reflect) - # HACK: IPv6 ping replies always do a routing lookup with the - # interface the ping came in on. So even if mark reflection is not - # working, IPv6 ping replies will be properly reflected. Don't - # fail when that happens. - if reflect or desc == "ICMPv6 echo": - reply_desc, reply = gen_reply(version, myaddr, remoteaddr, packet) - else: - reply_desc, reply = None, None - - msg = self._FormatMessage(iif, ip_if, "reflect=%d" % reflect, - desc, reply_desc) - self._ReceiveAndExpectResponse(netid, packet, reply, msg) - - def SYNToClosedPort(self, *args): - return packets.SYN(999, *args) - - def testIPv4ICMPErrorsReflectMark(self): - self.CheckReflection(4, packets.UDP, packets.ICMPPortUnreachable) - - def testIPv6ICMPErrorsReflectMark(self): - self.CheckReflection(6, packets.UDP, packets.ICMPPortUnreachable) - - def testIPv4PingRepliesReflectMarkAndTos(self): - self.CheckReflection(4, packets.ICMPEcho, packets.ICMPReply) - - def testIPv6PingRepliesReflectMarkAndTos(self): - self.CheckReflection(6, packets.ICMPEcho, packets.ICMPReply) - - def testIPv4RSTsReflectMark(self): - self.CheckReflection(4, self.SYNToClosedPort, packets.RST) - - def testIPv6RSTsReflectMark(self): - self.CheckReflection(6, self.SYNToClosedPort, packets.RST) - - -class TCPAcceptTest(InboundMarkingTest): - - MODE_BINDTODEVICE = "SO_BINDTODEVICE" - MODE_INCOMING_MARK = "incoming mark" - MODE_EXPLICIT_MARK = "explicit mark" - MODE_UID = "uid" - - @classmethod - def setUpClass(cls): - super(TCPAcceptTest, cls).setUpClass() - - # Open a port so we can observe SYN+ACKs. Since it's a dual-stack socket it - # will accept both IPv4 and IPv6 connections. We do this here instead of in - # each test so we can use the same socket every time. That way, if a kernel - # bug causes incoming packets to mark the listening socket instead of the - # accepted socket, the test will fail as soon as the next address/interface - # combination is tried. - cls.listenport = 1234 - cls.listensocket = net_test.IPv6TCPSocket() - cls.listensocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) - cls.listensocket.bind(("::", cls.listenport)) - cls.listensocket.listen(100) - - def BounceSocket(self, s): - """Attempts to invalidate a socket's destination cache entry.""" - if s.family == AF_INET: - tos = s.getsockopt(SOL_IP, IP_TOS) - s.setsockopt(net_test.SOL_IP, IP_TOS, 53) - s.setsockopt(net_test.SOL_IP, IP_TOS, tos) - else: - # UDP, 8 bytes dstopts; PAD1, 4 bytes padding; 4 bytes zeros. - pad8 = "".join(["\x11\x00", "\x01\x04", "\x00" * 4]) - s.setsockopt(net_test.SOL_IPV6, IPV6_DSTOPTS, pad8) - s.setsockopt(net_test.SOL_IPV6, IPV6_DSTOPTS, "") - - def _SetTCPMarkAcceptSysctl(self, value): - self.SetSysctl(TCP_MARK_ACCEPT_SYSCTL, value) - - def CheckTCPConnection(self, mode, listensocket, netid, version, - myaddr, remoteaddr, packet, reply, msg): - establishing_ack = packets.ACK(version, remoteaddr, myaddr, reply)[1] - - # Attempt to confuse the kernel. - self.BounceSocket(listensocket) - - self.ReceivePacketOn(netid, establishing_ack) - - # If we're using UID routing, the accept() call has to be run as a UID that - # is routed to the specified netid, because the UID of the socket returned - # by accept() is the effective UID of the process that calls it. It doesn't - # need to be the same UID; any UID that selects the same interface will do. - with net_test.RunAsUid(self.UidForNetid(netid)): - s, _ = listensocket.accept() - - try: - # Check that data sent on the connection goes out on the right interface. - desc, data = packets.ACK(version, myaddr, remoteaddr, establishing_ack, - payload=UDP_PAYLOAD) - s.send(UDP_PAYLOAD) - self.ExpectPacketOn(netid, msg + ": expecting %s" % desc, data) - self.BounceSocket(s) - - # Keep up our end of the conversation. - ack = packets.ACK(version, remoteaddr, myaddr, data)[1] - self.BounceSocket(listensocket) - self.ReceivePacketOn(netid, ack) - - mark = self.GetSocketMark(s) - finally: - self.BounceSocket(s) - s.close() - - if mode == self.MODE_INCOMING_MARK: - self.assertEquals(netid, mark, - msg + ": Accepted socket: Expected mark %d, got %d" % ( - netid, mark)) - elif mode != self.MODE_EXPLICIT_MARK: - self.assertEquals(0, self.GetSocketMark(listensocket)) - - # Check the FIN was sent on the right interface, and ack it. We don't expect - # this to fail because by the time the connection is established things are - # likely working, but a) extra tests are always good and b) extra packets - # like the FIN (and retransmitted FINs) could cause later tests that expect - # no packets to fail. - desc, fin = packets.FIN(version, myaddr, remoteaddr, ack) - self.ExpectPacketOn(netid, msg + ": expecting %s after close" % desc, fin) - - desc, finack = packets.FIN(version, remoteaddr, myaddr, fin) - self.ReceivePacketOn(netid, finack) - - # Since we called close() earlier, the userspace socket object is gone, so - # the socket has no UID. If we're doing UID routing, the ack might be routed - # incorrectly. Not much we can do here. - desc, finackack = packets.ACK(version, myaddr, remoteaddr, finack) - if mode != self.MODE_UID: - self.ExpectPacketOn(netid, msg + ": expecting final ack", finackack) - else: - self.ClearTunQueues() - - def CheckTCP(self, version, modes): - """Checks that incoming TCP connections work. - - Args: - version: An integer, 4 or 6. - modes: A list of modes to excercise. - """ - for syncookies in [0, 2]: - for mode in modes: - for netid, iif, ip_if, myaddr, remoteaddr in self.Combinations(version): - if mode == self.MODE_UID: - listensocket = self.BuildSocket(6, net_test.TCPSocket, netid, mode) - listensocket.listen(100) - else: - listensocket = self.listensocket - - listenport = listensocket.getsockname()[1] - - accept_sysctl = 1 if mode == self.MODE_INCOMING_MARK else 0 - self._SetTCPMarkAcceptSysctl(accept_sysctl) - - bound_dev = iif if mode == self.MODE_BINDTODEVICE else None - self.BindToDevice(listensocket, bound_dev) - - mark = netid if mode == self.MODE_EXPLICIT_MARK else 0 - self.SetSocketMark(listensocket, mark) - - # Generate the packet here instead of in the outer loop, so - # subsequent TCP connections use different source ports and - # retransmissions from old connections don't confuse subsequent - # tests. - desc, packet = packets.SYN(listenport, version, remoteaddr, myaddr) - - if mode: - reply_desc, reply = packets.SYNACK(version, myaddr, remoteaddr, - packet) - else: - reply_desc, reply = None, None - - extra = "mode=%s, syncookies=%d" % (mode, syncookies) - msg = self._FormatMessage(iif, ip_if, extra, desc, reply_desc) - reply = self._ReceiveAndExpectResponse(netid, packet, reply, msg) - if reply: - self.CheckTCPConnection(mode, listensocket, netid, version, myaddr, - remoteaddr, packet, reply, msg) - - def testBasicTCP(self): - self.CheckTCP(4, [None, self.MODE_BINDTODEVICE, self.MODE_EXPLICIT_MARK]) - self.CheckTCP(6, [None, self.MODE_BINDTODEVICE, self.MODE_EXPLICIT_MARK]) - - def testIPv4MarkAccept(self): - self.CheckTCP(4, [self.MODE_INCOMING_MARK]) - - def testIPv6MarkAccept(self): - self.CheckTCP(6, [self.MODE_INCOMING_MARK]) - - @unittest.skipUnless(multinetwork_base.HAVE_UID_ROUTING, "no UID routes") - def testIPv4UidAccept(self): - self.CheckTCP(4, [self.MODE_UID]) - - @unittest.skipUnless(multinetwork_base.HAVE_UID_ROUTING, "no UID routes") - def testIPv6UidAccept(self): - self.CheckTCP(6, [self.MODE_UID]) - - def testIPv6ExplicitMark(self): - self.CheckTCP(6, [self.MODE_EXPLICIT_MARK]) - - -class RATest(multinetwork_base.MultiNetworkBaseTest): - - def testDoesNotHaveObsoleteSysctl(self): - self.assertFalse(os.path.isfile( - "/proc/sys/net/ipv6/route/autoconf_table_offset")) - - @unittest.skipUnless(multinetwork_base.HAVE_AUTOCONF_TABLE, - "no support for per-table autoconf") - def testPurgeDefaultRouters(self): - - def CheckIPv6Connectivity(expect_connectivity): - for netid in self.NETIDS: - s = net_test.UDPSocket(AF_INET6) - self.SetSocketMark(s, netid) - if expect_connectivity: - self.assertTrue(s.sendto(UDP_PAYLOAD, (net_test.IPV6_ADDR, 1234))) - else: - self.assertRaisesErrno(errno.ENETUNREACH, s.sendto, UDP_PAYLOAD, - (net_test.IPV6_ADDR, 1234)) - - try: - CheckIPv6Connectivity(True) - self.SetIPv6SysctlOnAllIfaces("accept_ra", 1) - self.SetSysctl("/proc/sys/net/ipv6/conf/all/forwarding", 1) - CheckIPv6Connectivity(False) - finally: - self.SetSysctl("/proc/sys/net/ipv6/conf/all/forwarding", 0) - for netid in self.NETIDS: - self.SendRA(netid) - CheckIPv6Connectivity(True) - - def testOnlinkCommunication(self): - """Checks that on-link communication goes direct and not through routers.""" - for netid in self.tuns: - # Send a UDP packet to a random on-link destination. - s = net_test.UDPSocket(AF_INET6) - iface = self.GetInterfaceName(netid) - self.BindToDevice(s, iface) - # dstaddr can never be our address because GetRandomDestination only fills - # in the lower 32 bits, but our address has 0xff in the byte before that - # (since it's constructed from the EUI-64 and so has ff:fe in the middle). - dstaddr = self.GetRandomDestination(self.IPv6Prefix(netid)) - s.sendto(UDP_PAYLOAD, (dstaddr, 53)) - - # Expect an NS for that destination on the interface. - myaddr = self.MyAddress(6, netid) - mymac = self.MyMacAddress(netid) - desc, expected = packets.NS(myaddr, dstaddr, mymac) - msg = "Sending UDP packet to on-link destination: expecting %s" % desc - time.sleep(0.0001) # Required to make the test work on kernel 3.1(!) - self.ExpectPacketOn(netid, msg, expected) - - # Send an NA. - tgtmac = "02:00:00:00:%02x:99" % netid - _, reply = packets.NA(dstaddr, myaddr, tgtmac) - # Don't use ReceivePacketOn, since that uses the router's MAC address as - # the source. Instead, construct our own Ethernet header with source - # MAC of tgtmac. - reply = scapy.Ether(src=tgtmac, dst=mymac) / reply - self.ReceiveEtherPacketOn(netid, reply) - - # Expect the kernel to send the original UDP packet now that the ND cache - # entry has been populated. - sport = s.getsockname()[1] - desc, expected = packets.UDP(6, myaddr, dstaddr, sport=sport) - msg = "After NA response, expecting %s" % desc - self.ExpectPacketOn(netid, msg, expected) - - # This test documents a known issue: routing tables are never deleted. - @unittest.skipUnless(multinetwork_base.HAVE_AUTOCONF_TABLE, - "no support for per-table autoconf") - def testLeftoverRoutes(self): - def GetNumRoutes(): - return len(open("/proc/net/ipv6_route").readlines()) - - num_routes = GetNumRoutes() - for i in xrange(10, 20): - try: - self.tuns[i] = self.CreateTunInterface(i) - self.SendRA(i) - self.tuns[i].close() - finally: - del self.tuns[i] - self.assertLess(num_routes, GetNumRoutes()) - - -class PMTUTest(InboundMarkingTest): - - PAYLOAD_SIZE = 1400 - - # Socket options to change PMTU behaviour. - IP_MTU_DISCOVER = 10 - IP_PMTUDISC_DO = 1 - IPV6_DONTFRAG = 62 - - # Socket options to get the MTU. - IP_MTU = 14 - IPV6_PATHMTU = 61 - - def GetSocketMTU(self, version, s): - if version == 6: - ip6_mtuinfo = s.getsockopt(net_test.SOL_IPV6, self.IPV6_PATHMTU, 32) - unused_sockaddr, mtu = struct.unpack("=28sI", ip6_mtuinfo) - return mtu - else: - return s.getsockopt(net_test.SOL_IP, self.IP_MTU) - - def DisableFragmentationAndReportErrors(self, version, s): - if version == 4: - s.setsockopt(net_test.SOL_IP, self.IP_MTU_DISCOVER, self.IP_PMTUDISC_DO) - s.setsockopt(net_test.SOL_IP, net_test.IP_RECVERR, 1) - else: - s.setsockopt(net_test.SOL_IPV6, self.IPV6_DONTFRAG, 1) - s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_RECVERR, 1) - - def CheckPMTU(self, version, use_connect, modes): - - def SendBigPacket(version, s, dstaddr, netid, payload): - if use_connect: - s.send(payload) - else: - self.SendOnNetid(version, s, dstaddr, 1234, netid, payload, []) - - for netid in self.tuns: - for mode in modes: - s = self.BuildSocket(version, net_test.UDPSocket, netid, mode) - self.DisableFragmentationAndReportErrors(version, s) - - srcaddr = self.MyAddress(version, netid) - dst_prefix, intermediate = { - 4: ("172.19.", "172.16.9.12"), - 6: ("2001:db8::", "2001:db8::1") - }[version] - dstaddr = self.GetRandomDestination(dst_prefix) - - if use_connect: - s.connect((dstaddr, 1234)) - - payload = self.PAYLOAD_SIZE * "a" - - # Send a packet and receive a packet too big. - SendBigPacket(version, s, dstaddr, netid, payload) - received = self.ReadAllPacketsOn(netid) - self.assertEquals(1, len(received)) - _, toobig = packets.ICMPPacketTooBig(version, intermediate, srcaddr, - received[0]) - self.ReceivePacketOn(netid, toobig) - - # Check that another send on the same socket returns EMSGSIZE. - self.assertRaisesErrno( - errno.EMSGSIZE, - SendBigPacket, version, s, dstaddr, netid, payload) - - # If this is a connected socket, make sure the socket MTU was set. - # Note that in IPv4 this only started working in Linux 3.6! - if use_connect and (version == 6 or net_test.LINUX_VERSION >= (3, 6)): - self.assertEquals(1280, self.GetSocketMTU(version, s)) - - s.close() - - # Check that other sockets pick up the PMTU we have been told about by - # connecting another socket to the same destination and getting its MTU. - # This new socket can use any method to select its outgoing interface; - # here we use a mark for simplicity. - s2 = self.BuildSocket(version, net_test.UDPSocket, netid, "mark") - s2.connect((dstaddr, 1234)) - self.assertEquals(1280, self.GetSocketMTU(version, s2)) - - # Also check the MTU reported by ip route get, this time using the oif. - routes = self.iproute.GetRoutes(dstaddr, self.ifindices[netid], 0, None) - self.assertTrue(routes) - route = routes[0] - rtmsg, attributes = route - self.assertEquals(iproute.RTN_UNICAST, rtmsg.type) - metrics = attributes["RTA_METRICS"] - self.assertEquals(metrics["RTAX_MTU"], 1280) - - def testIPv4BasicPMTU(self): - """Tests IPv4 path MTU discovery. - - Relevant kernel commits: - upstream net-next: - 6a66271 ipv4, fib: pass LOOPBACK_IFINDEX instead of 0 to flowi4_iif - - android-3.10: - 4bc64dd ipv4, fib: pass LOOPBACK_IFINDEX instead of 0 to flowi4_iif - """ - - self.CheckPMTU(4, True, ["mark", "oif"]) - self.CheckPMTU(4, False, ["mark", "oif"]) - - def testIPv6BasicPMTU(self): - self.CheckPMTU(6, True, ["mark", "oif"]) - self.CheckPMTU(6, False, ["mark", "oif"]) - - @unittest.skipUnless(multinetwork_base.HAVE_UID_ROUTING, "no UID routes") - def testIPv4UIDPMTU(self): - self.CheckPMTU(4, True, ["uid"]) - self.CheckPMTU(4, False, ["uid"]) - - @unittest.skipUnless(multinetwork_base.HAVE_UID_ROUTING, "no UID routes") - def testIPv6UIDPMTU(self): - self.CheckPMTU(6, True, ["uid"]) - self.CheckPMTU(6, False, ["uid"]) - - # Making Path MTU Discovery work on unmarked sockets requires that mark - # reflection be enabled. Otherwise the kernel has no way to know what routing - # table the original packet used, and thus it won't be able to clone the - # correct route. - - def testIPv4UnmarkedSocketPMTU(self): - self.SetMarkReflectSysctls(1) - try: - self.CheckPMTU(4, False, [None]) - finally: - self.SetMarkReflectSysctls(0) - - def testIPv6UnmarkedSocketPMTU(self): - self.SetMarkReflectSysctls(1) - try: - self.CheckPMTU(6, False, [None]) - finally: - self.SetMarkReflectSysctls(0) - - -@unittest.skipUnless(multinetwork_base.HAVE_UID_ROUTING, "no UID routes") -class UidRoutingTest(multinetwork_base.MultiNetworkBaseTest): - """Tests that per-UID routing works properly. - - Relevant kernel commits: - android-3.4: - 0b42874 net: core: Support UID-based routing. - 0836a0c Handle 'sk' being NULL in UID-based routing. - - android-3.10: - 99a6ea4 net: core: Support UID-based routing. - 455b09d Handle 'sk' being NULL in UID-based routing. - """ - - def GetRulesAtPriority(self, version, priority): - rules = self.iproute.DumpRules(version) - out = [(rule, attributes) for rule, attributes in rules - if attributes.get("FRA_PRIORITY", 0) == priority] - return out - - def CheckInitialTablesHaveNoUIDs(self, version): - rules = [] - for priority in [0, 32766, 32767]: - rules.extend(self.GetRulesAtPriority(version, priority)) - for _, attributes in rules: - self.assertNotIn("FRA_UID_START", attributes) - self.assertNotIn("FRA_UID_END", attributes) - - def testIPv4InitialTablesHaveNoUIDs(self): - self.CheckInitialTablesHaveNoUIDs(4) - - def testIPv6InitialTablesHaveNoUIDs(self): - self.CheckInitialTablesHaveNoUIDs(6) - - def CheckGetAndSetRules(self, version): - def Random(): - return random.randint(1000000, 2000000) - - start, end = tuple(sorted([Random(), Random()])) - table = Random() - priority = Random() - - try: - self.iproute.UidRangeRule(version, True, start, end, table, - priority=priority) - - rules = self.GetRulesAtPriority(version, priority) - self.assertTrue(rules) - _, attributes = rules[-1] - self.assertEquals(priority, attributes["FRA_PRIORITY"]) - self.assertEquals(start, attributes["FRA_UID_START"]) - self.assertEquals(end, attributes["FRA_UID_END"]) - self.assertEquals(table, attributes["FRA_TABLE"]) - finally: - self.iproute.UidRangeRule(version, False, start, end, table, - priority=priority) - - def testIPv4GetAndSetRules(self): - self.CheckGetAndSetRules(4) - - def testIPv6GetAndSetRules(self): - self.CheckGetAndSetRules(6) - - def ExpectNoRoute(self, addr, oif, mark, uid): - # The lack of a route may be either an error, or an unreachable route. - try: - routes = self.iproute.GetRoutes(addr, oif, mark, uid) - rtmsg, _ = routes[0] - self.assertEquals(iproute.RTN_UNREACHABLE, rtmsg.type) - except IOError, e: - if int(e.errno) != -int(errno.ENETUNREACH): - raise e - - def ExpectRoute(self, addr, oif, mark, uid): - routes = self.iproute.GetRoutes(addr, oif, mark, uid) - rtmsg, _ = routes[0] - self.assertEquals(iproute.RTN_UNICAST, rtmsg.type) - - def CheckGetRoute(self, version, addr): - self.ExpectNoRoute(addr, 0, 0, 0) - for netid in self.NETIDS: - uid = self.UidForNetid(netid) - self.ExpectRoute(addr, 0, 0, uid) - self.ExpectNoRoute(addr, 0, 0, 0) - - def testIPv4RouteGet(self): - self.CheckGetRoute(4, net_test.IPV4_ADDR) - - def testIPv6RouteGet(self): - self.CheckGetRoute(6, net_test.IPV6_ADDR) - - -class RulesTest(net_test.NetworkTest): - - RULE_PRIORITY = 99999 - - def setUp(self): - self.iproute = iproute.IPRoute() - for version in [4, 6]: - self.iproute.DeleteRulesAtPriority(version, self.RULE_PRIORITY) - - def tearDown(self): - for version in [4, 6]: - self.iproute.DeleteRulesAtPriority(version, self.RULE_PRIORITY) - - def testRuleDeletionMatchesTable(self): - for version in [4, 6]: - # Add rules with mark 300 pointing at tables 301 and 302. - # This checks for a kernel bug where deletion request for tables > 256 - # ignored the table. - self.iproute.FwmarkRule(version, True, 300, 301, - priority=self.RULE_PRIORITY) - self.iproute.FwmarkRule(version, True, 300, 302, - priority=self.RULE_PRIORITY) - # Delete rule with mark 300 pointing at table 302. - self.iproute.FwmarkRule(version, False, 300, 302, - priority=self.RULE_PRIORITY) - # Check that the rule pointing at table 301 is still around. - attributes = [a for _, a in self.iproute.DumpRules(version) - if a.get("FRA_PRIORITY", 0) == self.RULE_PRIORITY] - self.assertEquals(1, len(attributes)) - self.assertEquals(301, attributes[0]["FRA_TABLE"]) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/net_test/neighbour_test.py b/tests/net_test/neighbour_test.py deleted file mode 100755 index 1e7739e4..00000000 --- a/tests/net_test/neighbour_test.py +++ /dev/null @@ -1,297 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2015 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 errno -import random -from socket import * # pylint: disable=wildcard-import -import time -import unittest - -from scapy import all as scapy - -import multinetwork_base -import net_test - - -RTMGRP_NEIGH = 4 - -NUD_INCOMPLETE = 0x01 -NUD_REACHABLE = 0x02 -NUD_STALE = 0x04 -NUD_DELAY = 0x08 -NUD_PROBE = 0x10 -NUD_FAILED = 0x20 -NUD_PERMANENT = 0x80 - - -# TODO: Support IPv4. -class NeighbourTest(multinetwork_base.MultiNetworkBaseTest): - - # Set a 100-ms retrans timer so we can test for ND retransmits without - # waiting too long. Apparently this cannot go below 500ms. - RETRANS_TIME_MS = 500 - - # This can only be in seconds, so 1000 is the minimum. - DELAY_TIME_MS = 1000 - - # Unfortunately, this must be above the delay timer or the kernel ND code will - # not behave correctly (e.g., go straight from REACHABLE into DELAY). This is - # is fuzzed by the kernel from 0.5x to 1.5x of its value, so we need a value - # that's 2x the delay timer. - REACHABLE_TIME_MS = 2 * DELAY_TIME_MS - - @classmethod - def setUpClass(cls): - super(NeighbourTest, cls).setUpClass() - for netid in cls.tuns: - iface = cls.GetInterfaceName(netid) - # This can't be set in an RA. - cls.SetSysctl( - "/proc/sys/net/ipv6/neigh/%s/delay_first_probe_time" % iface, - cls.DELAY_TIME_MS / 1000) - - def setUp(self): - super(NeighbourTest, self).setUp() - - for netid in self.tuns: - # Clear the ND cache entries for all routers, so each test starts with - # the IPv6 default router in state STALE. - addr = self._RouterAddress(netid, 6) - ifindex = self.ifindices[netid] - self.iproute.UpdateNeighbour(6, addr, None, ifindex, NUD_FAILED) - - # Configure IPv6 by sending an RA. - self.SendRA(netid, - retranstimer=self.RETRANS_TIME_MS, - reachabletime=self.REACHABLE_TIME_MS) - - self.sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) - self.sock.bind((0, RTMGRP_NEIGH)) - net_test.SetNonBlocking(self.sock) - - self.netid = random.choice(self.tuns.keys()) - self.ifindex = self.ifindices[self.netid] - - def GetNeighbour(self, addr): - version = 6 if ":" in addr else 4 - for msg, args in self.iproute.DumpNeighbours(version): - if args["NDA_DST"] == addr: - return msg, args - - def GetNdEntry(self, addr): - return self.GetNeighbour(addr) - - def CheckNoNdEvents(self): - self.assertRaisesErrno(errno.EAGAIN, self.sock.recvfrom, 4096, MSG_PEEK) - - def assertNeighbourState(self, state, addr): - self.assertEquals(state, self.GetNdEntry(addr)[0].state) - - def assertNeighbourAttr(self, addr, name, value): - self.assertEquals(value, self.GetNdEntry(addr)[1][name]) - - def ExpectNeighbourNotification(self, addr, state, attrs=None): - msg = self.sock.recv(4096) - msg, actual_attrs = self.iproute.ParseNeighbourMessage(msg) - self.assertEquals(addr, actual_attrs["NDA_DST"]) - self.assertEquals(state, msg.state) - if attrs: - for name in attrs: - self.assertEquals(attrs[name], actual_attrs[name]) - - def ExpectProbe(self, is_unicast, addr): - version = 6 if ":" in addr else 4 - if version == 6: - llsrc = self.MyMacAddress(self.netid) - if is_unicast: - src = self.MyLinkLocalAddress(self.netid) - dst = addr - else: - solicited = inet_pton(AF_INET6, addr) - last3bytes = tuple([ord(b) for b in solicited[-3:]]) - dst = "ff02::1:ff%02x:%02x%02x" % last3bytes - src = self.MyAddress(6, self.netid) - expected = ( - scapy.IPv6(src=src, dst=dst) / - scapy.ICMPv6ND_NS(tgt=addr) / - scapy.ICMPv6NDOptSrcLLAddr(lladdr=llsrc) - ) - msg = "%s probe" % ("Unicast" if is_unicast else "Multicast") - self.ExpectPacketOn(self.netid, msg, expected) - else: - raise NotImplementedError - - def ExpectUnicastProbe(self, addr): - self.ExpectProbe(True, addr) - - def ExpectMulticastNS(self, addr): - self.ExpectProbe(False, addr) - - def ReceiveUnicastAdvertisement(self, addr, mac, srcaddr=None, dstaddr=None, - S=1, O=0, R=1): - version = 6 if ":" in addr else 4 - if srcaddr is None: - srcaddr = addr - if dstaddr is None: - dstaddr = self.MyLinkLocalAddress(self.netid) - if version == 6: - packet = ( - scapy.Ether(src=mac, dst=self.MyMacAddress(self.netid)) / - scapy.IPv6(src=srcaddr, dst=dstaddr) / - scapy.ICMPv6ND_NA(tgt=addr, S=S, O=O, R=R) / - scapy.ICMPv6NDOptDstLLAddr(lladdr=mac) - ) - self.ReceiveEtherPacketOn(self.netid, packet) - else: - raise NotImplementedError - - def MonitorSleepMs(self, interval, addr): - slept = 0 - while slept < interval: - sleep_ms = min(100, interval - slept) - time.sleep(sleep_ms / 1000.0) - slept += sleep_ms - print self.GetNdEntry(addr) - - def MonitorSleep(self, intervalseconds, addr): - self.MonitorSleepMs(intervalseconds * 1000, addr) - - def SleepMs(self, ms): - time.sleep(ms / 1000.0) - - def testNotifications(self): - """Tests neighbour notifications. - - Relevant kernel commits: - upstream net-next: - 765c9c6 neigh: Better handling of transition to NUD_PROBE state - 53385d2 neigh: Netlink notification for administrative NUD state change - (only checked on kernel v3.13+, not on v3.10) - - android-3.10: - e4a6d6b neigh: Better handling of transition to NUD_PROBE state - - android-3.18: - 2011e72 neigh: Better handling of transition to NUD_PROBE state - """ - - router4 = self._RouterAddress(self.netid, 4) - router6 = self._RouterAddress(self.netid, 6) - self.assertNeighbourState(NUD_PERMANENT, router4) - self.assertNeighbourState(NUD_STALE, router6) - - # Send a packet and check that we go into DELAY. - routing_mode = random.choice(["mark", "oif", "uid"]) - s = self.BuildSocket(6, net_test.UDPSocket, self.netid, routing_mode) - s.connect((net_test.IPV6_ADDR, 53)) - s.send(net_test.UDP_PAYLOAD) - self.assertNeighbourState(NUD_DELAY, router6) - - # Wait for the probe interval, then check that we're in PROBE, and that the - # kernel has notified us. - self.SleepMs(self.DELAY_TIME_MS) - self.ExpectNeighbourNotification(router6, NUD_PROBE) - self.assertNeighbourState(NUD_PROBE, router6) - self.ExpectUnicastProbe(router6) - - # Respond to the NS and verify we're in REACHABLE again. - self.ReceiveUnicastAdvertisement(router6, self.RouterMacAddress(self.netid)) - self.assertNeighbourState(NUD_REACHABLE, router6) - if net_test.LINUX_VERSION >= (3, 13, 0): - # commit 53385d2 (v3.13) "neigh: Netlink notification for administrative - # NUD state change" produces notifications for NUD_REACHABLE, but these - # are not generated on earlier kernels. - self.ExpectNeighbourNotification(router6, NUD_REACHABLE) - - # Wait until the reachable time has passed, and verify we're in STALE. - self.SleepMs(self.REACHABLE_TIME_MS * 1.5) - self.assertNeighbourState(NUD_STALE, router6) - self.ExpectNeighbourNotification(router6, NUD_STALE) - - # Send a packet, and verify we go into DELAY and then to PROBE. - s.send(net_test.UDP_PAYLOAD) - self.assertNeighbourState(NUD_DELAY, router6) - self.SleepMs(self.DELAY_TIME_MS) - self.assertNeighbourState(NUD_PROBE, router6) - self.ExpectNeighbourNotification(router6, NUD_PROBE) - - # Wait for the probes to time out, and expect a FAILED notification. - self.assertNeighbourAttr(router6, "NDA_PROBES", 1) - self.ExpectUnicastProbe(router6) - - self.SleepMs(self.RETRANS_TIME_MS) - self.ExpectUnicastProbe(router6) - self.assertNeighbourAttr(router6, "NDA_PROBES", 2) - - self.SleepMs(self.RETRANS_TIME_MS) - self.ExpectUnicastProbe(router6) - self.assertNeighbourAttr(router6, "NDA_PROBES", 3) - - self.SleepMs(self.RETRANS_TIME_MS) - self.assertNeighbourState(NUD_FAILED, router6) - self.ExpectNeighbourNotification(router6, NUD_FAILED, {"NDA_PROBES": 3}) - - def testRepeatedProbes(self): - router4 = self._RouterAddress(self.netid, 4) - router6 = self._RouterAddress(self.netid, 6) - routermac = self.RouterMacAddress(self.netid) - self.assertNeighbourState(NUD_PERMANENT, router4) - self.assertNeighbourState(NUD_STALE, router6) - - def ForceProbe(addr, mac): - self.iproute.UpdateNeighbour(6, addr, None, self.ifindex, NUD_PROBE) - self.assertNeighbourState(NUD_PROBE, addr) - self.SleepMs(1) # TODO: Why is this necessary? - self.assertNeighbourState(NUD_PROBE, addr) - self.ExpectUnicastProbe(addr) - self.ReceiveUnicastAdvertisement(addr, mac) - self.assertNeighbourState(NUD_REACHABLE, addr) - - for _ in xrange(5): - ForceProbe(router6, routermac) - - def testIsRouterFlag(self): - router6 = self._RouterAddress(self.netid, 6) - self.assertNeighbourState(NUD_STALE, router6) - - # Get into FAILED. - ifindex = self.ifindices[self.netid] - self.iproute.UpdateNeighbour(6, router6, None, ifindex, NUD_FAILED) - self.ExpectNeighbourNotification(router6, NUD_FAILED) - self.assertNeighbourState(NUD_FAILED, router6) - - time.sleep(1) - - # Send another packet and expect a multicast NS. - routing_mode = random.choice(["mark", "oif", "uid"]) - s = self.BuildSocket(6, net_test.UDPSocket, self.netid, routing_mode) - s.connect((net_test.IPV6_ADDR, 53)) - s.send(net_test.UDP_PAYLOAD) - self.ExpectMulticastNS(router6) - - # Receive a unicast NA with the R flag set to 0. - self.ReceiveUnicastAdvertisement(router6, self.RouterMacAddress(self.netid), - srcaddr=self._RouterAddress(self.netid, 6), - dstaddr=self.MyAddress(6, self.netid), - S=1, O=0, R=0) - - # Expect that this takes us to REACHABLE. - self.ExpectNeighbourNotification(router6, NUD_REACHABLE) - self.assertNeighbourState(NUD_REACHABLE, router6) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/net_test/net_test.py b/tests/net_test/net_test.py deleted file mode 100755 index 71429605..00000000 --- a/tests/net_test/net_test.py +++ /dev/null @@ -1,399 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2014 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 fcntl -import os -import random -import re -from socket import * # pylint: disable=wildcard-import -import struct -import unittest - -from scapy import all as scapy - -SOL_IPV6 = 41 -IP_RECVERR = 11 -IPV6_RECVERR = 25 -IP_TRANSPARENT = 19 -IPV6_TRANSPARENT = 75 -IPV6_TCLASS = 67 -IPV6_FLOWLABEL_MGR = 32 -IPV6_FLOWINFO_SEND = 33 - -SO_BINDTODEVICE = 25 -SO_MARK = 36 -SO_PROTOCOL = 38 -SO_DOMAIN = 39 - -ETH_P_IP = 0x0800 -ETH_P_IPV6 = 0x86dd - -IPPROTO_GRE = 47 - -SIOCSIFHWADDR = 0x8924 - -IPV6_FL_A_GET = 0 -IPV6_FL_A_PUT = 1 -IPV6_FL_A_RENEW = 1 - -IPV6_FL_F_CREATE = 1 -IPV6_FL_F_EXCL = 2 - -IPV6_FL_S_NONE = 0 -IPV6_FL_S_EXCL = 1 -IPV6_FL_S_ANY = 255 - -IFNAMSIZ = 16 - -IPV4_PING = "\x08\x00\x00\x00\x0a\xce\x00\x03" -IPV6_PING = "\x80\x00\x00\x00\x0a\xce\x00\x03" - -IPV4_ADDR = "8.8.8.8" -IPV6_ADDR = "2001:4860:4860::8888" - -IPV6_SEQ_DGRAM_HEADER = (" sl " - "local_address " - "remote_address " - "st tx_queue rx_queue tr tm->when retrnsmt" - " uid timeout inode ref pointer drops\n") - -# Arbitrary packet payload. -UDP_PAYLOAD = str(scapy.DNS(rd=1, - id=random.randint(0, 65535), - qd=scapy.DNSQR(qname="wWW.GoOGle.CoM", - qtype="AAAA"))) - -# Unix group to use if we want to open sockets as non-root. -AID_INET = 3003 - - -def LinuxVersion(): - # Example: "3.4.67-00753-gb7a556f". - # Get the part before the dash. - version = os.uname()[2].split("-")[0] - # 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(".")) - return version - - -LINUX_VERSION = LinuxVersion() - - -def SetSocketTimeout(sock, ms): - s = ms / 1000 - us = (ms % 1000) * 1000 - sock.setsockopt(SOL_SOCKET, SO_RCVTIMEO, struct.pack("LL", s, us)) - - -def SetSocketTos(s, tos): - level = {AF_INET: SOL_IP, AF_INET6: SOL_IPV6}[s.family] - option = {AF_INET: IP_TOS, AF_INET6: IPV6_TCLASS}[s.family] - s.setsockopt(level, option, tos) - - -def SetNonBlocking(fd): - flags = fcntl.fcntl(fd, fcntl.F_GETFL, 0) - fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) - - -# Convenience functions to create sockets. -def Socket(family, sock_type, protocol): - s = socket(family, sock_type, protocol) - SetSocketTimeout(s, 1000) - return s - - -def PingSocket(family): - proto = {AF_INET: IPPROTO_ICMP, AF_INET6: IPPROTO_ICMPV6}[family] - return Socket(family, SOCK_DGRAM, proto) - - -def IPv4PingSocket(): - return PingSocket(AF_INET) - - -def IPv6PingSocket(): - return PingSocket(AF_INET6) - - -def TCPSocket(family): - s = Socket(family, SOCK_STREAM, IPPROTO_TCP) - SetNonBlocking(s.fileno()) - return s - - -def IPv4TCPSocket(): - return TCPSocket(AF_INET) - - -def IPv6TCPSocket(): - return TCPSocket(AF_INET6) - - -def UDPSocket(family): - return Socket(family, SOCK_DGRAM, IPPROTO_UDP) - - -def RawGRESocket(family): - s = Socket(family, SOCK_RAW, IPPROTO_GRE) - return s - - -def EnableFinWait(sock): - # Disabling SO_LINGER causes sockets to go into FIN_WAIT on close(). - sock.setsockopt(SOL_SOCKET, SO_LINGER, struct.pack("ii", 0, 0)) - -def DisableFinWait(sock): - # Enabling SO_LINGER with a timeout of zero causes close() to send RST. - sock.setsockopt(SOL_SOCKET, SO_LINGER, struct.pack("ii", 1, 0)) - - -def CreateSocketPair(family, socktype, addr): - clientsock = socket(family, socktype, 0) - listensock = socket(family, socktype, 0) - listensock.bind((addr, 0)) - addr = listensock.getsockname() - listensock.listen(1) - clientsock.connect(addr) - acceptedsock, _ = listensock.accept() - DisableFinWait(clientsock) - DisableFinWait(acceptedsock) - listensock.close() - return clientsock, acceptedsock - - -def GetInterfaceIndex(ifname): - s = IPv4PingSocket() - ifr = struct.pack("%dsi" % IFNAMSIZ, ifname, 0) - ifr = fcntl.ioctl(s, scapy.SIOCGIFINDEX, ifr) - return struct.unpack("%dsi" % IFNAMSIZ, ifr)[1] - - -def SetInterfaceHWAddr(ifname, hwaddr): - s = IPv4PingSocket() - hwaddr = hwaddr.replace(":", "") - hwaddr = hwaddr.decode("hex") - if len(hwaddr) != 6: - raise ValueError("Unknown hardware address length %d" % len(hwaddr)) - ifr = struct.pack("%dsH6s" % IFNAMSIZ, ifname, scapy.ARPHDR_ETHER, hwaddr) - fcntl.ioctl(s, SIOCSIFHWADDR, ifr) - - -def SetInterfaceState(ifname, up): - s = IPv4PingSocket() - ifr = struct.pack("%dsH" % IFNAMSIZ, ifname, 0) - ifr = fcntl.ioctl(s, scapy.SIOCGIFFLAGS, ifr) - _, flags = struct.unpack("%dsH" % IFNAMSIZ, ifr) - if up: - flags |= scapy.IFF_UP - else: - flags &= ~scapy.IFF_UP - ifr = struct.pack("%dsH" % IFNAMSIZ, ifname, flags) - ifr = fcntl.ioctl(s, scapy.SIOCSIFFLAGS, ifr) - - -def SetInterfaceUp(ifname): - return SetInterfaceState(ifname, True) - - -def SetInterfaceDown(ifname): - return SetInterfaceState(ifname, False) - - -def FormatProcAddress(unformatted): - groups = [] - for i in xrange(0, len(unformatted), 4): - groups.append(unformatted[i:i+4]) - formatted = ":".join(groups) - # Compress the address. - address = inet_ntop(AF_INET6, inet_pton(AF_INET6, formatted)) - return address - - -def FormatSockStatAddress(address): - if ":" in address: - family = AF_INET6 - else: - family = AF_INET - binary = inet_pton(family, address) - out = "" - for i in xrange(0, len(binary), 4): - out += "%08X" % struct.unpack("=L", binary[i:i+4]) - return out - - -def GetLinkAddress(ifname, linklocal): - addresses = open("/proc/net/if_inet6").readlines() - for address in addresses: - address = [s for s in address.strip().split(" ") if s] - if address[5] == ifname: - if (linklocal and address[0].startswith("fe80") - or not linklocal and not address[0].startswith("fe80")): - # Convert the address from raw hex to something with colons in it. - return FormatProcAddress(address[0]) - return None - - -def GetDefaultRoute(version=6): - if version == 6: - routes = open("/proc/net/ipv6_route").readlines() - for route in routes: - route = [s for s in route.strip().split(" ") if s] - if (route[0] == "00000000000000000000000000000000" and route[1] == "00" - # Routes in non-default tables end up in /proc/net/ipv6_route!!! - and route[9] != "lo" and not route[9].startswith("nettest")): - return FormatProcAddress(route[4]), route[9] - raise ValueError("No IPv6 default route found") - elif version == 4: - routes = open("/proc/net/route").readlines() - for route in routes: - route = [s for s in route.strip().split("\t") if s] - if route[1] == "00000000" and route[7] == "00000000": - gw, iface = route[2], route[0] - gw = inet_ntop(AF_INET, gw.decode("hex")[::-1]) - return gw, iface - raise ValueError("No IPv4 default route found") - else: - raise ValueError("Don't know about IPv%s" % version) - - -def GetDefaultRouteInterface(): - unused_gw, iface = GetDefaultRoute() - return iface - - -def MakeFlowLabelOption(addr, label): - # struct in6_flowlabel_req { - # struct in6_addr flr_dst; - # __be32 flr_label; - # __u8 flr_action; - # __u8 flr_share; - # __u16 flr_flags; - # __u16 flr_expires; - # __u16 flr_linger; - # __u32 __flr_pad; - # /* Options in format of IPV6_PKTOPTIONS */ - # }; - fmt = "16sIBBHHH4s" - assert struct.calcsize(fmt) == 32 - addr = inet_pton(AF_INET6, addr) - assert len(addr) == 16 - label = htonl(label & 0xfffff) - action = IPV6_FL_A_GET - share = IPV6_FL_S_ANY - flags = IPV6_FL_F_CREATE - pad = "\x00" * 4 - return struct.pack(fmt, addr, label, action, share, flags, 0, 0, pad) - - -def SetFlowLabel(s, addr, label): - opt = MakeFlowLabelOption(addr, label) - s.setsockopt(SOL_IPV6, IPV6_FLOWLABEL_MGR, opt) - # Caller also needs to do s.setsockopt(SOL_IPV6, IPV6_FLOWINFO_SEND, 1). - - -# Determine network configuration. -try: - GetDefaultRoute(version=4) - HAVE_IPV4 = True -except ValueError: - HAVE_IPV4 = False - -try: - GetDefaultRoute(version=6) - HAVE_IPV6 = True -except ValueError: - HAVE_IPV6 = False - - -class RunAsUid(object): - """Context guard to run a code block as a given UID.""" - - def __init__(self, uid): - self.uid = uid - - def __enter__(self): - if self.uid: - self.saved_uid = os.geteuid() - self.saved_groups = os.getgroups() - if self.uid: - os.setgroups(self.saved_groups + [AID_INET]) - os.seteuid(self.uid) - - def __exit__(self, unused_type, unused_value, unused_traceback): - if self.uid: - os.seteuid(self.saved_uid) - os.setgroups(self.saved_groups) - - -class NetworkTest(unittest.TestCase): - - def assertRaisesErrno(self, err_num, f, *args): - msg = os.strerror(err_num) - self.assertRaisesRegexp(EnvironmentError, msg, f, *args) - - def ReadProcNetSocket(self, protocol): - # Read file. - filename = "/proc/net/%s" % protocol - lines = open(filename).readlines() - - # Possibly check, and strip, header. - if protocol in ["icmp6", "raw6", "udp6"]: - self.assertEqual(IPV6_SEQ_DGRAM_HEADER, lines[0]) - lines = lines[1:] - - # Check contents. - if protocol.endswith("6"): - addrlen = 32 - else: - addrlen = 8 - - 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]+|)$" - elif re.match("icmp|udp|raw", protocol): - # Drops. - end_regexp = " +([0-9]+) *$" - else: - raise ValueError("Don't know how to parse %s" % filename) - - regexp = re.compile(r" *(\d+): " # bucket - "([0-9A-F]{%d}:[0-9A-F]{4}) " # srcaddr, port - "([0-9A-F]{%d}:[0-9A-F]{4}) " # dstaddr, port - "([0-9A-F][0-9A-F]) " # state - "([0-9A-F]{8}:[0-9A-F]{8}) " # mem - "([0-9A-F]{2}:[0-9A-F]{8}) " # ? - "([0-9A-F]{8}) +" # ? - "([0-9]+) +" # uid - "([0-9]+) +" # timeout - "([0-9]+) +" # inode - "([0-9]+) +" # refcnt - "([0-9a-f]+)" # sp - "%s" # icmp has spaces - % (addrlen, addrlen, end_regexp)) - # Return a list of lists with only source / dest addresses for now. - # TODO: consider returning a dict or namedtuple instead. - out = [] - for line in lines: - (_, src, dst, state, mem, - _, _, uid, _, _, refcnt, _, extra) = regexp.match(line).groups() - out.append([src, dst, state, mem, uid, refcnt, extra]) - return out - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/net_test/net_test.sh b/tests/net_test/net_test.sh deleted file mode 100755 index acac6602..00000000 --- a/tests/net_test/net_test.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -# In case IPv6 is compiled as a module. -[ -f /proc/net/if_inet6 ] || insmod $DIR/kernel/net-next/net/ipv6/ipv6.ko - -# Minimal network setup. -ip link set lo up -ip link set lo mtu 16436 -ip link set eth0 up - -# Allow people to run ping. -echo "0 65536" > /proc/sys/net/ipv4/ping_group_range - -# Fall out to a shell once the test completes or if there's an error. -trap "exec /bin/bash" ERR EXIT - -# Find and run the test. -test=$(cat /proc/cmdline | sed -re 's/.*net_test=([^ ]*).*/\1/g') -echo -e "Running $test\n" -$test diff --git a/tests/net_test/netlink.py b/tests/net_test/netlink.py deleted file mode 100644 index 261350b4..00000000 --- a/tests/net_test/netlink.py +++ /dev/null @@ -1,257 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2014 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. - -"""Partial Python implementation of iproute functionality.""" - -# pylint: disable=g-bad-todo - -import errno -import os -import socket -import struct -import sys - -import cstruct - - -# Request constants. -NLM_F_REQUEST = 1 -NLM_F_ACK = 4 -NLM_F_REPLACE = 0x100 -NLM_F_EXCL = 0x200 -NLM_F_CREATE = 0x400 -NLM_F_DUMP = 0x300 - -# Message types. -NLMSG_ERROR = 2 -NLMSG_DONE = 3 - -# Data structure formats. -# These aren't constants, they're classes. So, pylint: disable=invalid-name -NLMsgHdr = cstruct.Struct("NLMsgHdr", "=LHHLL", "length type flags seq pid") -NLMsgErr = cstruct.Struct("NLMsgErr", "=i", "error") -NLAttr = cstruct.Struct("NLAttr", "=HH", "nla_len nla_type") - -# Alignment / padding. -NLA_ALIGNTO = 4 - - -def PaddedLength(length): - # TODO: This padding is probably overly simplistic. - return NLA_ALIGNTO * ((length / NLA_ALIGNTO) + (length % NLA_ALIGNTO != 0)) - - -class NetlinkSocket(object): - """A basic netlink socket object.""" - - BUFSIZE = 65536 - DEBUG = False - # List of netlink messages to print, e.g., [], ["NEIGH", "ROUTE"], or ["ALL"] - NL_DEBUG = [] - - def _Debug(self, s): - if self.DEBUG: - print s - - def _NlAttr(self, nla_type, data): - datalen = len(data) - # Pad the data if it's not a multiple of NLA_ALIGNTO bytes long. - padding = "\x00" * (PaddedLength(datalen) - datalen) - nla_len = datalen + len(NLAttr) - return NLAttr((nla_len, nla_type)).Pack() + data + padding - - def _NlAttrU32(self, nla_type, value): - return self._NlAttr(nla_type, struct.pack("=I", value)) - - def _GetConstantName(self, module, value, prefix): - thismodule = sys.modules[module] - for name in dir(thismodule): - if name.startswith("INET_DIAG_BC"): - continue - if (name.startswith(prefix) and - not name.startswith(prefix + "F_") and - name.isupper() and getattr(thismodule, name) == value): - return name - return value - - def _Decode(self, command, msg, nla_type, nla_data): - """No-op, nonspecific version of decode.""" - return nla_type, nla_data - - def _ParseAttributes(self, command, family, msg, data): - """Parses and decodes netlink attributes. - - Takes a block of NLAttr data structures, decodes them using Decode, and - returns the result in a dict keyed by attribute number. - - Args: - command: An integer, the rtnetlink command being carried out. - family: The address family. - msg: A Struct, the type of the data after the netlink header. - data: A byte string containing a sequence of NLAttr data structures. - - Returns: - A dictionary mapping attribute types (integers) to decoded values. - - Raises: - ValueError: There was a duplicate attribute type. - """ - attributes = {} - while data: - # Read the nlattr header. - nla, data = cstruct.Read(data, NLAttr) - - # Read the data. - datalen = nla.nla_len - len(nla) - padded_len = PaddedLength(nla.nla_len) - len(nla) - nla_data, data = data[:datalen], data[padded_len:] - - # If it's an attribute we know about, try to decode it. - nla_name, nla_data = self._Decode(command, msg, nla.nla_type, nla_data) - - # We only support unique attributes for now, except for INET_DIAG_NONE, - # which can appear more than once but doesn't seem to contain any data. - if nla_name in attributes and nla_name != "INET_DIAG_NONE": - raise ValueError("Duplicate attribute %s" % nla_name) - - attributes[nla_name] = nla_data - self._Debug(" %s" % str((nla_name, nla_data))) - - return attributes - - def __init__(self): - # Global sequence number. - self.seq = 0 - self.sock = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, self.FAMILY) - self.sock.connect((0, 0)) # The kernel. - self.pid = self.sock.getsockname()[1] - - def _Send(self, msg): - # self._Debug(msg.encode("hex")) - self.seq += 1 - self.sock.send(msg) - - def _Recv(self): - data = self.sock.recv(self.BUFSIZE) - # self._Debug(data.encode("hex")) - return data - - def _ExpectDone(self): - response = self._Recv() - hdr = NLMsgHdr(response) - if hdr.type != NLMSG_DONE: - raise ValueError("Expected DONE, got type %d" % hdr.type) - - def _ParseAck(self, response): - # Find the error code. - hdr, data = cstruct.Read(response, NLMsgHdr) - if hdr.type == NLMSG_ERROR: - error = NLMsgErr(data).error - if error: - raise IOError(error, os.strerror(-error)) - else: - raise ValueError("Expected ACK, got type %d" % hdr.type) - - def _ExpectAck(self): - response = self._Recv() - self._ParseAck(response) - - def _SendNlRequest(self, command, data, flags): - """Sends a netlink request and expects an ack.""" - length = len(NLMsgHdr) + len(data) - nlmsg = NLMsgHdr((length, command, flags, self.seq, self.pid)).Pack() - - self.MaybeDebugCommand(command, nlmsg + data) - - # Send the message. - self._Send(nlmsg + data) - - if flags & NLM_F_ACK: - self._ExpectAck() - - def _ParseNLMsg(self, data, msgtype): - """Parses a Netlink message into a header and a dictionary of attributes.""" - nlmsghdr, data = cstruct.Read(data, NLMsgHdr) - self._Debug(" %s" % nlmsghdr) - - if nlmsghdr.type == NLMSG_ERROR or nlmsghdr.type == NLMSG_DONE: - print "done" - return (None, None), data - - nlmsg, data = cstruct.Read(data, msgtype) - self._Debug(" %s" % nlmsg) - - # Parse the attributes in the nlmsg. - attrlen = nlmsghdr.length - len(nlmsghdr) - len(nlmsg) - attributes = self._ParseAttributes(nlmsghdr.type, nlmsg.family, - nlmsg, data[:attrlen]) - data = data[attrlen:] - return (nlmsg, attributes), data - - def _GetMsg(self, msgtype): - data = self._Recv() - if NLMsgHdr(data).type == NLMSG_ERROR: - self._ParseAck(data) - return self._ParseNLMsg(data, msgtype)[0] - - def _GetMsgList(self, msgtype, data, expect_done): - out = [] - while data: - msg, data = self._ParseNLMsg(data, msgtype) - if msg is None: - break - out.append(msg) - if expect_done: - self._ExpectDone() - return out - - def _Dump(self, command, msg, msgtype, attrs): - """Sends a dump request and returns a list of decoded messages. - - Args: - command: An integer, the command to run (e.g., RTM_NEWADDR). - msg: A string, the raw bytes of the request (e.g., a packed RTMsg). - msgtype: A cstruct.Struct, the data type to parse the dump results as. - attrs: A string, the raw bytes of any request attributes to include. - - Returns: - A list of (msg, attrs) tuples where msg is of type msgtype and attrs is - a dict of attributes. - """ - # Create a netlink dump request containing the msg. - flags = NLM_F_DUMP | NLM_F_REQUEST - length = len(NLMsgHdr) + len(msg) + len(attrs) - nlmsghdr = NLMsgHdr((length, command, flags, self.seq, self.pid)) - - # Send the request. - request = nlmsghdr.Pack() + msg.Pack() + attrs - self.MaybeDebugCommand(command, request) - self._Send(request) - - # Keep reading netlink messages until we get a NLMSG_DONE. - out = [] - while True: - data = self._Recv() - response_type = NLMsgHdr(data).type - if response_type == NLMSG_DONE: - break - elif response_type == NLMSG_ERROR: - # Likely means that the kernel didn't like our dump request. - # Parse the error and throw an exception. - self._ParseAck(data) - out.extend(self._GetMsgList(msgtype, data, False)) - - return out diff --git a/tests/net_test/packets.py b/tests/net_test/packets.py deleted file mode 100644 index c02adc0a..00000000 --- a/tests/net_test/packets.py +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2015 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 random - -from scapy import all as scapy -from socket import * - -import net_test - -TCP_FIN = 1 -TCP_SYN = 2 -TCP_RST = 4 -TCP_PSH = 8 -TCP_ACK = 16 - -TCP_SEQ = 1692871236 -TCP_WINDOW = 14400 - -PING_IDENT = 0xff19 -PING_PAYLOAD = "foobarbaz" -PING_SEQ = 3 -PING_TOS = 0x83 - -# For brevity. -UDP_PAYLOAD = net_test.UDP_PAYLOAD - - -def RandomPort(): - return random.randint(1025, 65535) - -def _GetIpLayer(version): - return {4: scapy.IP, 6: scapy.IPv6}[version] - -def _SetPacketTos(packet, tos): - if isinstance(packet, scapy.IPv6): - packet.tc = tos - elif isinstance(packet, scapy.IP): - packet.tos = tos - else: - raise ValueError("Can't find ToS Field") - -def UDP(version, srcaddr, dstaddr, sport=0): - ip = _GetIpLayer(version) - # Can't just use "if sport" because None has meaning (it means unspecified). - if sport == 0: - sport = RandomPort() - return ("UDPv%d packet" % version, - ip(src=srcaddr, dst=dstaddr) / - scapy.UDP(sport=sport, dport=53) / UDP_PAYLOAD) - -def UDPWithOptions(version, srcaddr, dstaddr, sport=0): - if version == 4: - packet = (scapy.IP(src=srcaddr, dst=dstaddr, ttl=39, tos=0x83) / - scapy.UDP(sport=sport, dport=53) / - UDP_PAYLOAD) - else: - packet = (scapy.IPv6(src=srcaddr, dst=dstaddr, - fl=0xbeef, hlim=39, tc=0x83) / - scapy.UDP(sport=sport, dport=53) / - UDP_PAYLOAD) - return ("UDPv%d packet with options" % version, packet) - -def SYN(dport, version, srcaddr, dstaddr, sport=0, seq=TCP_SEQ): - ip = _GetIpLayer(version) - if sport == 0: - sport = RandomPort() - return ("TCP SYN", - ip(src=srcaddr, dst=dstaddr) / - scapy.TCP(sport=sport, dport=dport, - seq=seq, ack=0, - flags=TCP_SYN, window=TCP_WINDOW)) - -def RST(version, srcaddr, dstaddr, packet): - ip = _GetIpLayer(version) - original = packet.getlayer("TCP") - was_syn_or_fin = (original.flags & (TCP_SYN | TCP_FIN)) != 0 - return ("TCP RST", - ip(src=srcaddr, dst=dstaddr) / - scapy.TCP(sport=original.dport, dport=original.sport, - ack=original.seq + was_syn_or_fin, seq=None, - flags=TCP_RST | TCP_ACK, window=TCP_WINDOW)) - -def SYNACK(version, srcaddr, dstaddr, packet): - ip = _GetIpLayer(version) - original = packet.getlayer("TCP") - return ("TCP SYN+ACK", - ip(src=srcaddr, dst=dstaddr) / - scapy.TCP(sport=original.dport, dport=original.sport, - ack=original.seq + 1, seq=None, - flags=TCP_SYN | TCP_ACK, window=None)) - -def ACK(version, srcaddr, dstaddr, packet, payload=""): - ip = _GetIpLayer(version) - original = packet.getlayer("TCP") - was_syn_or_fin = (original.flags & (TCP_SYN | TCP_FIN)) != 0 - ack_delta = was_syn_or_fin + len(original.payload) - desc = "TCP data" if payload else "TCP ACK" - flags = TCP_ACK | TCP_PSH if payload else TCP_ACK - return (desc, - ip(src=srcaddr, dst=dstaddr) / - scapy.TCP(sport=original.dport, dport=original.sport, - ack=original.seq + ack_delta, seq=original.ack, - flags=flags, window=TCP_WINDOW) / - payload) - -def FIN(version, srcaddr, dstaddr, packet): - ip = _GetIpLayer(version) - original = packet.getlayer("TCP") - was_syn_or_fin = (original.flags & (TCP_SYN | TCP_FIN)) != 0 - ack_delta = was_syn_or_fin + len(original.payload) - return ("TCP FIN", - ip(src=srcaddr, dst=dstaddr) / - scapy.TCP(sport=original.dport, dport=original.sport, - ack=original.seq + ack_delta, seq=original.ack, - flags=TCP_ACK | TCP_FIN, window=TCP_WINDOW)) - -def GRE(version, srcaddr, dstaddr, proto, packet): - if version == 4: - ip = scapy.IP(src=srcaddr, dst=dstaddr, proto=net_test.IPPROTO_GRE) - else: - ip = scapy.IPv6(src=srcaddr, dst=dstaddr, nh=net_test.IPPROTO_GRE) - packet = ip / scapy.GRE(proto=proto) / packet - return ("GRE packet", packet) - -def ICMPPortUnreachable(version, srcaddr, dstaddr, packet): - if version == 4: - # Linux hardcodes the ToS on ICMP errors to 0xc0 or greater because of - # RFC 1812 4.3.2.5 (!). - return ("ICMPv4 port unreachable", - scapy.IP(src=srcaddr, dst=dstaddr, proto=1, tos=0xc0) / - scapy.ICMPerror(type=3, code=3) / packet) - else: - return ("ICMPv6 port unreachable", - scapy.IPv6(src=srcaddr, dst=dstaddr) / - scapy.ICMPv6DestUnreach(code=4) / packet) - -def ICMPPacketTooBig(version, srcaddr, dstaddr, packet): - if version == 4: - return ("ICMPv4 fragmentation needed", - scapy.IP(src=srcaddr, dst=dstaddr, proto=1) / - scapy.ICMPerror(type=3, code=4, unused=1280) / str(packet)[:64]) - else: - udp = packet.getlayer("UDP") - udp.payload = str(udp.payload)[:1280-40-8] - return ("ICMPv6 Packet Too Big", - scapy.IPv6(src=srcaddr, dst=dstaddr) / - scapy.ICMPv6PacketTooBig() / str(packet)[:1232]) - -def ICMPEcho(version, srcaddr, dstaddr): - ip = _GetIpLayer(version) - icmp = {4: scapy.ICMP, 6: scapy.ICMPv6EchoRequest}[version] - packet = (ip(src=srcaddr, dst=dstaddr) / - icmp(id=PING_IDENT, seq=PING_SEQ) / PING_PAYLOAD) - _SetPacketTos(packet, PING_TOS) - return ("ICMPv%d echo" % version, packet) - -def ICMPReply(version, srcaddr, dstaddr, packet): - ip = _GetIpLayer(version) - # Scapy doesn't provide an ICMP echo reply constructor. - icmpv4_reply = lambda **kwargs: scapy.ICMP(type=0, **kwargs) - icmp = {4: icmpv4_reply, 6: scapy.ICMPv6EchoReply}[version] - packet = (ip(src=srcaddr, dst=dstaddr) / - icmp(id=PING_IDENT, seq=PING_SEQ) / PING_PAYLOAD) - # IPv6 only started copying the tclass to echo replies in 3.14. - if version == 4 or net_test.LINUX_VERSION >= (3, 14): - _SetPacketTos(packet, PING_TOS) - return ("ICMPv%d echo reply" % version, packet) - -def NS(srcaddr, tgtaddr, srcmac): - solicited = inet_pton(AF_INET6, tgtaddr) - last3bytes = tuple([ord(b) for b in solicited[-3:]]) - solicited = "ff02::1:ff%02x:%02x%02x" % last3bytes - packet = (scapy.IPv6(src=srcaddr, dst=solicited) / - scapy.ICMPv6ND_NS(tgt=tgtaddr) / - scapy.ICMPv6NDOptSrcLLAddr(lladdr=srcmac)) - return ("ICMPv6 NS", packet) - -def NA(srcaddr, dstaddr, srcmac): - packet = (scapy.IPv6(src=srcaddr, dst=dstaddr) / - scapy.ICMPv6ND_NA(tgt=srcaddr, R=0, S=1, O=1) / - scapy.ICMPv6NDOptDstLLAddr(lladdr=srcmac)) - return ("ICMPv6 NA", packet) - diff --git a/tests/net_test/ping6_test.py b/tests/net_test/ping6_test.py deleted file mode 100755 index bf51cfa1..00000000 --- a/tests/net_test/ping6_test.py +++ /dev/null @@ -1,709 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2014 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. - -# pylint: disable=g-bad-todo - -import errno -import os -import posix -import random -from socket import * # pylint: disable=wildcard-import -import threading -import time -import unittest - -from scapy import all as scapy - -import csocket -import multinetwork_base -import net_test - - -HAVE_PROC_NET_ICMP6 = os.path.isfile("/proc/net/icmp6") - -ICMP_ECHO = 8 -ICMP_ECHOREPLY = 0 -ICMPV6_ECHO_REQUEST = 128 -ICMPV6_ECHO_REPLY = 129 - - -class PingReplyThread(threading.Thread): - - MIN_TTL = 10 - INTERMEDIATE_IPV4 = "192.0.2.2" - INTERMEDIATE_IPV6 = "2001:db8:1:2::ace:d00d" - NEIGHBOURS = ["fe80::1"] - - def __init__(self, tun, mymac, routermac): - super(PingReplyThread, self).__init__() - self._tun = tun - self._stopped = False - self._mymac = mymac - self._routermac = routermac - - def Stop(self): - self._stopped = True - - def ChecksumValid(self, packet): - # Get and clear the checksums. - def GetAndClearChecksum(layer): - if not layer: - return - try: - checksum = layer.chksum - del layer.chksum - except AttributeError: - checksum = layer.cksum - del layer.cksum - return checksum - - def GetChecksum(layer): - try: - return layer.chksum - except AttributeError: - return layer.cksum - - layers = ["IP", "ICMP", scapy.ICMPv6EchoRequest] - sums = {} - for name in layers: - sums[name] = GetAndClearChecksum(packet.getlayer(name)) - - # Serialize the packet, so scapy recalculates the checksums, and compare - # them with the ones in the packet. - packet = packet.__class__(str(packet)) - for name in layers: - layer = packet.getlayer(name) - if layer and GetChecksum(layer) != sums[name]: - return False - - return True - - def SendTimeExceeded(self, version, packet): - if version == 4: - src = packet.getlayer(scapy.IP).src - self.SendPacket( - scapy.IP(src=self.INTERMEDIATE_IPV4, dst=src) / - scapy.ICMP(type=11, code=0) / - packet) - elif version == 6: - src = packet.getlayer(scapy.IPv6).src - self.SendPacket( - scapy.IPv6(src=self.INTERMEDIATE_IPV6, dst=src) / - scapy.ICMPv6TimeExceeded(code=0) / - packet) - - def IPv4Packet(self, ip): - icmp = ip.getlayer(scapy.ICMP) - - # We only support ping for now. - if (ip.proto != IPPROTO_ICMP or - icmp.type != ICMP_ECHO or - icmp.code != 0): - return - - # Check the checksums. - if not self.ChecksumValid(ip): - return - - if ip.ttl < self.MIN_TTL: - self.SendTimeExceeded(4, ip) - return - - icmp.type = ICMP_ECHOREPLY - self.SwapAddresses(ip) - self.SendPacket(ip) - - def IPv6Packet(self, ipv6): - icmpv6 = ipv6.getlayer(scapy.ICMPv6EchoRequest) - - # We only support ping for now. - if (ipv6.nh != IPPROTO_ICMPV6 or - not icmpv6 or - icmpv6.type != ICMPV6_ECHO_REQUEST or - icmpv6.code != 0): - return - - # Check the checksums. - if not self.ChecksumValid(ipv6): - return - - if ipv6.dst.startswith("ff02::"): - ipv6.dst = ipv6.src - for src in self.NEIGHBOURS: - ipv6.src = src - icmpv6.type = ICMPV6_ECHO_REPLY - self.SendPacket(ipv6) - elif ipv6.hlim < self.MIN_TTL: - self.SendTimeExceeded(6, ipv6) - else: - icmpv6.type = ICMPV6_ECHO_REPLY - self.SwapAddresses(ipv6) - self.SendPacket(ipv6) - - def SwapAddresses(self, packet): - src = packet.src - packet.src = packet.dst - packet.dst = src - - def SendPacket(self, packet): - packet = scapy.Ether(src=self._routermac, dst=self._mymac) / packet - try: - posix.write(self._tun.fileno(), str(packet)) - except ValueError: - pass - - def run(self): - while not self._stopped: - - try: - packet = posix.read(self._tun.fileno(), 4096) - except OSError, e: - if e.errno == errno.EAGAIN: - continue - else: - break - - ether = scapy.Ether(packet) - if ether.type == net_test.ETH_P_IPV6: - self.IPv6Packet(ether.payload) - elif ether.type == net_test.ETH_P_IP: - self.IPv4Packet(ether.payload) - - -class Ping6Test(multinetwork_base.MultiNetworkBaseTest): - - @classmethod - def setUpClass(cls): - super(Ping6Test, cls).setUpClass() - cls.netid = random.choice(cls.NETIDS) - cls.reply_thread = PingReplyThread( - cls.tuns[cls.netid], - cls.MyMacAddress(cls.netid), - cls.RouterMacAddress(cls.netid)) - cls.SetDefaultNetwork(cls.netid) - cls.reply_thread.start() - - @classmethod - def tearDownClass(cls): - cls.reply_thread.Stop() - cls.ClearDefaultNetwork() - super(Ping6Test, cls).tearDownClass() - - def setUp(self): - self.ifname = self.GetInterfaceName(self.netid) - self.ifindex = self.ifindices[self.netid] - self.lladdr = net_test.GetLinkAddress(self.ifname, True) - self.globaladdr = net_test.GetLinkAddress(self.ifname, False) - - def assertValidPingResponse(self, s, data): - family = s.family - - # Receive the reply. - rcvd, src = s.recvfrom(32768) - self.assertNotEqual(0, len(rcvd), "No data received") - - # If this is a dual-stack socket sending to a mapped IPv4 address, treat it - # as IPv4. - if src[0].startswith("::ffff:"): - family = AF_INET - src = (src[0].replace("::ffff:", ""), src[1:]) - - # Check the data being sent is valid. - self.assertGreater(len(data), 7, "Not enough data for ping packet") - if family == AF_INET: - self.assertTrue(data.startswith("\x08\x00"), "Not an IPv4 echo request") - elif family == AF_INET6: - self.assertTrue(data.startswith("\x80\x00"), "Not an IPv6 echo request") - else: - self.fail("Unknown socket address family %d" * s.family) - - # Check address, ICMP type, and ICMP code. - if family == AF_INET: - addr, unused_port = src - self.assertGreaterEqual(len(addr), len("1.1.1.1")) - self.assertTrue(rcvd.startswith("\x00\x00"), "Not an IPv4 echo reply") - else: - addr, unused_port, flowlabel, scope_id = src # pylint: disable=unbalanced-tuple-unpacking - self.assertGreaterEqual(len(addr), len("::")) - self.assertTrue(rcvd.startswith("\x81\x00"), "Not an IPv6 echo reply") - # 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 self.ifindices.values()) - else: - self.assertEquals(0, scope_id) - - # TODO: check the checksum. We can't do this easily now for ICMPv6 because - # we don't have the IP addresses so we can't construct the pseudoheader. - - # Check the sequence number and the data. - self.assertEqual(len(data), len(rcvd)) - self.assertEqual(data[6:].encode("hex"), rcvd[6:].encode("hex")) - - def CheckSockStatFile(self, name, srcaddr, srcport, dstaddr, dstport, state, - txmem=0, rxmem=0): - expected = ["%s:%04X" % (net_test.FormatSockStatAddress(srcaddr), srcport), - "%s:%04X" % (net_test.FormatSockStatAddress(dstaddr), dstport), - "%02X" % state, - "%08X:%08X" % (txmem, rxmem), - str(os.getuid()), "2", "0"] - actual = self.ReadProcNetSocket(name)[-1] - self.assertListEqual(expected, actual) - - def testIPv4SendWithNoConnection(self): - s = net_test.IPv4PingSocket() - self.assertRaisesErrno(errno.EDESTADDRREQ, s.send, net_test.IPV4_PING) - - def testIPv6SendWithNoConnection(self): - s = net_test.IPv6PingSocket() - self.assertRaisesErrno(errno.EDESTADDRREQ, s.send, net_test.IPV6_PING) - - def testIPv4LoopbackPingWithConnect(self): - s = net_test.IPv4PingSocket() - s.connect(("127.0.0.1", 55)) - data = net_test.IPV4_PING + "foobarbaz" - s.send(data) - self.assertValidPingResponse(s, data) - - def testIPv6LoopbackPingWithConnect(self): - s = net_test.IPv6PingSocket() - s.connect(("::1", 55)) - s.send(net_test.IPV6_PING) - self.assertValidPingResponse(s, net_test.IPV6_PING) - - def testIPv4PingUsingSendto(self): - s = net_test.IPv4PingSocket() - written = s.sendto(net_test.IPV4_PING, (net_test.IPV4_ADDR, 55)) - self.assertEquals(len(net_test.IPV4_PING), written) - self.assertValidPingResponse(s, net_test.IPV4_PING) - - def testIPv6PingUsingSendto(self): - s = net_test.IPv6PingSocket() - written = s.sendto(net_test.IPV6_PING, (net_test.IPV6_ADDR, 55)) - self.assertEquals(len(net_test.IPV6_PING), written) - self.assertValidPingResponse(s, net_test.IPV6_PING) - - def testIPv4NoCrash(self): - # Python 2.x does not provide either read() or recvmsg. - s = net_test.IPv4PingSocket() - written = s.sendto(net_test.IPV4_PING, ("127.0.0.1", 55)) - self.assertEquals(len(net_test.IPV4_PING), written) - fd = s.fileno() - reply = posix.read(fd, 4096) - self.assertEquals(written, len(reply)) - - def testIPv6NoCrash(self): - # Python 2.x does not provide either read() or recvmsg. - s = net_test.IPv6PingSocket() - written = s.sendto(net_test.IPV6_PING, ("::1", 55)) - self.assertEquals(len(net_test.IPV6_PING), written) - fd = s.fileno() - reply = posix.read(fd, 4096) - self.assertEquals(written, len(reply)) - - def testCrossProtocolCrash(self): - # Checks that an ICMP error containing a ping packet that matches the ID - # of a socket of the wrong protocol (which can happen when using 464xlat) - # doesn't crash the kernel. - - # We can only test this using IPv6 unreachables and IPv4 ping sockets, - # because IPv4 packets sent by scapy.send() on loopback are not received by - # the kernel. So we don't actually use this function yet. - def GetIPv4Unreachable(port): # pylint: disable=unused-variable - return (scapy.IP(src="192.0.2.1", dst="127.0.0.1") / - scapy.ICMP(type=3, code=0) / - scapy.IP(src="127.0.0.1", dst="127.0.0.1") / - scapy.ICMP(type=8, id=port, seq=1)) - - def GetIPv6Unreachable(port): - return (scapy.IPv6(src="::1", dst="::1") / - scapy.ICMPv6DestUnreach() / - scapy.IPv6(src="::1", dst="::1") / - scapy.ICMPv6EchoRequest(id=port, seq=1, data="foobarbaz")) - - # An unreachable matching the ID of a socket of the wrong protocol - # shouldn't crash. - s = net_test.IPv4PingSocket() - s.connect(("127.0.0.1", 12345)) - _, port = s.getsockname() - scapy.send(GetIPv6Unreachable(port)) - # No crash? Good. - - def testCrossProtocolCalls(self): - """Tests that passing in the wrong family returns EAFNOSUPPORT. - - Relevant kernel commits: - upstream net: - 91a0b60 net/ping: handle protocol mismatching scenario - 9145736d net: ping: Return EAFNOSUPPORT when appropriate. - - android-3.10: - 78a6809 net/ping: handle protocol mismatching scenario - 428e6d6 net: ping: Return EAFNOSUPPORT when appropriate. - """ - - def CheckEAFNoSupport(function, *args): - self.assertRaisesErrno(errno.EAFNOSUPPORT, function, *args) - - ipv6sockaddr = csocket.Sockaddr((net_test.IPV6_ADDR, 53)) - - # In order to check that IPv6 socket calls return EAFNOSUPPORT when passed - # IPv4 socket address structures, we need to pass down a socket address - # length argument that's at least sizeof(sockaddr_in6). Otherwise, the calls - # will fail immediately with EINVAL because the passed-in socket length is - # too short. So create a sockaddr_in that's as long as a sockaddr_in6. - ipv4sockaddr = csocket.Sockaddr((net_test.IPV4_ADDR, 53)) - ipv4sockaddr = csocket.SockaddrIn6( - ipv4sockaddr.Pack() + - "\x00" * (len(csocket.SockaddrIn6) - len(csocket.SockaddrIn))) - - s4 = net_test.IPv4PingSocket() - s6 = net_test.IPv6PingSocket() - - # We can't just call s.connect(), s.bind() etc. with a tuple of the wrong - # address family, because the Python implementation will just pass garbage - # down to the kernel. So call the C functions directly. - CheckEAFNoSupport(csocket.Bind, s4, ipv6sockaddr) - CheckEAFNoSupport(csocket.Bind, s6, ipv4sockaddr) - CheckEAFNoSupport(csocket.Connect, s4, ipv6sockaddr) - CheckEAFNoSupport(csocket.Connect, s6, ipv4sockaddr) - CheckEAFNoSupport(csocket.Sendmsg, - s4, ipv6sockaddr, net_test.IPV4_PING, None, 0) - CheckEAFNoSupport(csocket.Sendmsg, - s6, ipv4sockaddr, net_test.IPV6_PING, None, 0) - - def testIPv4Bind(self): - # Bind to unspecified address. - s = net_test.IPv4PingSocket() - s.bind(("0.0.0.0", 544)) - self.assertEquals(("0.0.0.0", 544), s.getsockname()) - - # Bind to loopback. - s = net_test.IPv4PingSocket() - s.bind(("127.0.0.1", 99)) - self.assertEquals(("127.0.0.1", 99), s.getsockname()) - - # Binding twice is not allowed. - self.assertRaisesErrno(errno.EINVAL, s.bind, ("127.0.0.1", 22)) - - # But binding two different sockets to the same ID is allowed. - s2 = net_test.IPv4PingSocket() - s2.bind(("127.0.0.1", 99)) - self.assertEquals(("127.0.0.1", 99), s2.getsockname()) - s3 = net_test.IPv4PingSocket() - s3.bind(("127.0.0.1", 99)) - self.assertEquals(("127.0.0.1", 99), s3.getsockname()) - - # If two sockets bind to the same port, the first one to call read() gets - # the response. - s4 = net_test.IPv4PingSocket() - s5 = net_test.IPv4PingSocket() - s4.bind(("0.0.0.0", 167)) - s5.bind(("0.0.0.0", 167)) - s4.sendto(net_test.IPV4_PING, (net_test.IPV4_ADDR, 44)) - self.assertValidPingResponse(s5, net_test.IPV4_PING) - net_test.SetSocketTimeout(s4, 100) - self.assertRaisesErrno(errno.EAGAIN, s4.recv, 32768) - - # If SO_REUSEADDR is turned off, then we get EADDRINUSE. - s6 = net_test.IPv4PingSocket() - s4.setsockopt(SOL_SOCKET, SO_REUSEADDR, 0) - self.assertRaisesErrno(errno.EADDRINUSE, s6.bind, ("0.0.0.0", 167)) - - # Can't bind after sendto. - s = net_test.IPv4PingSocket() - s.sendto(net_test.IPV4_PING, (net_test.IPV4_ADDR, 9132)) - self.assertRaisesErrno(errno.EINVAL, s.bind, ("0.0.0.0", 5429)) - - def testIPv6Bind(self): - # Bind to unspecified address. - s = net_test.IPv6PingSocket() - s.bind(("::", 769)) - self.assertEquals(("::", 769, 0, 0), s.getsockname()) - - # Bind to loopback. - s = net_test.IPv6PingSocket() - s.bind(("::1", 99)) - self.assertEquals(("::1", 99, 0, 0), s.getsockname()) - - # Binding twice is not allowed. - self.assertRaisesErrno(errno.EINVAL, s.bind, ("::1", 22)) - - # But binding two different sockets to the same ID is allowed. - s2 = net_test.IPv6PingSocket() - s2.bind(("::1", 99)) - self.assertEquals(("::1", 99, 0, 0), s2.getsockname()) - s3 = net_test.IPv6PingSocket() - s3.bind(("::1", 99)) - self.assertEquals(("::1", 99, 0, 0), s3.getsockname()) - - # Binding both IPv4 and IPv6 to the same socket works. - s4 = net_test.IPv4PingSocket() - s6 = net_test.IPv6PingSocket() - s4.bind(("0.0.0.0", 444)) - s6.bind(("::", 666, 0, 0)) - - # Can't bind after sendto. - s = net_test.IPv6PingSocket() - s.sendto(net_test.IPV6_PING, (net_test.IPV6_ADDR, 9132)) - self.assertRaisesErrno(errno.EINVAL, s.bind, ("::", 5429)) - - def testIPv4InvalidBind(self): - s = net_test.IPv4PingSocket() - self.assertRaisesErrno(errno.EADDRNOTAVAIL, - s.bind, ("255.255.255.255", 1026)) - self.assertRaisesErrno(errno.EADDRNOTAVAIL, - s.bind, ("224.0.0.1", 651)) - # Binding to an address we don't have only works with IP_TRANSPARENT. - self.assertRaisesErrno(errno.EADDRNOTAVAIL, - s.bind, (net_test.IPV4_ADDR, 651)) - try: - s.setsockopt(SOL_IP, net_test.IP_TRANSPARENT, 1) - s.bind((net_test.IPV4_ADDR, 651)) - except IOError, e: - if e.errno == errno.EACCES: - pass # We're not root. let it go for now. - - def testIPv6InvalidBind(self): - s = net_test.IPv6PingSocket() - self.assertRaisesErrno(errno.EINVAL, - s.bind, ("ff02::2", 1026)) - - # Binding to an address we don't have only works with IPV6_TRANSPARENT. - self.assertRaisesErrno(errno.EADDRNOTAVAIL, - s.bind, (net_test.IPV6_ADDR, 651)) - try: - s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_TRANSPARENT, 1) - s.bind((net_test.IPV6_ADDR, 651)) - except IOError, e: - if e.errno == errno.EACCES: - pass # We're not root. let it go for now. - - def testAfUnspecBind(self): - # Binding to AF_UNSPEC is treated as IPv4 if the address is 0.0.0.0. - s4 = net_test.IPv4PingSocket() - sockaddr = csocket.Sockaddr(("0.0.0.0", 12996)) - sockaddr.family = AF_UNSPEC - csocket.Bind(s4, sockaddr) - self.assertEquals(("0.0.0.0", 12996), s4.getsockname()) - - # But not if the address is anything else. - sockaddr = csocket.Sockaddr(("127.0.0.1", 58234)) - sockaddr.family = AF_UNSPEC - self.assertRaisesErrno(errno.EAFNOSUPPORT, csocket.Bind, s4, sockaddr) - - # This doesn't work for IPv6. - s6 = net_test.IPv6PingSocket() - sockaddr = csocket.Sockaddr(("::1", 58997)) - sockaddr.family = AF_UNSPEC - self.assertRaisesErrno(errno.EAFNOSUPPORT, csocket.Bind, s6, sockaddr) - - def testIPv6ScopedBind(self): - # Can't bind to a link-local address without a scope ID. - s = net_test.IPv6PingSocket() - self.assertRaisesErrno(errno.EINVAL, - s.bind, (self.lladdr, 1026, 0, 0)) - - # Binding to a link-local address with a scope ID works, and the scope ID is - # returned by a subsequent getsockname. Interestingly, Python's getsockname - # returns "fe80:1%foo", even though it does not understand it. - expected = self.lladdr + "%" + self.ifname - s.bind((self.lladdr, 4646, 0, self.ifindex)) - self.assertEquals((expected, 4646, 0, self.ifindex), s.getsockname()) - - # Of course, for the above to work the address actually has to be configured - # on the machine. - self.assertRaisesErrno(errno.EADDRNOTAVAIL, - s.bind, ("fe80::f00", 1026, 0, 1)) - - # Scope IDs on non-link-local addresses are silently ignored. - s = net_test.IPv6PingSocket() - s.bind(("::1", 1234, 0, 1)) - self.assertEquals(("::1", 1234, 0, 0), s.getsockname()) - - def testBindAffectsIdentifier(self): - s = net_test.IPv6PingSocket() - s.bind((self.globaladdr, 0xf976)) - s.sendto(net_test.IPV6_PING, (net_test.IPV6_ADDR, 55)) - self.assertEquals("\xf9\x76", s.recv(32768)[4:6]) - - s = net_test.IPv6PingSocket() - s.bind((self.globaladdr, 0xace)) - s.sendto(net_test.IPV6_PING, (net_test.IPV6_ADDR, 55)) - self.assertEquals("\x0a\xce", s.recv(32768)[4:6]) - - def testLinkLocalAddress(self): - s = net_test.IPv6PingSocket() - # Sending to a link-local address with no scope fails with EINVAL. - self.assertRaisesErrno(errno.EINVAL, - s.sendto, net_test.IPV6_PING, ("fe80::1", 55)) - # Sending to link-local address with a scope succeeds. Note that Python - # doesn't understand the "fe80::1%lo" format, even though it returns it. - s.sendto(net_test.IPV6_PING, ("fe80::1", 55, 0, self.ifindex)) - # No exceptions? Good. - - def testMappedAddressFails(self): - s = net_test.IPv6PingSocket() - s.sendto(net_test.IPV6_PING, (net_test.IPV6_ADDR, 55)) - self.assertValidPingResponse(s, net_test.IPV6_PING) - s.sendto(net_test.IPV6_PING, ("2001:4860:4860::8844", 55)) - self.assertValidPingResponse(s, net_test.IPV6_PING) - self.assertRaisesErrno(errno.EINVAL, s.sendto, net_test.IPV6_PING, - ("::ffff:192.0.2.1", 55)) - - @unittest.skipUnless(False, "skipping: does not work yet") - def testFlowLabel(self): - s = net_test.IPv6PingSocket() - - # Specifying a flowlabel without having set IPV6_FLOWINFO_SEND succeeds but - # the flow label in the packet is not set. - s.sendto(net_test.IPV6_PING, (net_test.IPV6_ADDR, 93, 0xdead, 0)) - self.assertValidPingResponse(s, net_test.IPV6_PING) # Checks flow label==0. - - # If IPV6_FLOWINFO_SEND is set on the socket, attempting to set a flow label - # 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: - self.assertRaisesErrno(errno.EINVAL, s.sendto, net_test.IPV6_PING, - (net_test.IPV6_ADDR, 93, 0xdead, 0)) - - # After registering the flow label, it gets sent properly, appears in the - # output packet, and is returned in the response. - net_test.SetFlowLabel(s, net_test.IPV6_ADDR, 0xdead) - self.assertEqual(1, s.getsockopt(net_test.SOL_IPV6, - net_test.IPV6_FLOWINFO_SEND)) - s.sendto(net_test.IPV6_PING, (net_test.IPV6_ADDR, 93, 0xdead, 0)) - _, src = s.recvfrom(32768) - _, _, flowlabel, _ = src - self.assertEqual(0xdead, flowlabel & 0xfffff) - - def testIPv4Error(self): - s = net_test.IPv4PingSocket() - s.setsockopt(SOL_IP, IP_TTL, 2) - s.setsockopt(SOL_IP, net_test.IP_RECVERR, 1) - s.sendto(net_test.IPV4_PING, (net_test.IPV4_ADDR, 55)) - # We can't check the actual error because Python 2.7 doesn't implement - # recvmsg, but we can at least check that the socket returns an error. - self.assertRaisesErrno(errno.EHOSTUNREACH, s.recv, 32768) # No response. - - def testIPv6Error(self): - s = net_test.IPv6PingSocket() - s.setsockopt(net_test.SOL_IPV6, IPV6_UNICAST_HOPS, 2) - s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_RECVERR, 1) - s.sendto(net_test.IPV6_PING, (net_test.IPV6_ADDR, 55)) - # We can't check the actual error because Python 2.7 doesn't implement - # recvmsg, but we can at least check that the socket returns an error. - self.assertRaisesErrno(errno.EHOSTUNREACH, s.recv, 32768) # No response. - - def testIPv6MulticastPing(self): - s = net_test.IPv6PingSocket() - # Send a multicast ping and check we get at least one duplicate. - # The setsockopt should not be necessary, but ping_v6_sendmsg has a bug. - s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_MULTICAST_IF, self.ifindex) - s.sendto(net_test.IPV6_PING, ("ff02::1", 55, 0, self.ifindex)) - self.assertValidPingResponse(s, net_test.IPV6_PING) - self.assertValidPingResponse(s, net_test.IPV6_PING) - - def testIPv4LargePacket(self): - s = net_test.IPv4PingSocket() - data = net_test.IPV4_PING + 20000 * "a" - s.sendto(data, ("127.0.0.1", 987)) - self.assertValidPingResponse(s, data) - - def testIPv6LargePacket(self): - s = net_test.IPv6PingSocket() - s.bind(("::", 0xace)) - data = net_test.IPV6_PING + "\x01" + 19994 * "\x00" + "aaaaa" - s.sendto(data, ("::1", 953)) - - @unittest.skipUnless(HAVE_PROC_NET_ICMP6, "skipping: no /proc/net/icmp6") - def testIcmpSocketsNotInIcmp6(self): - numrows = len(self.ReadProcNetSocket("icmp")) - numrows6 = len(self.ReadProcNetSocket("icmp6")) - s = net_test.Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP) - s.bind(("127.0.0.1", 0xace)) - s.connect(("127.0.0.1", 0xbeef)) - self.assertEquals(numrows + 1, len(self.ReadProcNetSocket("icmp"))) - self.assertEquals(numrows6, len(self.ReadProcNetSocket("icmp6"))) - - @unittest.skipUnless(HAVE_PROC_NET_ICMP6, "skipping: no /proc/net/icmp6") - def testIcmp6SocketsNotInIcmp(self): - numrows = len(self.ReadProcNetSocket("icmp")) - numrows6 = len(self.ReadProcNetSocket("icmp6")) - s = net_test.IPv6PingSocket() - s.bind(("::1", 0xace)) - s.connect(("::1", 0xbeef)) - self.assertEquals(numrows, len(self.ReadProcNetSocket("icmp"))) - self.assertEquals(numrows6 + 1, len(self.ReadProcNetSocket("icmp6"))) - - def testProcNetIcmp(self): - s = net_test.Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP) - s.bind(("127.0.0.1", 0xace)) - s.connect(("127.0.0.1", 0xbeef)) - self.CheckSockStatFile("icmp", "127.0.0.1", 0xace, "127.0.0.1", 0xbeef, 1) - - @unittest.skipUnless(HAVE_PROC_NET_ICMP6, "skipping: no /proc/net/icmp6") - def testProcNetIcmp6(self): - numrows6 = len(self.ReadProcNetSocket("icmp6")) - s = net_test.IPv6PingSocket() - s.bind(("::1", 0xace)) - s.connect(("::1", 0xbeef)) - self.CheckSockStatFile("icmp6", "::1", 0xace, "::1", 0xbeef, 1) - - # Check the row goes away when the socket is closed. - s.close() - self.assertEquals(numrows6, len(self.ReadProcNetSocket("icmp6"))) - - # Try send, bind and connect to check the addresses and the state. - s = net_test.IPv6PingSocket() - self.assertEqual(0, len(self.ReadProcNetSocket("icmp6"))) - s.sendto(net_test.IPV6_PING, (net_test.IPV6_ADDR, 12345)) - self.assertEqual(1, len(self.ReadProcNetSocket("icmp6"))) - - # Can't bind after sendto, apparently. - s = net_test.IPv6PingSocket() - self.assertEqual(0, len(self.ReadProcNetSocket("icmp6"))) - s.bind((self.lladdr, 0xd00d, 0, self.ifindex)) - self.CheckSockStatFile("icmp6", self.lladdr, 0xd00d, "::", 0, 7) - - # Check receive bytes. - s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_MULTICAST_IF, self.ifindex) - s.connect(("ff02::1", 0xdead)) - self.CheckSockStatFile("icmp6", self.lladdr, 0xd00d, "ff02::1", 0xdead, 1) - s.send(net_test.IPV6_PING) - time.sleep(0.01) # Give the other thread time to reply. - self.CheckSockStatFile("icmp6", self.lladdr, 0xd00d, "ff02::1", 0xdead, 1, - txmem=0, rxmem=0x300) - self.assertValidPingResponse(s, net_test.IPV6_PING) - self.CheckSockStatFile("icmp6", self.lladdr, 0xd00d, "ff02::1", 0xdead, 1, - txmem=0, rxmem=0) - - def testProcNetUdp6(self): - s = net_test.Socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP) - s.bind(("::1", 0xace)) - s.connect(("::1", 0xbeef)) - self.CheckSockStatFile("udp6", "::1", 0xace, "::1", 0xbeef, 1) - - def testProcNetRaw6(self): - s = net_test.Socket(AF_INET6, SOCK_RAW, IPPROTO_RAW) - s.bind(("::1", 0xace)) - s.connect(("::1", 0xbeef)) - self.CheckSockStatFile("raw6", "::1", 0xff, "::1", 0, 1) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/net_test/ping6_test.sh b/tests/net_test/ping6_test.sh deleted file mode 100755 index 41dabcea..00000000 --- a/tests/net_test/ping6_test.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -# Minimal network initialization. -ip link set eth0 up - -# Wait for autoconf and DAD to complete. -sleep 3 & - -# Block on starting DHCPv4. -udhcpc -i eth0 - -# If DHCPv4 took less than 3 seconds, keep waiting. -wait - -# Run the test. -$(dirname $0)/ping6_test.py diff --git a/tests/net_test/run_net_test.sh b/tests/net_test/run_net_test.sh deleted file mode 100755 index 080aac73..00000000 --- a/tests/net_test/run_net_test.sh +++ /dev/null @@ -1,125 +0,0 @@ -#!/bin/bash - -# Kernel configuration options. -OPTIONS=" DEBUG_SPINLOCK DEBUG_ATOMIC_SLEEP DEBUG_MUTEXES DEBUG_RT_MUTEXES" -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" -OPTIONS="$OPTIONS IP6_NF_IPTABLES IP6_NF_MANGLE INET6_IPCOMP" -OPTIONS="$OPTIONS IPV6_PRIVACY IPV6_OPTIMISTIC_DAD" -OPTIONS="$OPTIONS CONFIG_NETFILTER_XT_TARGET_NFLOG" -OPTIONS="$OPTIONS CONFIG_NETFILTER_XT_MATCH_QUOTA CONFIG_NETFILTER_XT_MATCH_QUOTA2" -OPTIONS="$OPTIONS CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG" -OPTIONS="$OPTIONS CONFIG_INET_UDP_DIAG CONFIG_INET_DIAG_DESTROY" - -# 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" - -# 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. -# -# This is not needed to run the tests, but can be used, for example, to allow -# the VM to update system packages, or to write tests that need access to a -# real network. The VM does not set up networking by default, but it contains a -# DHCP client and has the ability to use IPv6 autoconfiguration. This script -# does not perform any host-level setup beyond configuring tap interfaces; -# configuring IPv4 NAT and/or IPv6 router advertisements or ND proxying must -# be done separately. -NUMTAPINTERFACES=0 - -# The root filesystem disk image we'll use. -ROOTFS=net_test.rootfs.20150203 -COMPRESSED_ROOTFS=$ROOTFS.xz -URL=https://dl.google.com/dl/android/$COMPRESSED_ROOTFS - -# Figure out which test to run. -if [ -z "$1" ]; then - echo "Usage: $0 " >&2 - exit 1 -fi -test=$1 - -set -e - -# Check if we need to uncompress the disk image. -# We use xz because it compresses better: to 42M vs 72M (gzip) / 62M (bzip2). -cd $(dirname $0) -if [ ! -f $ROOTFS ]; then - echo "Deleting $COMPRESSED_ROOTFS" >&2 - rm -f $COMPRESSED_ROOTFS - echo "Downloading $URL" >&2 - wget $URL - echo "Uncompressing $COMPRESSED_ROOTFS" >&2 - unxz $COMPRESSED_ROOTFS -fi -echo "Using $ROOTFS" -cd - - -# If network access was requested, create NUMTAPINTERFACES tap interfaces on -# the host, and prepare UML command line params to use them. The interfaces are -# called TAP0, TAP1, on the host, and eth0, eth1, ..., in the VM. -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" - done - - for tap in $tapinterfaces; do - if ! ip link list $tap > /dev/null; then - echo "Creating tap interface $tap" >&2 - sudo tunctl -u $USER -t $tap - sudo ip link set $tap up - fi - done -fi - -if [ -z "$KERNEL_BINARY" ]; then - # Exporting ARCH=um SUBARCH=x86_64 doesn't seem to work, as it "sometimes" - # (?) results in a 32-bit kernel. - - # If there's no kernel config at all, create one or UML won't work. - [ -f .config ] || make defconfig ARCH=um SUBARCH=x86_64 - - # Enable the kernel config options listed in $OPTIONS. - cmdline=${OPTIONS// / -e } - ./scripts/config $cmdline - - # Disable the kernel config options listed in $DISABLE_OPTIONS. - cmdline=${DISABLE_OPTIONS// / -d } - ./scripts/config $cmdline - - # olddefconfig doesn't work on old kernels. - if ! make olddefconfig ARCH=um SUBARCH=x86_64 CROSS_COMPILE= ; then - cat >&2 << EOF - -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 - - # Compile the kernel. - make -j32 linux ARCH=um SUBARCH=x86_64 CROSS_COMPILE= - KERNEL_BINARY=./linux -fi - - -# Get the absolute path to the test file that's being run. -dir=/host$(dirname $(readlink -f $0)) - -# Start the VM. -exec $KERNEL_BINARY umid=net_test ubda=$(dirname $0)/$ROOTFS \ - mem=512M init=/sbin/net_test.sh net_test=$dir/$test \ - $netconfig diff --git a/tests/net_test/sock_diag.py b/tests/net_test/sock_diag.py deleted file mode 100755 index 9e4a22d0..00000000 --- a/tests/net_test/sock_diag.py +++ /dev/null @@ -1,348 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2015 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. - -"""Partial Python implementation of sock_diag functionality.""" - -# pylint: disable=g-bad-todo - -import errno -from socket import * # pylint: disable=wildcard-import -import struct - -import cstruct -import net_test -import netlink - -### Base netlink constants. See include/uapi/linux/netlink.h. -NETLINK_SOCK_DIAG = 4 - -### sock_diag constants. See include/uapi/linux/sock_diag.h. -# Message types. -SOCK_DIAG_BY_FAMILY = 20 -SOCK_DESTROY = 21 - -### inet_diag_constants. See include/uapi/linux/inet_diag.h -# Message types. -TCPDIAG_GETSOCK = 18 - -# Request attributes. -INET_DIAG_REQ_BYTECODE = 1 - -# Extensions. -INET_DIAG_NONE = 0 -INET_DIAG_MEMINFO = 1 -INET_DIAG_INFO = 2 -INET_DIAG_VEGASINFO = 3 -INET_DIAG_CONG = 4 -INET_DIAG_TOS = 5 -INET_DIAG_TCLASS = 6 -INET_DIAG_SKMEMINFO = 7 -INET_DIAG_SHUTDOWN = 8 -INET_DIAG_DCTCPINFO = 9 - -# Bytecode operations. -INET_DIAG_BC_NOP = 0 -INET_DIAG_BC_JMP = 1 -INET_DIAG_BC_S_GE = 2 -INET_DIAG_BC_S_LE = 3 -INET_DIAG_BC_D_GE = 4 -INET_DIAG_BC_D_LE = 5 -INET_DIAG_BC_AUTO = 6 -INET_DIAG_BC_S_COND = 7 -INET_DIAG_BC_D_COND = 8 - -# Data structure formats. -# These aren't constants, they're classes. So, pylint: disable=invalid-name -InetDiagSockId = cstruct.Struct( - "InetDiagSockId", "!HH16s16sI8s", "sport dport src dst iface cookie") -InetDiagReqV2 = cstruct.Struct( - "InetDiagReqV2", "=BBBxIS", "family protocol ext states id", - [InetDiagSockId]) -InetDiagMsg = cstruct.Struct( - "InetDiagMsg", "=BBBBSLLLLL", - "family state timer retrans id expires rqueue wqueue uid inode", - [InetDiagSockId]) -InetDiagMeminfo = cstruct.Struct( - "InetDiagMeminfo", "=IIII", "rmem wmem fmem tmem") -InetDiagBcOp = cstruct.Struct("InetDiagBcOp", "BBH", "code yes no") -InetDiagHostcond = cstruct.Struct("InetDiagHostcond", "=BBxxi", - "family prefix_len port") - -SkMeminfo = cstruct.Struct( - "SkMeminfo", "=IIIIIIII", - "rmem_alloc rcvbuf wmem_alloc sndbuf fwd_alloc wmem_queued optmem backlog") -TcpInfo = cstruct.Struct( - "TcpInfo", "=BBBBBBBxIIIIIIIIIIIIIIIIIIIIIIII", - "state ca_state retransmits probes backoff options wscale " - "rto ato snd_mss rcv_mss " - "unacked sacked lost retrans fackets " - "last_data_sent last_ack_sent last_data_recv last_ack_recv " - "pmtu rcv_ssthresh rtt rttvar snd_ssthresh snd_cwnd advmss reordering " - "rcv_rtt rcv_space " - "total_retrans") # As of linux 3.13, at least. - -TCP_TIME_WAIT = 6 -ALL_NON_TIME_WAIT = 0xffffffff & ~(1 << TCP_TIME_WAIT) - - -class SockDiag(netlink.NetlinkSocket): - - FAMILY = NETLINK_SOCK_DIAG - NL_DEBUG = [] - - def _Decode(self, command, msg, nla_type, nla_data): - """Decodes netlink attributes to Python types.""" - if msg.family == AF_INET or msg.family == AF_INET6: - if isinstance(msg, InetDiagReqV2): - prefix = "INET_DIAG_REQ" - else: - prefix = "INET_DIAG" - name = self._GetConstantName(__name__, nla_type, prefix) - else: - # Don't know what this is. Leave it as an integer. - name = nla_type - - if name in ["INET_DIAG_SHUTDOWN", "INET_DIAG_TOS", "INET_DIAG_TCLASS"]: - data = ord(nla_data) - elif name == "INET_DIAG_CONG": - data = nla_data.strip("\x00") - elif name == "INET_DIAG_MEMINFO": - data = InetDiagMeminfo(nla_data) - elif name == "INET_DIAG_INFO": - # TODO: Catch the exception and try something else if it's not TCP. - data = TcpInfo(nla_data) - elif name == "INET_DIAG_SKMEMINFO": - data = SkMeminfo(nla_data) - elif name == "INET_DIAG_REQ_BYTECODE": - data = nla_data.encode("hex") - else: - data = nla_data - - return name, data - - def MaybeDebugCommand(self, command, data): - name = self._GetConstantName(__name__, command, "SOCK_") - if "ALL" not in self.NL_DEBUG and "SOCK" not in self.NL_DEBUG: - return - parsed = self._ParseNLMsg(data, InetDiagReqV2) - print "%s %s" % (name, str(parsed)) - - @staticmethod - def _EmptyInetDiagSockId(): - return InetDiagSockId(("\x00" * len(InetDiagSockId))) - - def PackBytecode(self, instructions): - """Compiles instructions to inet_diag bytecode. - - The input is a list of (INET_DIAG_BC_xxx, yes, no, arg) tuples, where yes - and no are relative jump offsets measured in instructions. The yes branch - is taken if the instruction matches. - - To accept, jump 1 past the last instruction. To reject, jump 2 past the - last instruction. - - The target of a no jump is only valid if it is reachable by following - only yes jumps from the first instruction - see inet_diag_bc_audit and - valid_cc. This means that if cond1 and cond2 are two mutually exclusive - filter terms, it is not possible to implement cond1 OR cond2 using: - - ... - cond1 2 1 arg - cond2 1 2 arg - accept - reject - - but only using: - - ... - cond1 1 2 arg - jmp 1 2 - cond2 1 2 arg - accept - reject - - The jmp instruction ignores yes and always jumps to no, but yes must be 1 - or the bytecode won't validate. It doesn't have to be jmp - any instruction - that is guaranteed not to match on real data will do. - - Args: - instructions: list of instruction tuples - - Returns: - A string, the raw bytecode. - """ - args = [] - positions = [0] - - for op, yes, no, arg in instructions: - - if yes <= 0 or no <= 0: - raise ValueError("Jumps must be > 0") - - if op in [INET_DIAG_BC_NOP, INET_DIAG_BC_JMP, INET_DIAG_BC_AUTO]: - arg = "" - elif op in [INET_DIAG_BC_S_GE, INET_DIAG_BC_S_LE, - INET_DIAG_BC_D_GE, INET_DIAG_BC_D_LE]: - arg = "\x00\x00" + struct.pack("=H", arg) - elif op in [INET_DIAG_BC_S_COND, INET_DIAG_BC_D_COND]: - addr, prefixlen, port = arg - family = AF_INET6 if ":" in addr else AF_INET - addr = inet_pton(family, addr) - arg = InetDiagHostcond((family, prefixlen, port)).Pack() + addr - else: - raise ValueError("Unsupported opcode %d" % op) - - args.append(arg) - length = len(InetDiagBcOp) + len(arg) - positions.append(positions[-1] + length) - - # Reject label. - positions.append(positions[-1] + 4) # Why 4? Because the kernel uses 4. - assert len(args) == len(instructions) == len(positions) - 2 - - # print positions - - packed = "" - for i, (op, yes, no, arg) in enumerate(instructions): - yes = positions[i + yes] - positions[i] - no = positions[i + no] - positions[i] - instruction = InetDiagBcOp((op, yes, no)).Pack() + args[i] - #print "%3d: %d %3d %3d %s %s" % (positions[i], op, yes, no, - # arg, instruction.encode("hex")) - packed += instruction - #print - - return packed - - def Dump(self, diag_req, bytecode): - out = self._Dump(SOCK_DIAG_BY_FAMILY, diag_req, InetDiagMsg, bytecode) - return out - - def DumpAllInetSockets(self, protocol, bytecode, sock_id=None, ext=0, - states=ALL_NON_TIME_WAIT): - """Dumps IPv4 or IPv6 sockets matching the specified parameters.""" - # DumpSockets(AF_UNSPEC) does not result in dumping all inet sockets, it - # results in ENOENT. - if sock_id is None: - sock_id = self._EmptyInetDiagSockId() - - if bytecode: - bytecode = self._NlAttr(INET_DIAG_REQ_BYTECODE, bytecode) - - sockets = [] - for family in [AF_INET, AF_INET6]: - diag_req = InetDiagReqV2((family, protocol, ext, states, sock_id)) - sockets += self.Dump(diag_req, bytecode) - - return sockets - - @staticmethod - def GetRawAddress(family, addr): - """Fetches the source address from an InetDiagMsg.""" - addrlen = {AF_INET:4, AF_INET6: 16}[family] - return inet_ntop(family, addr[:addrlen]) - - @staticmethod - def GetSourceAddress(diag_msg): - """Fetches the source address from an InetDiagMsg.""" - return SockDiag.GetRawAddress(diag_msg.family, diag_msg.id.src) - - @staticmethod - def GetDestinationAddress(diag_msg): - """Fetches the source address from an InetDiagMsg.""" - return SockDiag.GetRawAddress(diag_msg.family, diag_msg.id.dst) - - @staticmethod - def RawAddress(addr): - """Converts an IP address string to binary format.""" - family = AF_INET6 if ":" in addr else AF_INET - return inet_pton(family, addr) - - @staticmethod - def PaddedAddress(addr): - """Converts an IP address string to binary format for InetDiagSockId.""" - padded = SockDiag.RawAddress(addr) - if len(padded) < 16: - padded += "\x00" * (16 - len(padded)) - return padded - - @staticmethod - def DiagReqFromSocket(s): - """Creates an InetDiagReqV2 that matches the specified socket.""" - family = s.getsockopt(net_test.SOL_SOCKET, net_test.SO_DOMAIN) - protocol = s.getsockopt(net_test.SOL_SOCKET, net_test.SO_PROTOCOL) - if net_test.LINUX_VERSION >= (3, 8): - iface = s.getsockopt(SOL_SOCKET, net_test.SO_BINDTODEVICE, - net_test.IFNAMSIZ) - iface = GetInterfaceIndex(iface) if iface else 0 - else: - iface = 0 - src, sport = s.getsockname()[:2] - try: - dst, dport = s.getpeername()[:2] - except error, e: - if e.errno == errno.ENOTCONN: - dport = 0 - dst = "::" if family == AF_INET6 else "0.0.0.0" - else: - raise e - src = SockDiag.PaddedAddress(src) - dst = SockDiag.PaddedAddress(dst) - sock_id = InetDiagSockId((sport, dport, src, dst, iface, "\x00" * 8)) - return InetDiagReqV2((family, protocol, 0, 0xffffffff, sock_id)) - - def FindSockDiagFromReq(self, req): - for diag_msg, attrs in self.Dump(req, ""): - return diag_msg - raise ValueError("Dump of %s returned no sockets" % req) - - def FindSockDiagFromFd(self, s): - """Gets an InetDiagMsg from the kernel for the specified socket.""" - req = self.DiagReqFromSocket(s) - return self.FindSockDiagFromReq(req) - - def GetSockDiag(self, req): - """Gets an InetDiagMsg from the kernel for the specified request.""" - self._SendNlRequest(SOCK_DIAG_BY_FAMILY, req.Pack(), netlink.NLM_F_REQUEST) - return self._GetMsg(InetDiagMsg)[0] - - @staticmethod - def DiagReqFromDiagMsg(d, protocol): - """Constructs a diag_req from a diag_msg the kernel has given us.""" - return InetDiagReqV2((d.family, protocol, 0, 1 << d.state, d.id)) - - def CloseSocket(self, req): - self._SendNlRequest(SOCK_DESTROY, req.Pack(), - netlink.NLM_F_REQUEST | netlink.NLM_F_ACK) - - def CloseSocketFromFd(self, s): - diag_msg = self.FindSockDiagFromFd(s) - protocol = s.getsockopt(SOL_SOCKET, net_test.SO_PROTOCOL) - req = self.DiagReqFromDiagMsg(diag_msg, protocol) - return self.CloseSocket(req) - - -if __name__ == "__main__": - n = SockDiag() - n.DEBUG = True - bytecode = "" - sock_id = n._EmptyInetDiagSockId() - sock_id.dport = 443 - ext = 1 << (INET_DIAG_TOS - 1) | 1 << (INET_DIAG_TCLASS - 1) - states = 0xffffffff - diag_msgs = n.DumpAllInetSockets(IPPROTO_TCP, "", - sock_id=sock_id, ext=ext, states=states) - print diag_msgs diff --git a/tests/net_test/sock_diag_test.py b/tests/net_test/sock_diag_test.py deleted file mode 100755 index 71cce384..00000000 --- a/tests/net_test/sock_diag_test.py +++ /dev/null @@ -1,602 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2015 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. - -# pylint: disable=g-bad-todo,g-bad-file-header,wildcard-import -from errno import * # pylint: disable=wildcard-import -import os -import random -import re -from socket import * # pylint: disable=wildcard-import -import threading -import time -import unittest - -import multinetwork_base -import net_test -import packets -import sock_diag -import tcp_test - - -NUM_SOCKETS = 30 -NO_BYTECODE = "" - - -class SockDiagBaseTest(multinetwork_base.MultiNetworkBaseTest): - """Basic tests for SOCK_DIAG functionality. - - Relevant kernel commits: - android-3.4: - ab4a727 net: inet_diag: zero out uninitialized idiag_{src,dst} fields - 99ee451 net: diag: support v4mapped sockets in inet_diag_find_one_icsk() - - android-3.10: - 3eb409b net: inet_diag: zero out uninitialized idiag_{src,dst} fields - f77e059 net: diag: support v4mapped sockets in inet_diag_find_one_icsk() - - android-3.18: - e603010 net: diag: support v4mapped sockets in inet_diag_find_one_icsk() - - android-4.4: - 525ee59 net: diag: support v4mapped sockets in inet_diag_find_one_icsk() - """ - @staticmethod - def _CreateLotsOfSockets(): - # Dict mapping (addr, sport, dport) tuples to socketpairs. - socketpairs = {} - for _ in xrange(NUM_SOCKETS): - family, addr = random.choice([ - (AF_INET, "127.0.0.1"), - (AF_INET6, "::1"), - (AF_INET6, "::ffff:127.0.0.1")]) - socketpair = net_test.CreateSocketPair(family, SOCK_STREAM, addr) - sport, dport = (socketpair[0].getsockname()[1], - socketpair[1].getsockname()[1]) - socketpairs[(addr, sport, dport)] = socketpair - return socketpairs - - def assertSocketClosed(self, sock): - self.assertRaisesErrno(ENOTCONN, sock.getpeername) - - def assertSocketConnected(self, sock): - sock.getpeername() # No errors? Socket is alive and connected. - - def assertSocketsClosed(self, socketpair): - for sock in socketpair: - self.assertSocketClosed(sock) - - def setUp(self): - super(SockDiagBaseTest, self).setUp() - self.sock_diag = sock_diag.SockDiag() - self.socketpairs = {} - - def tearDown(self): - for socketpair in self.socketpairs.values(): - for s in socketpair: - s.close() - super(SockDiagBaseTest, self).tearDown() - - -class SockDiagTest(SockDiagBaseTest): - - def assertSockDiagMatchesSocket(self, s, diag_msg): - family = s.getsockopt(net_test.SOL_SOCKET, net_test.SO_DOMAIN) - self.assertEqual(diag_msg.family, family) - - src, sport = s.getsockname()[0:2] - self.assertEqual(diag_msg.id.src, self.sock_diag.PaddedAddress(src)) - self.assertEqual(diag_msg.id.sport, sport) - - if self.sock_diag.GetDestinationAddress(diag_msg) not in ["0.0.0.0", "::"]: - dst, dport = s.getpeername()[0:2] - self.assertEqual(diag_msg.id.dst, self.sock_diag.PaddedAddress(dst)) - self.assertEqual(diag_msg.id.dport, dport) - else: - self.assertRaisesErrno(ENOTCONN, s.getpeername) - - def testFindsMappedSockets(self): - """Tests that inet_diag_find_one_icsk can find mapped sockets.""" - socketpair = net_test.CreateSocketPair(AF_INET6, SOCK_STREAM, - "::ffff:127.0.0.1") - for sock in socketpair: - diag_msg = self.sock_diag.FindSockDiagFromFd(sock) - diag_req = self.sock_diag.DiagReqFromDiagMsg(diag_msg, IPPROTO_TCP) - self.sock_diag.GetSockDiag(diag_req) - # No errors? Good. - - def testFindsAllMySockets(self): - """Tests that basic socket dumping works.""" - self.socketpairs = self._CreateLotsOfSockets() - sockets = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, NO_BYTECODE) - self.assertGreaterEqual(len(sockets), NUM_SOCKETS) - - # Find the cookies for all of our sockets. - cookies = {} - for diag_msg, unused_attrs in sockets: - addr = self.sock_diag.GetSourceAddress(diag_msg) - sport = diag_msg.id.sport - dport = diag_msg.id.dport - if (addr, sport, dport) in self.socketpairs: - cookies[(addr, sport, dport)] = diag_msg.id.cookie - elif (addr, dport, sport) in self.socketpairs: - cookies[(addr, sport, dport)] = diag_msg.id.cookie - - # Did we find all the cookies? - self.assertEquals(2 * NUM_SOCKETS, len(cookies)) - - socketpairs = self.socketpairs.values() - random.shuffle(socketpairs) - for socketpair in socketpairs: - for sock in socketpair: - # Check that we can find a diag_msg by scanning a dump. - self.assertSockDiagMatchesSocket( - sock, - self.sock_diag.FindSockDiagFromFd(sock)) - cookie = self.sock_diag.FindSockDiagFromFd(sock).id.cookie - - # Check that we can find a diag_msg once we know the cookie. - req = self.sock_diag.DiagReqFromSocket(sock) - req.id.cookie = cookie - diag_msg = self.sock_diag.GetSockDiag(req) - req.states = 1 << diag_msg.state - self.assertSockDiagMatchesSocket(sock, diag_msg) - - def testBytecodeCompilation(self): - # pylint: disable=bad-whitespace - instructions = [ - (sock_diag.INET_DIAG_BC_S_GE, 1, 8, 0), # 0 - (sock_diag.INET_DIAG_BC_D_LE, 1, 7, 0xffff), # 8 - (sock_diag.INET_DIAG_BC_S_COND, 1, 2, ("::1", 128, -1)), # 16 - (sock_diag.INET_DIAG_BC_JMP, 1, 3, None), # 44 - (sock_diag.INET_DIAG_BC_S_COND, 2, 4, ("127.0.0.1", 32, -1)), # 48 - (sock_diag.INET_DIAG_BC_D_LE, 1, 3, 0x6665), # not used # 64 - (sock_diag.INET_DIAG_BC_NOP, 1, 1, None), # 72 - # 76 acc - # 80 rej - ] - # pylint: enable=bad-whitespace - bytecode = self.sock_diag.PackBytecode(instructions) - expected = ( - "0208500000000000" - "050848000000ffff" - "071c20000a800000ffffffff00000000000000000000000000000001" - "01041c00" - "0718200002200000ffffffff7f000001" - "0508100000006566" - "00040400" - ) - states = 1 << tcp_test.TCP_ESTABLISHED - self.assertMultiLineEqual(expected, bytecode.encode("hex")) - self.assertEquals(76, len(bytecode)) - self.socketpairs = self._CreateLotsOfSockets() - filteredsockets = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, bytecode, - states=states) - allsockets = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, NO_BYTECODE, - states=states) - self.assertItemsEqual(allsockets, filteredsockets) - - # Pick a few sockets in hash table order, and check that the bytecode we - # compiled selects them properly. - for socketpair in self.socketpairs.values()[:20]: - for s in socketpair: - diag_msg = self.sock_diag.FindSockDiagFromFd(s) - instructions = [ - (sock_diag.INET_DIAG_BC_S_GE, 1, 5, diag_msg.id.sport), - (sock_diag.INET_DIAG_BC_S_LE, 1, 4, diag_msg.id.sport), - (sock_diag.INET_DIAG_BC_D_GE, 1, 3, diag_msg.id.dport), - (sock_diag.INET_DIAG_BC_D_LE, 1, 2, diag_msg.id.dport), - ] - bytecode = self.sock_diag.PackBytecode(instructions) - self.assertEquals(32, len(bytecode)) - sockets = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, bytecode) - self.assertEquals(1, len(sockets)) - - # TODO: why doesn't comparing the cstructs work? - self.assertEquals(diag_msg.Pack(), sockets[0][0].Pack()) - - def testCrossFamilyBytecode(self): - """Checks for a cross-family bug in inet_diag_hostcond matching. - - Relevant kernel commits: - android-3.4: - f67caec inet_diag: avoid unsafe and nonsensical prefix matches in inet_diag_bc_run() - """ - # TODO: this is only here because the test fails if there are any open - # sockets other than the ones it creates itself. Make the bytecode more - # specific and remove it. - states = 1 << tcp_test.TCP_ESTABLISHED - self.assertFalse(self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, "", - states=states)) - - unused_pair4 = net_test.CreateSocketPair(AF_INET, SOCK_STREAM, "127.0.0.1") - unused_pair6 = net_test.CreateSocketPair(AF_INET6, SOCK_STREAM, "::1") - - bytecode4 = self.sock_diag.PackBytecode([ - (sock_diag.INET_DIAG_BC_S_COND, 1, 2, ("0.0.0.0", 0, -1))]) - bytecode6 = self.sock_diag.PackBytecode([ - (sock_diag.INET_DIAG_BC_S_COND, 1, 2, ("::", 0, -1))]) - - # IPv4/v6 filters must never match IPv6/IPv4 sockets... - v4socks = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, bytecode4, - states=states) - self.assertTrue(v4socks) - self.assertTrue(all(d.family == AF_INET for d, _ in v4socks)) - - v6socks = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, bytecode6, - states=states) - self.assertTrue(v6socks) - self.assertTrue(all(d.family == AF_INET6 for d, _ in v6socks)) - - # Except for mapped addresses, which match both IPv4 and IPv6. - pair5 = net_test.CreateSocketPair(AF_INET6, SOCK_STREAM, - "::ffff:127.0.0.1") - diag_msgs = [self.sock_diag.FindSockDiagFromFd(s) for s in pair5] - v4socks = [d for d, _ in self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, - bytecode4, - states=states)] - v6socks = [d for d, _ in self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, - bytecode6, - states=states)] - self.assertTrue(all(d in v4socks for d in diag_msgs)) - self.assertTrue(all(d in v6socks for d in diag_msgs)) - - def testPortComparisonValidation(self): - """Checks for a bug in validating port comparison bytecode. - - Relevant kernel commits: - android-3.4: - 5e1f542 inet_diag: validate port comparison byte code to prevent unsafe reads - """ - bytecode = sock_diag.InetDiagBcOp((sock_diag.INET_DIAG_BC_D_GE, 4, 8)) - self.assertRaisesErrno( - EINVAL, - self.sock_diag.DumpAllInetSockets, IPPROTO_TCP, bytecode.Pack()) - - def testNonSockDiagCommand(self): - def DiagDump(code): - sock_id = self.sock_diag._EmptyInetDiagSockId() - req = sock_diag.InetDiagReqV2((AF_INET6, IPPROTO_TCP, 0, 0xffffffff, - sock_id)) - self.sock_diag._Dump(code, req, sock_diag.InetDiagMsg, "") - - op = sock_diag.SOCK_DIAG_BY_FAMILY - DiagDump(op) # No errors? Good. - self.assertRaisesErrno(EINVAL, DiagDump, op + 17) - - -class SockDestroyTest(SockDiagBaseTest): - """Tests that SOCK_DESTROY works correctly. - - Relevant kernel commits: - net-next: - b613f56 net: diag: split inet_diag_dump_one_icsk into two - 64be0ae net: diag: Add the ability to destroy a socket. - 6eb5d2e net: diag: Support SOCK_DESTROY for inet sockets. - c1e64e2 net: diag: Support destroying TCP sockets. - 2010b93 net: tcp: deal with listen sockets properly in tcp_abort. - - android-3.4: - d48ec88 net: diag: split inet_diag_dump_one_icsk into two - 2438189 net: diag: Add the ability to destroy a socket. - 7a2ddbc net: diag: Support SOCK_DESTROY for inet sockets. - 44047b2 net: diag: Support destroying TCP sockets. - 200dae7 net: tcp: deal with listen sockets properly in tcp_abort. - - android-3.10: - 9eaff90 net: diag: split inet_diag_dump_one_icsk into two - d60326c net: diag: Add the ability to destroy a socket. - 3d4ce85 net: diag: Support SOCK_DESTROY for inet sockets. - 529dfc6 net: diag: Support destroying TCP sockets. - 9c712fe net: tcp: deal with listen sockets properly in tcp_abort. - - android-3.18: - 100263d net: diag: split inet_diag_dump_one_icsk into two - 194c5f3 net: diag: Add the ability to destroy a socket. - 8387ea2 net: diag: Support SOCK_DESTROY for inet sockets. - b80585a net: diag: Support destroying TCP sockets. - 476c6ce net: tcp: deal with listen sockets properly in tcp_abort. - - android-4.1: - 56eebf8 net: diag: split inet_diag_dump_one_icsk into two - fb486c9 net: diag: Add the ability to destroy a socket. - 0c02b7e net: diag: Support SOCK_DESTROY for inet sockets. - 67c71d8 net: diag: Support destroying TCP sockets. - a76e0ec net: tcp: deal with listen sockets properly in tcp_abort. - e6e277b net: diag: support v4mapped sockets in inet_diag_find_one_icsk() - - android-4.4: - 76c83a9 net: diag: split inet_diag_dump_one_icsk into two - f7cf791 net: diag: Add the ability to destroy a socket. - 1c42248 net: diag: Support SOCK_DESTROY for inet sockets. - c9e8440d net: diag: Support destroying TCP sockets. - 3d9502c tcp: diag: add support for request sockets to tcp_abort() - 001cf75 net: tcp: deal with listen sockets properly in tcp_abort. - """ - - def testClosesSockets(self): - self.socketpairs = self._CreateLotsOfSockets() - for _, socketpair in self.socketpairs.iteritems(): - # Close one of the sockets. - # This will send a RST that will close the other side as well. - s = random.choice(socketpair) - if random.randrange(0, 2) == 1: - self.sock_diag.CloseSocketFromFd(s) - else: - diag_msg = self.sock_diag.FindSockDiagFromFd(s) - - # Get the cookie wrong and ensure that we get an error and the socket - # is not closed. - real_cookie = diag_msg.id.cookie - diag_msg.id.cookie = os.urandom(len(real_cookie)) - req = self.sock_diag.DiagReqFromDiagMsg(diag_msg, IPPROTO_TCP) - self.assertRaisesErrno(ENOENT, self.sock_diag.CloseSocket, req) - self.assertSocketConnected(s) - - # Now close it with the correct cookie. - req.id.cookie = real_cookie - self.sock_diag.CloseSocket(req) - - # Check that both sockets in the pair are closed. - self.assertSocketsClosed(socketpair) - - def testNonTcpSockets(self): - s = socket(AF_INET6, SOCK_DGRAM, 0) - s.connect(("::1", 53)) - self.sock_diag.FindSockDiagFromFd(s) # No exceptions? Good. - self.assertRaisesErrno(EOPNOTSUPP, self.sock_diag.CloseSocketFromFd, s) - - # TODO: - # Test that killing unix sockets returns EOPNOTSUPP. - - -class SocketExceptionThread(threading.Thread): - - def __init__(self, sock, operation): - self.exception = None - super(SocketExceptionThread, self).__init__() - self.daemon = True - self.sock = sock - self.operation = operation - - def run(self): - try: - self.operation(self.sock) - except IOError, e: - self.exception = e - - -class SockDiagTcpTest(tcp_test.TcpBaseTest, SockDiagBaseTest): - - def testIpv4MappedSynRecvSocket(self): - """Tests for the absence of a bug with AF_INET6 TCP SYN-RECV sockets. - - Relevant kernel commits: - android-3.4: - 457a04b inet_diag: fix oops for IPv4 AF_INET6 TCP SYN-RECV state - """ - netid = random.choice(self.tuns.keys()) - self.IncomingConnection(5, tcp_test.TCP_SYN_RECV, netid) - sock_id = self.sock_diag._EmptyInetDiagSockId() - sock_id.sport = self.port - states = 1 << tcp_test.TCP_SYN_RECV - req = sock_diag.InetDiagReqV2((AF_INET6, IPPROTO_TCP, 0, states, sock_id)) - children = self.sock_diag.Dump(req, NO_BYTECODE) - - self.assertTrue(children) - for child, unused_args in children: - self.assertEqual(tcp_test.TCP_SYN_RECV, child.state) - self.assertEqual(self.sock_diag.PaddedAddress(self.remoteaddr), - child.id.dst) - self.assertEqual(self.sock_diag.PaddedAddress(self.myaddr), - child.id.src) - - -class SockDestroyTcpTest(tcp_test.TcpBaseTest, SockDiagBaseTest): - - def setUp(self): - super(SockDestroyTcpTest, self).setUp() - self.netid = random.choice(self.tuns.keys()) - - def CheckRstOnClose(self, sock, req, expect_reset, msg, do_close=True): - """Closes the socket and checks whether a RST is sent or not.""" - if sock is not None: - self.assertIsNone(req, "Must specify sock or req, not both") - self.sock_diag.CloseSocketFromFd(sock) - self.assertRaisesErrno(EINVAL, sock.accept) - else: - self.assertIsNone(sock, "Must specify sock or req, not both") - self.sock_diag.CloseSocket(req) - - if expect_reset: - desc, rst = self.RstPacket() - msg = "%s: expecting %s: " % (msg, desc) - self.ExpectPacketOn(self.netid, msg, rst) - else: - msg = "%s: " % msg - self.ExpectNoPacketsOn(self.netid, msg) - - if sock is not None and do_close: - sock.close() - - def CheckTcpReset(self, state, statename): - for version in [4, 5, 6]: - msg = "Closing incoming IPv%d %s socket" % (version, statename) - self.IncomingConnection(version, state, self.netid) - self.CheckRstOnClose(self.s, None, False, msg) - if state != tcp_test.TCP_LISTEN: - msg = "Closing accepted IPv%d %s socket" % (version, statename) - self.CheckRstOnClose(self.accepted, None, True, msg) - - def testTcpResets(self): - """Checks that closing sockets in appropriate states sends a RST.""" - self.CheckTcpReset(tcp_test.TCP_LISTEN, "TCP_LISTEN") - self.CheckTcpReset(tcp_test.TCP_ESTABLISHED, "TCP_ESTABLISHED") - self.CheckTcpReset(tcp_test.TCP_CLOSE_WAIT, "TCP_CLOSE_WAIT") - - def testFinWait1Socket(self): - for version in [4, 5, 6]: - self.IncomingConnection(version, tcp_test.TCP_ESTABLISHED, self.netid) - - # Get the cookie so we can find this socket after we close it. - diag_msg = self.sock_diag.FindSockDiagFromFd(self.accepted) - diag_req = self.sock_diag.DiagReqFromDiagMsg(diag_msg, IPPROTO_TCP) - - # Close the socket and check that it goes into FIN_WAIT1 and sends a FIN. - net_test.EnableFinWait(self.accepted) - self.accepted.close() - diag_req.states = 1 << tcp_test.TCP_FIN_WAIT1 - diag_msg = self.sock_diag.GetSockDiag(diag_req) - self.assertEquals(tcp_test.TCP_FIN_WAIT1, diag_msg.state) - desc, fin = self.FinPacket() - self.ExpectPacketOn(self.netid, "Closing FIN_WAIT1 socket", fin) - - # Destroy the socket and expect no RST. - self.CheckRstOnClose(None, diag_req, False, "Closing FIN_WAIT1 socket") - self.sock_diag.GetSockDiag(diag_req) - - # The socket is still in FIN_WAIT1: SOCK_DESTROY did nothing because - # userspace had already closed it. - diag_msg = self.assertEquals(tcp_test.TCP_FIN_WAIT1, diag_msg.state) - - def FindChildSockets(self, s): - """Finds the SYN_RECV child sockets of a given listening socket.""" - d = self.sock_diag.FindSockDiagFromFd(self.s) - req = self.sock_diag.DiagReqFromDiagMsg(d, IPPROTO_TCP) - req.states = 1 << tcp_test.TCP_SYN_RECV | 1 << tcp_test.TCP_ESTABLISHED - req.id.cookie = "\x00" * 8 - children = self.sock_diag.Dump(req, NO_BYTECODE) - return [self.sock_diag.DiagReqFromDiagMsg(d, IPPROTO_TCP) - for d, _ in children] - - def CheckChildSocket(self, version, statename, parent_first): - state = getattr(tcp_test, statename) - - self.IncomingConnection(version, state, self.netid) - - d = self.sock_diag.FindSockDiagFromFd(self.s) - parent = self.sock_diag.DiagReqFromDiagMsg(d, IPPROTO_TCP) - children = self.FindChildSockets(self.s) - self.assertEquals(1, len(children)) - - is_established = (state == tcp_test.TCP_NOT_YET_ACCEPTED) - - # The new TCP listener code in 4.4 makes SYN_RECV sockets live in the - # regular TCP hash tables, and inet_diag_find_one_icsk can find them. - # Before 4.4, we can see those sockets in dumps, but we can't fetch - # or close them. - can_close_children = is_established or net_test.LINUX_VERSION >= (4, 4) - - for child in children: - if can_close_children: - self.sock_diag.GetSockDiag(child) # No errors? Good, child found. - else: - self.assertRaisesErrno(ENOENT, self.sock_diag.GetSockDiag, child) - - def CloseParent(expect_reset): - msg = "Closing parent IPv%d %s socket %s child" % ( - version, statename, "before" if parent_first else "after") - self.CheckRstOnClose(self.s, None, expect_reset, msg) - self.assertRaisesErrno(ENOENT, self.sock_diag.GetSockDiag, parent) - - def CheckChildrenClosed(): - for child in children: - self.assertRaisesErrno(ENOENT, self.sock_diag.GetSockDiag, child) - - def CloseChildren(): - for child in children: - msg = "Closing child IPv%d %s socket %s parent" % ( - version, statename, "after" if parent_first else "before") - self.sock_diag.GetSockDiag(child) - self.CheckRstOnClose(None, child, is_established, msg) - self.assertRaisesErrno(ENOENT, self.sock_diag.GetSockDiag, child) - CheckChildrenClosed() - - if parent_first: - # Closing the parent will close child sockets, which will send a RST, - # iff they are already established. - CloseParent(is_established) - if is_established: - CheckChildrenClosed() - elif can_close_children: - CloseChildren() - CheckChildrenClosed() - self.s.close() - else: - if can_close_children: - CloseChildren() - CloseParent(False) - self.s.close() - - def testChildSockets(self): - for version in [4, 5, 6]: - self.CheckChildSocket(version, "TCP_SYN_RECV", False) - self.CheckChildSocket(version, "TCP_SYN_RECV", True) - self.CheckChildSocket(version, "TCP_NOT_YET_ACCEPTED", False) - self.CheckChildSocket(version, "TCP_NOT_YET_ACCEPTED", True) - - def CloseDuringBlockingCall(self, sock, call, expected_errno): - thread = SocketExceptionThread(sock, call) - thread.start() - time.sleep(0.1) - self.sock_diag.CloseSocketFromFd(sock) - thread.join(1) - self.assertFalse(thread.is_alive()) - self.assertIsNotNone(thread.exception) - self.assertTrue(isinstance(thread.exception, IOError), - "Expected IOError, got %s" % thread.exception) - self.assertEqual(expected_errno, thread.exception.errno) - self.assertSocketClosed(sock) - - def testAcceptInterrupted(self): - """Tests that accept() is interrupted by SOCK_DESTROY.""" - for version in [4, 5, 6]: - self.IncomingConnection(version, tcp_test.TCP_LISTEN, self.netid) - self.CloseDuringBlockingCall(self.s, lambda sock: sock.accept(), EINVAL) - self.assertRaisesErrno(ECONNABORTED, self.s.send, "foo") - self.assertRaisesErrno(EINVAL, self.s.accept) - - def testReadInterrupted(self): - """Tests that read() is interrupted by SOCK_DESTROY.""" - for version in [4, 5, 6]: - self.IncomingConnection(version, tcp_test.TCP_ESTABLISHED, self.netid) - self.CloseDuringBlockingCall(self.accepted, lambda sock: sock.recv(4096), - ECONNABORTED) - self.assertRaisesErrno(EPIPE, self.accepted.send, "foo") - - def testConnectInterrupted(self): - """Tests that connect() is interrupted by SOCK_DESTROY.""" - for version in [4, 5, 6]: - family = {4: AF_INET, 5: AF_INET6, 6: AF_INET6}[version] - s = net_test.Socket(family, SOCK_STREAM, IPPROTO_TCP) - self.SelectInterface(s, self.netid, "mark") - if version == 5: - remoteaddr = "::ffff:" + self.GetRemoteAddress(4) - version = 4 - else: - remoteaddr = self.GetRemoteAddress(version) - s.bind(("", 0)) - _, sport = s.getsockname()[:2] - self.CloseDuringBlockingCall( - s, lambda sock: sock.connect((remoteaddr, 53)), ECONNABORTED) - desc, syn = packets.SYN(53, version, self.MyAddress(version, self.netid), - remoteaddr, sport=sport, seq=None) - self.ExpectPacketOn(self.netid, desc, syn) - msg = "SOCK_DESTROY of socket in connect, expected no RST" - self.ExpectNoPacketsOn(self.netid, msg) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/net_test/srcaddr_selection_test.py b/tests/net_test/srcaddr_selection_test.py deleted file mode 100755 index d3efdd9d..00000000 --- a/tests/net_test/srcaddr_selection_test.py +++ /dev/null @@ -1,345 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2014 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 errno -import random -from socket import * # pylint: disable=wildcard-import -import time -import unittest - -from scapy import all as scapy - -import csocket -import iproute -import multinetwork_base -import packets -import net_test - -# Setsockopt values. -IPV6_ADDR_PREFERENCES = 72 -IPV6_PREFER_SRC_PUBLIC = 0x0002 - - -class IPv6SourceAddressSelectionTest(multinetwork_base.MultiNetworkBaseTest): - """Test for IPv6 source address selection. - - Relevant kernel commits: - upstream net-next: - 7fd2561 net: ipv6: Add a sysctl to make optimistic addresses useful candidates - c58da4c net: ipv6: allow explicitly choosing optimistic addresses - 9131f3d ipv6: Do not iterate over all interfaces when finding source address on specific interface. - c0b8da1 ipv6: Fix finding best source address in ipv6_dev_get_saddr(). - c15df30 ipv6: Remove unused arguments for __ipv6_dev_get_saddr(). - 3985e8a ipv6: sysctl to restrict candidate source addresses - - android-3.10: - 2ce95507 net: ipv6: Add a sysctl to make optimistic addresses useful candidates - 0065bf4 net: ipv6: allow choosing optimistic addresses with use_optimistic - 0633924 ipv6: sysctl to restrict candidate source addresses - """ - - def SetIPv6Sysctl(self, ifname, sysctl, value): - self.SetSysctl("/proc/sys/net/ipv6/conf/%s/%s" % (ifname, sysctl), value) - - def SetDAD(self, ifname, value): - self.SetSysctl("/proc/sys/net/ipv6/conf/%s/accept_dad" % ifname, value) - self.SetSysctl("/proc/sys/net/ipv6/conf/%s/dad_transmits" % ifname, value) - - def SetOptimisticDAD(self, ifname, value): - self.SetSysctl("/proc/sys/net/ipv6/conf/%s/optimistic_dad" % ifname, value) - - def SetUseTempaddrs(self, ifname, value): - self.SetSysctl("/proc/sys/net/ipv6/conf/%s/use_tempaddr" % ifname, value) - - def SetUseOptimistic(self, ifname, value): - self.SetSysctl("/proc/sys/net/ipv6/conf/%s/use_optimistic" % ifname, value) - - def GetSourceIP(self, netid, mode="mark"): - s = self.BuildSocket(6, net_test.UDPSocket, netid, mode) - # Because why not...testing for temporary addresses is a separate thing. - s.setsockopt(IPPROTO_IPV6, IPV6_ADDR_PREFERENCES, IPV6_PREFER_SRC_PUBLIC) - - s.connect((net_test.IPV6_ADDR, 123)) - src_addr = s.getsockname()[0] - self.assertTrue(src_addr) - return src_addr - - def assertAddressNotPresent(self, address): - self.assertRaises(IOError, self.iproute.GetAddress, address) - - def assertAddressHasExpectedAttributes( - self, address, expected_ifindex, expected_flags): - ifa_msg = self.iproute.GetAddress(address)[0] - self.assertEquals(AF_INET6 if ":" in address else AF_INET, ifa_msg.family) - self.assertEquals(64, ifa_msg.prefixlen) - self.assertEquals(iproute.RT_SCOPE_UNIVERSE, ifa_msg.scope) - self.assertEquals(expected_ifindex, ifa_msg.index) - self.assertEquals(expected_flags, ifa_msg.flags & expected_flags) - - def AddressIsTentative(self, address): - ifa_msg = self.iproute.GetAddress(address)[0] - return ifa_msg.flags & iproute.IFA_F_TENTATIVE - - def BindToAddress(self, address): - s = net_test.UDPSocket(AF_INET6) - s.bind((address, 0, 0, 0)) - - 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), "Hello", cmsgs, 0) - - def assertAddressUsable(self, address, netid): - self.BindToAddress(address) - self.SendWithSourceAddress(address, netid) - # No exceptions? Good. - - def assertAddressNotUsable(self, address, netid): - self.assertRaisesErrno(errno.EADDRNOTAVAIL, self.BindToAddress, address) - self.assertRaisesErrno(errno.EINVAL, - self.SendWithSourceAddress, address, netid) - - def assertAddressSelected(self, address, netid): - self.assertEquals(address, self.GetSourceIP(netid)) - - def assertAddressNotSelected(self, address, netid): - self.assertNotEquals(address, self.GetSourceIP(netid)) - - def WaitForDad(self, address): - for _ in xrange(20): - if not self.AddressIsTentative(address): - return - time.sleep(0.1) - raise AssertionError("%s did not complete DAD after 2 seconds") - - -class MultiInterfaceSourceAddressSelectionTest(IPv6SourceAddressSelectionTest): - - def setUp(self): - # [0] Make sure DAD, optimistic DAD, and the use_optimistic option - # are all consistently disabled at the outset. - for netid in self.tuns: - ifname = self.GetInterfaceName(netid) - self.SetDAD(ifname, 0) - self.SetOptimisticDAD(ifname, 0) - self.SetUseTempaddrs(ifname, 0) - self.SetUseOptimistic(ifname, 0) - self.SetIPv6Sysctl(ifname, "use_oif_addrs_only", 0) - - # [1] Pick an interface on which to test. - self.test_netid = random.choice(self.tuns.keys()) - self.test_ip = self.MyAddress(6, self.test_netid) - self.test_ifindex = self.ifindices[self.test_netid] - self.test_ifname = self.GetInterfaceName(self.test_netid) - self.test_lladdr = net_test.GetLinkAddress(self.test_ifname, True) - - # [2] Delete the test interface's IPv6 address. - self.iproute.DelAddress(self.test_ip, 64, self.test_ifindex) - self.assertAddressNotPresent(self.test_ip) - - self.assertAddressNotUsable(self.test_ip, self.test_netid) - # Verify that the link-local address is not tentative. - self.assertFalse(self.AddressIsTentative(self.test_lladdr)) - - -class TentativeAddressTest(MultiInterfaceSourceAddressSelectionTest): - - def testRfc6724Behaviour(self): - # [3] Get an IPv6 address back, in DAD start-up. - self.SetDAD(self.test_ifname, 1) # Enable DAD - # Send a RA to start SLAAC and subsequent DAD. - self.SendRA(self.test_netid, 0) - # Get flags and prove tentative-ness. - self.assertAddressHasExpectedAttributes( - self.test_ip, self.test_ifindex, iproute.IFA_F_TENTATIVE) - - # Even though the interface has an IPv6 address, its tentative nature - # prevents it from being selected. - self.assertAddressNotUsable(self.test_ip, self.test_netid) - self.assertAddressNotSelected(self.test_ip, self.test_netid) - - # Busy wait for DAD to complete (should be less than 1 second). - self.WaitForDad(self.test_ip) - - # The test_ip should have completed DAD by now, and should be the - # chosen source address, eligible to bind to, etc. - self.assertAddressUsable(self.test_ip, self.test_netid) - self.assertAddressSelected(self.test_ip, self.test_netid) - - -class OptimisticAddressTest(MultiInterfaceSourceAddressSelectionTest): - - def testRfc6724Behaviour(self): - # [3] Get an IPv6 address back, in optimistic DAD start-up. - self.SetDAD(self.test_ifname, 1) # Enable DAD - self.SetOptimisticDAD(self.test_ifname, 1) - # Send a RA to start SLAAC and subsequent DAD. - self.SendRA(self.test_netid, 0) - # Get flags and prove optimism. - self.assertAddressHasExpectedAttributes( - self.test_ip, self.test_ifindex, iproute.IFA_F_OPTIMISTIC) - - # Optimistic addresses are usable but are not selected. - if net_test.LinuxVersion() >= (3, 18, 0): - # The version checked in to android kernels <= 3.10 requires the - # use_optimistic sysctl to be turned on. - self.assertAddressUsable(self.test_ip, self.test_netid) - self.assertAddressNotSelected(self.test_ip, self.test_netid) - - # Busy wait for DAD to complete (should be less than 1 second). - self.WaitForDad(self.test_ip) - - # The test_ip should have completed DAD by now, and should be the - # chosen source address. - self.assertAddressUsable(self.test_ip, self.test_netid) - self.assertAddressSelected(self.test_ip, self.test_netid) - - -class OptimisticAddressOkayTest(MultiInterfaceSourceAddressSelectionTest): - - def testModifiedRfc6724Behaviour(self): - # [3] Get an IPv6 address back, in optimistic DAD start-up. - self.SetDAD(self.test_ifname, 1) # Enable DAD - self.SetOptimisticDAD(self.test_ifname, 1) - self.SetUseOptimistic(self.test_ifname, 1) - # Send a RA to start SLAAC and subsequent DAD. - self.SendRA(self.test_netid, 0) - # Get flags and prove optimistism. - self.assertAddressHasExpectedAttributes( - self.test_ip, self.test_ifindex, iproute.IFA_F_OPTIMISTIC) - - # The interface has an IPv6 address and, despite its optimistic nature, - # the use_optimistic option allows it to be selected. - self.assertAddressUsable(self.test_ip, self.test_netid) - self.assertAddressSelected(self.test_ip, self.test_netid) - - -class ValidBeforeOptimisticTest(MultiInterfaceSourceAddressSelectionTest): - - def testModifiedRfc6724Behaviour(self): - # [3] Add a valid IPv6 address to this interface and verify it is - # selected as the source address. - preferred_ip = self.IPv6Prefix(self.test_netid) + "cafe" - self.iproute.AddAddress(preferred_ip, 64, self.test_ifindex) - self.assertAddressHasExpectedAttributes( - preferred_ip, self.test_ifindex, iproute.IFA_F_PERMANENT) - self.assertEquals(preferred_ip, self.GetSourceIP(self.test_netid)) - - # [4] Get another IPv6 address, in optimistic DAD start-up. - self.SetDAD(self.test_ifname, 1) # Enable DAD - self.SetOptimisticDAD(self.test_ifname, 1) - self.SetUseOptimistic(self.test_ifname, 1) - # Send a RA to start SLAAC and subsequent DAD. - self.SendRA(self.test_netid, 0) - # Get flags and prove optimism. - self.assertAddressHasExpectedAttributes( - self.test_ip, self.test_ifindex, iproute.IFA_F_OPTIMISTIC) - - # Since the interface has another IPv6 address, the optimistic address - # is not selected--the other, valid address is chosen. - self.assertAddressUsable(self.test_ip, self.test_netid) - self.assertAddressNotSelected(self.test_ip, self.test_netid) - self.assertAddressSelected(preferred_ip, self.test_netid) - - -class DadFailureTest(MultiInterfaceSourceAddressSelectionTest): - - def testDadFailure(self): - # [3] Get an IPv6 address back, in optimistic DAD start-up. - self.SetDAD(self.test_ifname, 1) # Enable DAD - self.SetOptimisticDAD(self.test_ifname, 1) - self.SetUseOptimistic(self.test_ifname, 1) - # Send a RA to start SLAAC and subsequent DAD. - self.SendRA(self.test_netid, 0) - # Prove optimism and usability. - self.assertAddressHasExpectedAttributes( - self.test_ip, self.test_ifindex, iproute.IFA_F_OPTIMISTIC) - self.assertAddressUsable(self.test_ip, self.test_netid) - self.assertAddressSelected(self.test_ip, self.test_netid) - - # Send a NA for the optimistic address, indicating address conflict - # ("DAD defense"). - conflict_macaddr = "02:00:0b:ad:d0:0d" - dad_defense = (scapy.Ether(src=conflict_macaddr, dst="33:33:33:00:00:01") / - scapy.IPv6(src=self.test_ip, dst="ff02::1") / - 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) - - # The address should have failed DAD, and therefore no longer be usable. - self.assertAddressNotUsable(self.test_ip, self.test_netid) - self.assertAddressNotSelected(self.test_ip, self.test_netid) - - # TODO(ek): verify that an RTM_DELADDR issued for the DAD-failed address. - - -class NoNsFromOptimisticTest(MultiInterfaceSourceAddressSelectionTest): - - def testSendToOnlinkDestination(self): - # [3] Get an IPv6 address back, in optimistic DAD start-up. - self.SetDAD(self.test_ifname, 1) # Enable DAD - self.SetOptimisticDAD(self.test_ifname, 1) - self.SetUseOptimistic(self.test_ifname, 1) - # Send a RA to start SLAAC and subsequent DAD. - self.SendRA(self.test_netid, 0) - # Prove optimism and usability. - self.assertAddressHasExpectedAttributes( - self.test_ip, self.test_ifindex, iproute.IFA_F_OPTIMISTIC) - self.assertAddressUsable(self.test_ip, self.test_netid) - self.assertAddressSelected(self.test_ip, self.test_netid) - - # [4] Send to an on-link destination and observe a Neighbor Solicitation - # packet with a source address that is NOT the optimistic address. - # In this setup, the only usable address is the link-local address. - onlink_dest = self.GetRandomDestination(self.IPv6Prefix(self.test_netid)) - self.SendWithSourceAddress(self.test_ip, self.test_netid, onlink_dest) - - if net_test.LinuxVersion() >= (3, 18, 0): - # Older versions will actually choose the optimistic address to - # originate Neighbor Solications (RFC violation). - expected_ns = packets.NS( - self.test_lladdr, - onlink_dest, - self.MyMacAddress(self.test_netid))[1] - self.ExpectPacketOn(self.test_netid, "link-local NS", expected_ns) - - -# TODO(ek): add tests listening for netlink events. - - -class DefaultCandidateSrcAddrsTest(MultiInterfaceSourceAddressSelectionTest): - - def testChoosesNonInterfaceSourceAddress(self): - self.SetIPv6Sysctl(self.test_ifname, "use_oif_addrs_only", 0) - src_ip = self.GetSourceIP(self.test_netid) - self.assertFalse(src_ip in [self.test_ip, self.test_lladdr]) - self.assertTrue(src_ip in - [self.MyAddress(6, netid) - for netid in self.tuns if netid != self.test_netid]) - - -class RestrictedCandidateSrcAddrsTest(MultiInterfaceSourceAddressSelectionTest): - - def testChoosesOnlyInterfaceSourceAddress(self): - self.SetIPv6Sysctl(self.test_ifname, "use_oif_addrs_only", 1) - # self.test_ifname does not have a global IPv6 address, so the only - # candidate is the existing link-local address. - self.assertAddressSelected(self.test_lladdr, self.test_netid) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/net_test/tcp_nuke_addr_test.py b/tests/net_test/tcp_nuke_addr_test.py deleted file mode 100755 index b0ba27dc..00000000 --- a/tests/net_test/tcp_nuke_addr_test.py +++ /dev/null @@ -1,250 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2015 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 contextlib -import errno -import fcntl -import resource -import os -from socket import * # pylint: disable=wildcard-import -import struct -import threading -import time -import unittest - -import csocket -import cstruct -import net_test - -IPV4_LOOPBACK_ADDR = "127.0.0.1" -IPV6_LOOPBACK_ADDR = "::1" -LOOPBACK_DEV = "lo" -LOOPBACK_IFINDEX = 1 - -SIOCKILLADDR = 0x8939 - -DEFAULT_TCP_PORT = 8001 -DEFAULT_BUFFER_SIZE = 20 -DEFAULT_TEST_MESSAGE = "TCP NUKE ADDR TEST" -DEFAULT_TEST_RUNS = 100 -HASH_TEST_RUNS = 4000 -HASH_TEST_NOFILE = 16384 - - -Ifreq = cstruct.Struct("Ifreq", "=16s16s", "name data") -In6Ifreq = cstruct.Struct("In6Ifreq", "=16sIi", "addr prefixlen ifindex") - -@contextlib.contextmanager -def RunInBackground(thread): - """Starts a thread and waits until it joins. - - Args: - thread: A not yet started threading.Thread object. - """ - try: - thread.start() - yield thread - finally: - thread.join() - - -def TcpAcceptAndReceive(listening_sock, buffer_size=DEFAULT_BUFFER_SIZE): - """Accepts a single connection and blocks receiving data from it. - - Args: - listening_socket: A socket in LISTEN state. - buffer_size: Size of buffer where to read a message. - """ - connection, _ = listening_sock.accept() - with contextlib.closing(connection): - _ = connection.recv(buffer_size) - - -def ExchangeMessage(addr_family, ip_addr): - """Creates a listening socket, accepts a connection and sends data to it. - - Args: - addr_family: The address family (e.g. AF_INET6). - ip_addr: The IP address (IPv4 or IPv6 depending on the addr_family). - tcp_port: The TCP port to listen on. - """ - # Bind to a random port and connect to it. - test_addr = (ip_addr, 0) - with contextlib.closing( - socket(addr_family, SOCK_STREAM)) as listening_socket: - listening_socket.bind(test_addr) - test_addr = listening_socket.getsockname() - listening_socket.listen(1) - with RunInBackground(threading.Thread(target=TcpAcceptAndReceive, - args=(listening_socket,))): - with contextlib.closing( - socket(addr_family, SOCK_STREAM)) as client_socket: - client_socket.connect(test_addr) - client_socket.send(DEFAULT_TEST_MESSAGE) - - -def KillAddrIoctl(addr): - """Calls the SIOCKILLADDR ioctl on the provided IP address. - - Args: - addr The IP address to pass to the ioctl. - - Raises: - ValueError: If addr is of an unsupported address family. - """ - family, _, _, _, _ = getaddrinfo(addr, None, AF_UNSPEC, SOCK_DGRAM, 0, - AI_NUMERICHOST)[0] - if family == AF_INET6: - addr = inet_pton(AF_INET6, addr) - ifreq = In6Ifreq((addr, 128, LOOPBACK_IFINDEX)).Pack() - elif family == AF_INET: - addr = inet_pton(AF_INET, addr) - sockaddr = csocket.SockaddrIn((AF_INET, 0, addr)).Pack() - ifreq = Ifreq((LOOPBACK_DEV, sockaddr)).Pack() - else: - raise ValueError('Address family %r not supported.' % family) - datagram_socket = socket(family, SOCK_DGRAM) - fcntl.ioctl(datagram_socket.fileno(), SIOCKILLADDR, ifreq) - datagram_socket.close() - - -class ExceptionalReadThread(threading.Thread): - - def __init__(self, sock): - self.sock = sock - self.exception = None - super(ExceptionalReadThread, self).__init__() - self.daemon = True - - def run(self): - try: - read = self.sock.recv(4096) - except Exception, e: - self.exception = e - -# For convenience. -def CreateIPv4SocketPair(): - return net_test.CreateSocketPair(AF_INET, SOCK_STREAM, IPV4_LOOPBACK_ADDR) - -def CreateIPv6SocketPair(): - return net_test.CreateSocketPair(AF_INET6, SOCK_STREAM, IPV6_LOOPBACK_ADDR) - - -class TcpNukeAddrTest(net_test.NetworkTest): - - def testTimewaitSockets(self): - """Tests that SIOCKILLADDR works as expected. - - Relevant kernel commits: - https://www.codeaurora.org/cgit/quic/la/kernel/msm-3.18/commit/net/ipv4/tcp.c?h=aosp/android-3.10&id=1dcd3a1fa2fe78251cc91700eb1d384ab02e2dd6 - """ - for i in xrange(DEFAULT_TEST_RUNS): - ExchangeMessage(AF_INET6, IPV6_LOOPBACK_ADDR) - KillAddrIoctl(IPV6_LOOPBACK_ADDR) - ExchangeMessage(AF_INET, IPV4_LOOPBACK_ADDR) - KillAddrIoctl(IPV4_LOOPBACK_ADDR) - # Test passes if kernel does not crash. - - def testClosesIPv6Sockets(self): - """Tests that SIOCKILLADDR closes IPv6 sockets and unblocks threads.""" - - threadpairs = [] - - for i in xrange(DEFAULT_TEST_RUNS): - clientsock, acceptedsock = CreateIPv6SocketPair() - clientthread = ExceptionalReadThread(clientsock) - clientthread.start() - serverthread = ExceptionalReadThread(acceptedsock) - serverthread.start() - threadpairs.append((clientthread, serverthread)) - - KillAddrIoctl(IPV6_LOOPBACK_ADDR) - - def CheckThreadException(thread): - thread.join(100) - self.assertFalse(thread.is_alive()) - self.assertIsNotNone(thread.exception) - self.assertTrue(isinstance(thread.exception, IOError)) - self.assertEquals(errno.ETIMEDOUT, thread.exception.errno) - self.assertRaisesErrno(errno.ENOTCONN, thread.sock.getpeername) - self.assertRaisesErrno(errno.EISCONN, thread.sock.connect, - (IPV6_LOOPBACK_ADDR, 53)) - self.assertRaisesErrno(errno.EPIPE, thread.sock.send, "foo") - - for clientthread, serverthread in threadpairs: - CheckThreadException(clientthread) - CheckThreadException(serverthread) - - def assertSocketsClosed(self, socketpair): - for sock in socketpair: - self.assertRaisesErrno(errno.ENOTCONN, sock.getpeername) - - def assertSocketsNotClosed(self, socketpair): - for sock in socketpair: - self.assertTrue(sock.getpeername()) - - def testAddresses(self): - socketpair = CreateIPv4SocketPair() - KillAddrIoctl("::") - self.assertSocketsNotClosed(socketpair) - KillAddrIoctl("::1") - self.assertSocketsNotClosed(socketpair) - KillAddrIoctl("127.0.0.3") - self.assertSocketsNotClosed(socketpair) - KillAddrIoctl("0.0.0.0") - self.assertSocketsNotClosed(socketpair) - KillAddrIoctl("127.0.0.1") - self.assertSocketsClosed(socketpair) - - socketpair = CreateIPv6SocketPair() - KillAddrIoctl("0.0.0.0") - self.assertSocketsNotClosed(socketpair) - KillAddrIoctl("127.0.0.1") - self.assertSocketsNotClosed(socketpair) - KillAddrIoctl("::2") - self.assertSocketsNotClosed(socketpair) - KillAddrIoctl("::") - self.assertSocketsNotClosed(socketpair) - KillAddrIoctl("::1") - self.assertSocketsClosed(socketpair) - - -class TcpNukeAddrHashTest(net_test.NetworkTest): - - def setUp(self): - self.nofile = resource.getrlimit(resource.RLIMIT_NOFILE) - resource.setrlimit(resource.RLIMIT_NOFILE, (HASH_TEST_NOFILE, - HASH_TEST_NOFILE)) - - def tearDown(self): - resource.setrlimit(resource.RLIMIT_NOFILE, self.nofile) - - def testClosesAllSockets(self): - socketpairs = [] - for i in xrange(HASH_TEST_RUNS): - socketpairs.append(CreateIPv4SocketPair()) - socketpairs.append(CreateIPv6SocketPair()) - - KillAddrIoctl(IPV4_LOOPBACK_ADDR) - KillAddrIoctl(IPV6_LOOPBACK_ADDR) - - for socketpair in socketpairs: - for sock in socketpair: - self.assertRaisesErrno(errno.ENOTCONN, sock.getpeername) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/net_test/tcp_test.py b/tests/net_test/tcp_test.py deleted file mode 100644 index 98f1a1ee..00000000 --- a/tests/net_test/tcp_test.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2015 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 time -from socket import * # pylint: disable=wildcard-import - -import net_test -import multinetwork_base -import packets - -# TCP states. See include/net/tcp_states.h. -TCP_ESTABLISHED = 1 -TCP_SYN_SENT = 2 -TCP_SYN_RECV = 3 -TCP_FIN_WAIT1 = 4 -TCP_FIN_WAIT2 = 5 -TCP_TIME_WAIT = 6 -TCP_CLOSE = 7 -TCP_CLOSE_WAIT = 8 -TCP_LAST_ACK = 9 -TCP_LISTEN = 10 -TCP_CLOSING = 11 -TCP_NEW_SYN_RECV = 12 - -TCP_NOT_YET_ACCEPTED = -1 - - -class TcpBaseTest(multinetwork_base.MultiNetworkBaseTest): - - def tearDown(self): - if hasattr(self, "s"): - self.s.close() - super(TcpBaseTest, self).tearDown() - - def OpenListenSocket(self, version, netid): - self.port = packets.RandomPort() - family = {4: AF_INET, 5: AF_INET6, 6: AF_INET6}[version] - address = {4: "0.0.0.0", 5: "::", 6: "::"}[version] - s = net_test.Socket(family, SOCK_STREAM, IPPROTO_TCP) - s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) - s.bind((address, self.port)) - # We haven't configured inbound iptables marking, so bind explicitly. - self.SelectInterface(s, netid, "mark") - s.listen(100) - return s - - def _ReceiveAndExpectResponse(self, netid, packet, reply, msg): - pkt = super(TcpBaseTest, self)._ReceiveAndExpectResponse(netid, packet, - reply, msg) - self.last_packet = pkt - return pkt - - def ReceivePacketOn(self, netid, packet): - super(TcpBaseTest, self).ReceivePacketOn(netid, packet) - self.last_packet = packet - - def RstPacket(self): - return packets.RST(self.version, self.myaddr, self.remoteaddr, - self.last_packet) - - def FinPacket(self): - return packets.FIN(self.version, self.myaddr, self.remoteaddr, - self.last_packet) - - - def IncomingConnection(self, version, end_state, netid): - self.s = self.OpenListenSocket(version, netid) - self.end_state = end_state - - remoteaddr = self.remoteaddr = self.GetRemoteAddress(version) - myaddr = self.myaddr = self.MyAddress(version, netid) - - if version == 5: version = 4 - self.version = version - - if end_state == TCP_LISTEN: - return - - desc, syn = packets.SYN(self.port, version, remoteaddr, myaddr) - synack_desc, synack = packets.SYNACK(version, myaddr, remoteaddr, syn) - msg = "Received %s, expected to see reply %s" % (desc, synack_desc) - reply = self._ReceiveAndExpectResponse(netid, syn, synack, msg) - if end_state == TCP_SYN_RECV: - return - - establishing_ack = packets.ACK(version, remoteaddr, myaddr, reply)[1] - self.ReceivePacketOn(netid, establishing_ack) - - if end_state == TCP_NOT_YET_ACCEPTED: - return - - self.accepted, _ = self.s.accept() - net_test.DisableFinWait(self.accepted) - - if end_state == TCP_ESTABLISHED: - return - - desc, data = packets.ACK(version, myaddr, remoteaddr, establishing_ack, - payload=net_test.UDP_PAYLOAD) - self.accepted.send(net_test.UDP_PAYLOAD) - self.ExpectPacketOn(netid, msg + ": expecting %s" % desc, data) - - desc, fin = packets.FIN(version, remoteaddr, myaddr, data) - fin = packets._GetIpLayer(version)(str(fin)) - ack_desc, ack = packets.ACK(version, myaddr, remoteaddr, fin) - msg = "Received %s, expected to see reply %s" % (desc, ack_desc) - - # TODO: Why can't we use this? - # self._ReceiveAndExpectResponse(netid, fin, ack, msg) - self.ReceivePacketOn(netid, fin) - time.sleep(0.1) - self.ExpectPacketOn(netid, msg + ": expecting %s" % ack_desc, ack) - if end_state == TCP_CLOSE_WAIT: - return - - raise ValueError("Invalid TCP state %d specified" % end_state)