1*2f2c4c7aSAndroid Build Coastguard Worker# Copyright 2017 The Android Open Source Project 2*2f2c4c7aSAndroid Build Coastguard Worker# 3*2f2c4c7aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*2f2c4c7aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*2f2c4c7aSAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*2f2c4c7aSAndroid Build Coastguard Worker# 7*2f2c4c7aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*2f2c4c7aSAndroid Build Coastguard Worker# 9*2f2c4c7aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*2f2c4c7aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*2f2c4c7aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*2f2c4c7aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*2f2c4c7aSAndroid Build Coastguard Worker# limitations under the License. 14*2f2c4c7aSAndroid Build Coastguard Worker"""A utility for "twisting" packets on a tun/tap interface. 15*2f2c4c7aSAndroid Build Coastguard Worker 16*2f2c4c7aSAndroid Build Coastguard WorkerTunTwister and TapTwister echo packets on a tun/tap while swapping the source 17*2f2c4c7aSAndroid Build Coastguard Workerand destination at the ethernet and IP layers. This allows sockets to 18*2f2c4c7aSAndroid Build Coastguard Workereffectively loop back packets through the full networking stack, avoiding any 19*2f2c4c7aSAndroid Build Coastguard Workershortcuts the kernel may take for actual IP loopback. Additionally, users can 20*2f2c4c7aSAndroid Build Coastguard Workerinspect each packet to assert testing invariants. 21*2f2c4c7aSAndroid Build Coastguard Worker""" 22*2f2c4c7aSAndroid Build Coastguard Worker 23*2f2c4c7aSAndroid Build Coastguard Workerimport os 24*2f2c4c7aSAndroid Build Coastguard Workerimport select 25*2f2c4c7aSAndroid Build Coastguard Workerimport threading 26*2f2c4c7aSAndroid Build Coastguard Workerfrom scapy import all as scapy 27*2f2c4c7aSAndroid Build Coastguard Worker 28*2f2c4c7aSAndroid Build Coastguard Worker 29*2f2c4c7aSAndroid Build Coastguard Workerclass TunTwister(object): 30*2f2c4c7aSAndroid Build Coastguard Worker """TunTwister transports traffic travelling twixt two terminals. 31*2f2c4c7aSAndroid Build Coastguard Worker 32*2f2c4c7aSAndroid Build Coastguard Worker TunTwister is a context manager that will read packets from a tun file 33*2f2c4c7aSAndroid Build Coastguard Worker descriptor, swap the source and dest of the IP header, and write them back. 34*2f2c4c7aSAndroid Build Coastguard Worker To use this class, tests also need to set up routing so that packets will be 35*2f2c4c7aSAndroid Build Coastguard Worker routed to the tun interface. 36*2f2c4c7aSAndroid Build Coastguard Worker 37*2f2c4c7aSAndroid Build Coastguard Worker Two sockets can communicate with each other through a TunTwister as if they 38*2f2c4c7aSAndroid Build Coastguard Worker were each connecting to a remote endpoint. Both sockets will have the 39*2f2c4c7aSAndroid Build Coastguard Worker perspective that the address of the other is a remote address. 40*2f2c4c7aSAndroid Build Coastguard Worker 41*2f2c4c7aSAndroid Build Coastguard Worker Packet inspection can be done with a validator function. This can be any 42*2f2c4c7aSAndroid Build Coastguard Worker function that takes a scapy packet object as its only argument. Exceptions 43*2f2c4c7aSAndroid Build Coastguard Worker raised by your validator function will be re-raised on the main thread to fail 44*2f2c4c7aSAndroid Build Coastguard Worker your tests. 45*2f2c4c7aSAndroid Build Coastguard Worker 46*2f2c4c7aSAndroid Build Coastguard Worker NOTE: Exceptions raised by a validator function will supercede exceptions 47*2f2c4c7aSAndroid Build Coastguard Worker raised in the context. 48*2f2c4c7aSAndroid Build Coastguard Worker 49*2f2c4c7aSAndroid Build Coastguard Worker EXAMPLE: 50*2f2c4c7aSAndroid Build Coastguard Worker def testFeatureFoo(self): 51*2f2c4c7aSAndroid Build Coastguard Worker my_tun = MakeTunInterface() 52*2f2c4c7aSAndroid Build Coastguard Worker # Set up routing so packets go to my_tun. 53*2f2c4c7aSAndroid Build Coastguard Worker 54*2f2c4c7aSAndroid Build Coastguard Worker def ValidatePortNumber(packet): 55*2f2c4c7aSAndroid Build Coastguard Worker self.assertEqual(8080, packet.getlayer(scapy.UDP).sport) 56*2f2c4c7aSAndroid Build Coastguard Worker self.assertEqual(8080, packet.getlayer(scapy.UDP).dport) 57*2f2c4c7aSAndroid Build Coastguard Worker 58*2f2c4c7aSAndroid Build Coastguard Worker with TunTwister(tun_fd=my_tun, validator=ValidatePortNumber): 59*2f2c4c7aSAndroid Build Coastguard Worker sock = socket(AF_INET, SOCK_DGRAM, 0) 60*2f2c4c7aSAndroid Build Coastguard Worker sock.bind(("0.0.0.0", 8080)) 61*2f2c4c7aSAndroid Build Coastguard Worker sock.settimeout(1.0) 62*2f2c4c7aSAndroid Build Coastguard Worker sock.sendto("hello", ("1.2.3.4", 8080)) 63*2f2c4c7aSAndroid Build Coastguard Worker data, addr = sock.recvfrom(1024) 64*2f2c4c7aSAndroid Build Coastguard Worker self.assertEqual(b"hello", data) 65*2f2c4c7aSAndroid Build Coastguard Worker self.assertEqual(("1.2.3.4", 8080), addr) 66*2f2c4c7aSAndroid Build Coastguard Worker """ 67*2f2c4c7aSAndroid Build Coastguard Worker 68*2f2c4c7aSAndroid Build Coastguard Worker # Hopefully larger than any packet. 69*2f2c4c7aSAndroid Build Coastguard Worker _READ_BUF_SIZE = 2048 70*2f2c4c7aSAndroid Build Coastguard Worker _POLL_TIMEOUT_SEC = 2.0 71*2f2c4c7aSAndroid Build Coastguard Worker _POLL_FAST_TIMEOUT_MS = 100 72*2f2c4c7aSAndroid Build Coastguard Worker 73*2f2c4c7aSAndroid Build Coastguard Worker def __init__(self, fd=None, validator=None): 74*2f2c4c7aSAndroid Build Coastguard Worker """Construct a TunTwister. 75*2f2c4c7aSAndroid Build Coastguard Worker 76*2f2c4c7aSAndroid Build Coastguard Worker The TunTwister will listen on the given TUN fd. 77*2f2c4c7aSAndroid Build Coastguard Worker The validator is called for each packet *before* twisting. The packet is 78*2f2c4c7aSAndroid Build Coastguard Worker passed in as a scapy packet object, and is the only argument passed to the 79*2f2c4c7aSAndroid Build Coastguard Worker validator. 80*2f2c4c7aSAndroid Build Coastguard Worker 81*2f2c4c7aSAndroid Build Coastguard Worker Args: 82*2f2c4c7aSAndroid Build Coastguard Worker fd: File descriptor of a TUN interface. 83*2f2c4c7aSAndroid Build Coastguard Worker validator: Function taking one scapy packet object argument. 84*2f2c4c7aSAndroid Build Coastguard Worker """ 85*2f2c4c7aSAndroid Build Coastguard Worker self._fd = fd 86*2f2c4c7aSAndroid Build Coastguard Worker # Use a pipe to signal the thread to exit. 87*2f2c4c7aSAndroid Build Coastguard Worker self._signal_read, self._signal_write = os.pipe() 88*2f2c4c7aSAndroid Build Coastguard Worker self._thread = threading.Thread(target=self._RunLoop, name="TunTwister") 89*2f2c4c7aSAndroid Build Coastguard Worker self._validator = validator 90*2f2c4c7aSAndroid Build Coastguard Worker self._error = None 91*2f2c4c7aSAndroid Build Coastguard Worker 92*2f2c4c7aSAndroid Build Coastguard Worker def __enter__(self): 93*2f2c4c7aSAndroid Build Coastguard Worker self._thread.start() 94*2f2c4c7aSAndroid Build Coastguard Worker 95*2f2c4c7aSAndroid Build Coastguard Worker def __exit__(self, *args): 96*2f2c4c7aSAndroid Build Coastguard Worker # Signal thread exit. 97*2f2c4c7aSAndroid Build Coastguard Worker os.write(self._signal_write, b"bye") 98*2f2c4c7aSAndroid Build Coastguard Worker os.close(self._signal_write) 99*2f2c4c7aSAndroid Build Coastguard Worker self._thread.join(TunTwister._POLL_TIMEOUT_SEC) 100*2f2c4c7aSAndroid Build Coastguard Worker os.close(self._signal_read) 101*2f2c4c7aSAndroid Build Coastguard Worker if self._thread.is_alive(): 102*2f2c4c7aSAndroid Build Coastguard Worker raise RuntimeError("Timed out waiting for thread exit") 103*2f2c4c7aSAndroid Build Coastguard Worker # Re-raise any error thrown from our thread. 104*2f2c4c7aSAndroid Build Coastguard Worker if isinstance(self._error, Exception): 105*2f2c4c7aSAndroid Build Coastguard Worker raise self._error # pylint: disable=raising-bad-type 106*2f2c4c7aSAndroid Build Coastguard Worker 107*2f2c4c7aSAndroid Build Coastguard Worker def _RunLoop(self): 108*2f2c4c7aSAndroid Build Coastguard Worker """Twist packets until exit signal.""" 109*2f2c4c7aSAndroid Build Coastguard Worker try: 110*2f2c4c7aSAndroid Build Coastguard Worker while True: 111*2f2c4c7aSAndroid Build Coastguard Worker read_fds, _, _ = select.select([self._fd, self._signal_read], [], [], 112*2f2c4c7aSAndroid Build Coastguard Worker TunTwister._POLL_TIMEOUT_SEC) 113*2f2c4c7aSAndroid Build Coastguard Worker if self._signal_read in read_fds: 114*2f2c4c7aSAndroid Build Coastguard Worker self._Flush() 115*2f2c4c7aSAndroid Build Coastguard Worker return 116*2f2c4c7aSAndroid Build Coastguard Worker if self._fd in read_fds: 117*2f2c4c7aSAndroid Build Coastguard Worker self._ProcessPacket() 118*2f2c4c7aSAndroid Build Coastguard Worker except Exception as e: # pylint: disable=broad-except 119*2f2c4c7aSAndroid Build Coastguard Worker self._error = e 120*2f2c4c7aSAndroid Build Coastguard Worker 121*2f2c4c7aSAndroid Build Coastguard Worker def _Flush(self): 122*2f2c4c7aSAndroid Build Coastguard Worker """Ensure no packets are left in the buffer.""" 123*2f2c4c7aSAndroid Build Coastguard Worker p = select.poll() 124*2f2c4c7aSAndroid Build Coastguard Worker p.register(self._fd, select.POLLIN) 125*2f2c4c7aSAndroid Build Coastguard Worker while p.poll(TunTwister._POLL_FAST_TIMEOUT_MS): 126*2f2c4c7aSAndroid Build Coastguard Worker self._ProcessPacket() 127*2f2c4c7aSAndroid Build Coastguard Worker 128*2f2c4c7aSAndroid Build Coastguard Worker def _ProcessPacket(self): 129*2f2c4c7aSAndroid Build Coastguard Worker """Read, twist, and write one packet on the tun/tap.""" 130*2f2c4c7aSAndroid Build Coastguard Worker # TODO: Handle EAGAIN "errors". 131*2f2c4c7aSAndroid Build Coastguard Worker bytes_in = os.read(self._fd, TunTwister._READ_BUF_SIZE) 132*2f2c4c7aSAndroid Build Coastguard Worker packet = self.DecodePacket(bytes_in) 133*2f2c4c7aSAndroid Build Coastguard Worker # the user may wish to filter certain packets, such as 134*2f2c4c7aSAndroid Build Coastguard Worker # Ethernet multicast packets 135*2f2c4c7aSAndroid Build Coastguard Worker if self._DropPacket(packet): 136*2f2c4c7aSAndroid Build Coastguard Worker return 137*2f2c4c7aSAndroid Build Coastguard Worker 138*2f2c4c7aSAndroid Build Coastguard Worker if self._validator: 139*2f2c4c7aSAndroid Build Coastguard Worker self._validator(packet) 140*2f2c4c7aSAndroid Build Coastguard Worker packet = self.TwistPacket(packet) 141*2f2c4c7aSAndroid Build Coastguard Worker os.write(self._fd, packet.build()) 142*2f2c4c7aSAndroid Build Coastguard Worker 143*2f2c4c7aSAndroid Build Coastguard Worker def _DropPacket(self, packet): 144*2f2c4c7aSAndroid Build Coastguard Worker """Determine whether to drop the provided packet by inspection""" 145*2f2c4c7aSAndroid Build Coastguard Worker return False 146*2f2c4c7aSAndroid Build Coastguard Worker 147*2f2c4c7aSAndroid Build Coastguard Worker @classmethod 148*2f2c4c7aSAndroid Build Coastguard Worker def DecodePacket(cls, bytes_in): 149*2f2c4c7aSAndroid Build Coastguard Worker """Decode a byte array into a scapy object.""" 150*2f2c4c7aSAndroid Build Coastguard Worker return cls._DecodeIpPacket(bytes_in) 151*2f2c4c7aSAndroid Build Coastguard Worker 152*2f2c4c7aSAndroid Build Coastguard Worker @classmethod 153*2f2c4c7aSAndroid Build Coastguard Worker def TwistPacket(cls, packet): 154*2f2c4c7aSAndroid Build Coastguard Worker """Swap the src and dst in the IP header.""" 155*2f2c4c7aSAndroid Build Coastguard Worker ip_type = type(packet) 156*2f2c4c7aSAndroid Build Coastguard Worker if ip_type not in (scapy.IP, scapy.IPv6): 157*2f2c4c7aSAndroid Build Coastguard Worker raise TypeError("Expected an IPv4 or IPv6 packet.") 158*2f2c4c7aSAndroid Build Coastguard Worker packet.src, packet.dst = packet.dst, packet.src 159*2f2c4c7aSAndroid Build Coastguard Worker packet = ip_type(packet.build()) # Fix the IP checksum. 160*2f2c4c7aSAndroid Build Coastguard Worker return packet 161*2f2c4c7aSAndroid Build Coastguard Worker 162*2f2c4c7aSAndroid Build Coastguard Worker @staticmethod 163*2f2c4c7aSAndroid Build Coastguard Worker def _DecodeIpPacket(packet_bytes): 164*2f2c4c7aSAndroid Build Coastguard Worker """Decode 'packet_bytes' as an IPv4 or IPv6 scapy object.""" 165*2f2c4c7aSAndroid Build Coastguard Worker ip_ver = (ord(packet_bytes[0]) & 0xF0) >> 4 166*2f2c4c7aSAndroid Build Coastguard Worker if ip_ver == 4: 167*2f2c4c7aSAndroid Build Coastguard Worker return scapy.IP(packet_bytes) 168*2f2c4c7aSAndroid Build Coastguard Worker elif ip_ver == 6: 169*2f2c4c7aSAndroid Build Coastguard Worker return scapy.IPv6(packet_bytes) 170*2f2c4c7aSAndroid Build Coastguard Worker else: 171*2f2c4c7aSAndroid Build Coastguard Worker raise ValueError("packet_bytes is not a valid IPv4 or IPv6 packet") 172*2f2c4c7aSAndroid Build Coastguard Worker 173*2f2c4c7aSAndroid Build Coastguard Worker 174*2f2c4c7aSAndroid Build Coastguard Workerclass TapTwister(TunTwister): 175*2f2c4c7aSAndroid Build Coastguard Worker """Test util for tap interfaces. 176*2f2c4c7aSAndroid Build Coastguard Worker 177*2f2c4c7aSAndroid Build Coastguard Worker TapTwister works just like TunTwister, except it operates on tap interfaces 178*2f2c4c7aSAndroid Build Coastguard Worker instead of tuns. Ethernet headers will have their sources and destinations 179*2f2c4c7aSAndroid Build Coastguard Worker swapped in addition to IP headers. 180*2f2c4c7aSAndroid Build Coastguard Worker """ 181*2f2c4c7aSAndroid Build Coastguard Worker 182*2f2c4c7aSAndroid Build Coastguard Worker @staticmethod 183*2f2c4c7aSAndroid Build Coastguard Worker def _IsMulticastPacket(eth_pkt): 184*2f2c4c7aSAndroid Build Coastguard Worker return int(eth_pkt.dst.split(":")[0], 16) & 0x1 185*2f2c4c7aSAndroid Build Coastguard Worker 186*2f2c4c7aSAndroid Build Coastguard Worker def __init__(self, fd=None, validator=None, drop_multicast=True): 187*2f2c4c7aSAndroid Build Coastguard Worker """Construct a TapTwister. 188*2f2c4c7aSAndroid Build Coastguard Worker 189*2f2c4c7aSAndroid Build Coastguard Worker TapTwister works just like TunTwister, but handles both ethernet and IP 190*2f2c4c7aSAndroid Build Coastguard Worker headers. 191*2f2c4c7aSAndroid Build Coastguard Worker 192*2f2c4c7aSAndroid Build Coastguard Worker Args: 193*2f2c4c7aSAndroid Build Coastguard Worker fd: File descriptor of a TAP interface. 194*2f2c4c7aSAndroid Build Coastguard Worker validator: Function taking one scapy packet object argument. 195*2f2c4c7aSAndroid Build Coastguard Worker drop_multicast: Drop Ethernet multicast packets 196*2f2c4c7aSAndroid Build Coastguard Worker """ 197*2f2c4c7aSAndroid Build Coastguard Worker super(TapTwister, self).__init__(fd=fd, validator=validator) 198*2f2c4c7aSAndroid Build Coastguard Worker self._drop_multicast = drop_multicast 199*2f2c4c7aSAndroid Build Coastguard Worker 200*2f2c4c7aSAndroid Build Coastguard Worker def _DropPacket(self, packet): 201*2f2c4c7aSAndroid Build Coastguard Worker return self._drop_multicast and self._IsMulticastPacket(packet) 202*2f2c4c7aSAndroid Build Coastguard Worker 203*2f2c4c7aSAndroid Build Coastguard Worker @classmethod 204*2f2c4c7aSAndroid Build Coastguard Worker def DecodePacket(cls, bytes_in): 205*2f2c4c7aSAndroid Build Coastguard Worker return scapy.Ether(bytes_in) 206*2f2c4c7aSAndroid Build Coastguard Worker 207*2f2c4c7aSAndroid Build Coastguard Worker @classmethod 208*2f2c4c7aSAndroid Build Coastguard Worker def TwistPacket(cls, packet): 209*2f2c4c7aSAndroid Build Coastguard Worker """Swap the src and dst in the ethernet and IP headers.""" 210*2f2c4c7aSAndroid Build Coastguard Worker packet.src, packet.dst = packet.dst, packet.src 211*2f2c4c7aSAndroid Build Coastguard Worker ip_layer = packet.payload 212*2f2c4c7aSAndroid Build Coastguard Worker twisted_ip_layer = super(TapTwister, cls).TwistPacket(ip_layer) 213*2f2c4c7aSAndroid Build Coastguard Worker packet.payload = twisted_ip_layer 214*2f2c4c7aSAndroid Build Coastguard Worker return packet 215