1#!/usr/bin/python 2# 3# Copyright 2015 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17import random 18 19from scapy import all as scapy 20from socket import * 21 22import net_test 23 24TCP_FIN = 1 25TCP_SYN = 2 26TCP_RST = 4 27TCP_PSH = 8 28TCP_ACK = 16 29 30TCP_WINDOW = 14400 31 32PING_IDENT = 0xff19 33PING_PAYLOAD = "foobarbaz" 34PING_SEQ = 3 35PING_TOS = 0x83 36 37# For brevity. 38UDP_PAYLOAD = net_test.UDP_PAYLOAD 39 40 41def _RandomPort(): 42 return random.randint(1025, 65535) 43 44def _GetIpLayer(version): 45 return {4: scapy.IP, 6: scapy.IPv6}[version] 46 47def _SetPacketTos(packet, tos): 48 if isinstance(packet, scapy.IPv6): 49 packet.tc = tos 50 elif isinstance(packet, scapy.IP): 51 packet.tos = tos 52 else: 53 raise ValueError("Can't find ToS Field") 54 55def UDP(version, srcaddr, dstaddr, sport=0): 56 ip = _GetIpLayer(version) 57 # Can't just use "if sport" because None has meaning (it means unspecified). 58 if sport == 0: 59 sport = _RandomPort() 60 return ("UDPv%d packet" % version, 61 ip(src=srcaddr, dst=dstaddr) / 62 scapy.UDP(sport=sport, dport=53) / UDP_PAYLOAD) 63 64def UDPWithOptions(version, srcaddr, dstaddr, sport=0): 65 if version == 4: 66 packet = (scapy.IP(src=srcaddr, dst=dstaddr, ttl=39, tos=0x83) / 67 scapy.UDP(sport=sport, dport=53) / 68 UDP_PAYLOAD) 69 else: 70 packet = (scapy.IPv6(src=srcaddr, dst=dstaddr, 71 fl=0xbeef, hlim=39, tc=0x83) / 72 scapy.UDP(sport=sport, dport=53) / 73 UDP_PAYLOAD) 74 return ("UDPv%d packet with options" % version, packet) 75 76def SYN(dport, version, srcaddr, dstaddr, sport=0, seq=-1): 77 ip = _GetIpLayer(version) 78 if sport == 0: 79 sport = _RandomPort() 80 if seq == -1: # Can't use None because it means unspecified. 81 seq = random.getrandbits(32) 82 return ("TCP SYN", 83 ip(src=srcaddr, dst=dstaddr) / 84 scapy.TCP(sport=sport, dport=dport, 85 seq=seq, ack=0, 86 flags=TCP_SYN, window=TCP_WINDOW)) 87 88def RST(version, srcaddr, dstaddr, packet): 89 ip = _GetIpLayer(version) 90 original = packet.getlayer("TCP") 91 was_syn_or_fin = (original.flags & (TCP_SYN | TCP_FIN)) != 0 92 return ("TCP RST", 93 ip(src=srcaddr, dst=dstaddr) / 94 scapy.TCP(sport=original.dport, dport=original.sport, 95 ack=original.seq + was_syn_or_fin, seq=None, 96 flags=TCP_RST | TCP_ACK, window=TCP_WINDOW)) 97 98def SYNACK(version, srcaddr, dstaddr, packet): 99 ip = _GetIpLayer(version) 100 original = packet.getlayer("TCP") 101 return ("TCP SYN+ACK", 102 ip(src=srcaddr, dst=dstaddr) / 103 scapy.TCP(sport=original.dport, dport=original.sport, 104 ack=original.seq + 1, seq=None, 105 flags=TCP_SYN | TCP_ACK, window=None)) 106 107def ACK(version, srcaddr, dstaddr, packet, payload=""): 108 ip = _GetIpLayer(version) 109 original = packet.getlayer("TCP") 110 was_syn_or_fin = (original.flags & (TCP_SYN | TCP_FIN)) != 0 111 ack_delta = was_syn_or_fin + len(original.payload) 112 desc = "TCP data" if payload else "TCP ACK" 113 flags = TCP_ACK | TCP_PSH if payload else TCP_ACK 114 return (desc, 115 ip(src=srcaddr, dst=dstaddr) / 116 scapy.TCP(sport=original.dport, dport=original.sport, 117 ack=original.seq + ack_delta, seq=original.ack, 118 flags=flags, window=TCP_WINDOW) / 119 payload) 120 121def FIN(version, srcaddr, dstaddr, packet): 122 ip = _GetIpLayer(version) 123 original = packet.getlayer("TCP") 124 was_syn_or_fin = (original.flags & (TCP_SYN | TCP_FIN)) != 0 125 ack_delta = was_syn_or_fin + len(original.payload) 126 return ("TCP FIN", 127 ip(src=srcaddr, dst=dstaddr) / 128 scapy.TCP(sport=original.dport, dport=original.sport, 129 ack=original.seq + ack_delta, seq=original.ack, 130 flags=TCP_ACK | TCP_FIN, window=TCP_WINDOW)) 131 132def GRE(version, srcaddr, dstaddr, proto, packet): 133 if version == 4: 134 ip = scapy.IP(src=srcaddr, dst=dstaddr, proto=net_test.IPPROTO_GRE) 135 else: 136 ip = scapy.IPv6(src=srcaddr, dst=dstaddr, nh=net_test.IPPROTO_GRE) 137 packet = ip / scapy.GRE(proto=proto) / packet 138 return ("GRE packet", packet) 139 140def ICMPPortUnreachable(version, srcaddr, dstaddr, packet): 141 if version == 4: 142 # Linux hardcodes the ToS on ICMP errors to 0xc0 or greater because of 143 # RFC 1812 4.3.2.5 (!). 144 return ("ICMPv4 port unreachable", 145 scapy.IP(src=srcaddr, dst=dstaddr, proto=1, tos=0xc0) / 146 scapy.ICMPerror(type=3, code=3) / packet) 147 else: 148 return ("ICMPv6 port unreachable", 149 scapy.IPv6(src=srcaddr, dst=dstaddr) / 150 scapy.ICMPv6DestUnreach(code=4) / packet) 151 152def ICMPPacketTooBig(version, srcaddr, dstaddr, packet): 153 if version == 4: 154 return ("ICMPv4 fragmentation needed", 155 scapy.IP(src=srcaddr, dst=dstaddr, proto=1) / 156 scapy.ICMPerror(type=3, code=4, unused=1280) / str(packet)[:64]) 157 else: 158 udp = packet.getlayer("UDP") 159 udp.payload = str(udp.payload)[:1280-40-8] 160 return ("ICMPv6 Packet Too Big", 161 scapy.IPv6(src=srcaddr, dst=dstaddr) / 162 scapy.ICMPv6PacketTooBig() / str(packet)[:1232]) 163 164def ICMPEcho(version, srcaddr, dstaddr): 165 ip = _GetIpLayer(version) 166 icmp = {4: scapy.ICMP, 6: scapy.ICMPv6EchoRequest}[version] 167 packet = (ip(src=srcaddr, dst=dstaddr) / 168 icmp(id=PING_IDENT, seq=PING_SEQ) / PING_PAYLOAD) 169 _SetPacketTos(packet, PING_TOS) 170 return ("ICMPv%d echo" % version, packet) 171 172def ICMPReply(version, srcaddr, dstaddr, packet): 173 ip = _GetIpLayer(version) 174 # Scapy doesn't provide an ICMP echo reply constructor. 175 icmpv4_reply = lambda **kwargs: scapy.ICMP(type=0, **kwargs) 176 icmp = {4: icmpv4_reply, 6: scapy.ICMPv6EchoReply}[version] 177 packet = (ip(src=srcaddr, dst=dstaddr) / 178 icmp(id=PING_IDENT, seq=PING_SEQ) / PING_PAYLOAD) 179 # IPv6 only started copying the tclass to echo replies in 3.14. 180 if version == 4 or net_test.LINUX_VERSION >= (3, 14): 181 _SetPacketTos(packet, PING_TOS) 182 return ("ICMPv%d echo reply" % version, packet) 183 184def NS(srcaddr, tgtaddr, srcmac): 185 solicited = inet_pton(AF_INET6, tgtaddr) 186 last3bytes = tuple([ord(b) for b in solicited[-3:]]) 187 solicited = "ff02::1:ff%02x:%02x%02x" % last3bytes 188 packet = (scapy.IPv6(src=srcaddr, dst=solicited) / 189 scapy.ICMPv6ND_NS(tgt=tgtaddr) / 190 scapy.ICMPv6NDOptSrcLLAddr(lladdr=srcmac)) 191 return ("ICMPv6 NS", packet) 192 193def NA(srcaddr, dstaddr, srcmac): 194 packet = (scapy.IPv6(src=srcaddr, dst=dstaddr) / 195 scapy.ICMPv6ND_NA(tgt=srcaddr, R=0, S=1, O=1) / 196 scapy.ICMPv6NDOptDstLLAddr(lladdr=srcmac)) 197 return ("ICMPv6 NA", packet) 198 199