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 32PTB_MTU = 1280 33 34PING_IDENT = 0xff19 35PING_PAYLOAD = "foobarbaz" 36PING_SEQ = 3 37PING_TOS = 0x83 38 39# For brevity. 40UDP_PAYLOAD = net_test.UDP_PAYLOAD 41 42 43def _RandomPort(): 44 return random.randint(1025, 65535) 45 46def _GetIpLayer(version): 47 return {4: scapy.IP, 5: scapy.IP, 6: scapy.IPv6}[version] 48 49def _SetPacketTos(packet, tos): 50 if isinstance(packet, scapy.IPv6): 51 packet.tc = tos 52 elif isinstance(packet, scapy.IP): 53 packet.tos = tos 54 else: 55 raise ValueError("Can't find ToS Field") 56 57def UDP(version, srcaddr, dstaddr, sport=0): 58 ip = _GetIpLayer(version) 59 # Can't just use "if sport" because None has meaning (it means unspecified). 60 if sport == 0: 61 sport = _RandomPort() 62 return ("UDPv%d packet" % version, 63 ip(src=srcaddr, dst=dstaddr) / 64 scapy.UDP(sport=sport, dport=53) / UDP_PAYLOAD) 65 66def UDPWithOptions(version, srcaddr, dstaddr, sport=0, lifetime=39): 67 if version == 4: 68 packet = (scapy.IP(src=srcaddr, dst=dstaddr, ttl=lifetime, tos=0x83) / 69 scapy.UDP(sport=sport, dport=53) / 70 UDP_PAYLOAD) 71 else: 72 packet = (scapy.IPv6(src=srcaddr, dst=dstaddr, 73 fl=0xbeef, hlim=lifetime, tc=0x83) / 74 scapy.UDP(sport=sport, dport=53) / 75 UDP_PAYLOAD) 76 return ("UDPv%d packet with options" % version, packet) 77 78def SYN(dport, version, srcaddr, dstaddr, sport=0, seq=-1): 79 ip = _GetIpLayer(version) 80 if sport == 0: 81 sport = _RandomPort() 82 if seq == -1: # Can't use None because it means unspecified. 83 seq = random.getrandbits(32) 84 return ("TCP SYN", 85 ip(src=srcaddr, dst=dstaddr) / 86 scapy.TCP(sport=sport, dport=dport, 87 seq=seq, ack=0, 88 flags=TCP_SYN, window=TCP_WINDOW)) 89 90def RST(version, srcaddr, dstaddr, packet): 91 ip = _GetIpLayer(version) 92 original = packet.getlayer("TCP") 93 was_syn_or_fin = (original.flags & (TCP_SYN | TCP_FIN)) != 0 94 return ("TCP RST", 95 ip(src=srcaddr, dst=dstaddr) / 96 scapy.TCP(sport=original.dport, dport=original.sport, 97 ack=original.seq + was_syn_or_fin, 98 seq=original.ack, 99 flags=TCP_RST | TCP_ACK, window=TCP_WINDOW)) 100 101def SYNACK(version, srcaddr, dstaddr, packet): 102 ip = _GetIpLayer(version) 103 original = packet.getlayer("TCP") 104 return ("TCP SYN+ACK", 105 ip(src=srcaddr, dst=dstaddr) / 106 scapy.TCP(sport=original.dport, dport=original.sport, 107 ack=original.seq + 1, seq=None, 108 flags=TCP_SYN | TCP_ACK, window=None)) 109 110def ACK(version, srcaddr, dstaddr, packet, payload=""): 111 ip = _GetIpLayer(version) 112 original = packet.getlayer("TCP") 113 was_syn_or_fin = (original.flags & (TCP_SYN | TCP_FIN)) != 0 114 ack_delta = was_syn_or_fin + len(original.payload) 115 desc = "TCP data" if payload else "TCP ACK" 116 flags = TCP_ACK | TCP_PSH if payload else TCP_ACK 117 return (desc, 118 ip(src=srcaddr, dst=dstaddr) / 119 scapy.TCP(sport=original.dport, dport=original.sport, 120 ack=original.seq + ack_delta, seq=original.ack, 121 flags=flags, window=TCP_WINDOW) / 122 payload) 123 124def FIN(version, srcaddr, dstaddr, packet): 125 ip = _GetIpLayer(version) 126 original = packet.getlayer("TCP") 127 was_syn_or_fin = (original.flags & (TCP_SYN | TCP_FIN)) != 0 128 ack_delta = was_syn_or_fin + len(original.payload) 129 return ("TCP FIN", 130 ip(src=srcaddr, dst=dstaddr) / 131 scapy.TCP(sport=original.dport, dport=original.sport, 132 ack=original.seq + ack_delta, seq=original.ack, 133 flags=TCP_ACK | TCP_FIN, window=TCP_WINDOW)) 134 135def GRE(version, srcaddr, dstaddr, proto, packet): 136 if version == 4: 137 ip = scapy.IP(src=srcaddr, dst=dstaddr, proto=net_test.IPPROTO_GRE) 138 else: 139 ip = scapy.IPv6(src=srcaddr, dst=dstaddr, nh=net_test.IPPROTO_GRE) 140 packet = ip / scapy.GRE(proto=proto) / packet 141 return ("GRE packet", packet) 142 143def ICMPPortUnreachable(version, srcaddr, dstaddr, packet): 144 if version == 4: 145 # Linux hardcodes the ToS on ICMP errors to 0xc0 or greater because of 146 # RFC 1812 4.3.2.5 (!). 147 return ("ICMPv4 port unreachable", 148 scapy.IP(src=srcaddr, dst=dstaddr, proto=1, tos=0xc0) / 149 scapy.ICMPerror(type=3, code=3) / packet) 150 else: 151 return ("ICMPv6 port unreachable", 152 scapy.IPv6(src=srcaddr, dst=dstaddr) / 153 scapy.ICMPv6DestUnreach(code=4) / packet) 154 155def ICMPPacketTooBig(version, srcaddr, dstaddr, packet): 156 if version == 4: 157 desc = "ICMPv4 fragmentation needed" 158 pkt = (scapy.IP(src=srcaddr, dst=dstaddr, proto=1) / 159 scapy.ICMPerror(type=3, code=4) / str(packet)[:64]) 160 # Only newer versions of scapy understand that since RFC 1191, the last two 161 # bytes of a fragmentation needed ICMP error contain the MTU. 162 if hasattr(scapy.ICMP, "nexthopmtu"): 163 pkt[scapy.ICMPerror].nexthopmtu = PTB_MTU 164 else: 165 pkt[scapy.ICMPerror].unused = PTB_MTU 166 return desc, pkt 167 else: 168 return ("ICMPv6 Packet Too Big", 169 scapy.IPv6(src=srcaddr, dst=dstaddr) / 170 scapy.ICMPv6PacketTooBig(mtu=PTB_MTU) / str(packet)[:1232]) 171 172def ICMPEcho(version, srcaddr, dstaddr): 173 ip = _GetIpLayer(version) 174 icmp = {4: scapy.ICMP, 6: scapy.ICMPv6EchoRequest}[version] 175 packet = (ip(src=srcaddr, dst=dstaddr) / 176 icmp(id=PING_IDENT, seq=PING_SEQ) / PING_PAYLOAD) 177 _SetPacketTos(packet, PING_TOS) 178 return ("ICMPv%d echo" % version, packet) 179 180def ICMPReply(version, srcaddr, dstaddr, packet): 181 ip = _GetIpLayer(version) 182 # Scapy doesn't provide an ICMP echo reply constructor. 183 icmpv4_reply = lambda **kwargs: scapy.ICMP(type=0, **kwargs) 184 icmp = {4: icmpv4_reply, 6: scapy.ICMPv6EchoReply}[version] 185 packet = (ip(src=srcaddr, dst=dstaddr) / 186 icmp(id=PING_IDENT, seq=PING_SEQ) / PING_PAYLOAD) 187 # IPv6 only started copying the tclass to echo replies in 3.14. 188 if version == 4 or net_test.LINUX_VERSION >= (3, 14): 189 _SetPacketTos(packet, PING_TOS) 190 return ("ICMPv%d echo reply" % version, packet) 191 192def NS(srcaddr, tgtaddr, srcmac): 193 solicited = inet_pton(AF_INET6, tgtaddr) 194 last3bytes = tuple([ord(b) for b in solicited[-3:]]) 195 solicited = "ff02::1:ff%02x:%02x%02x" % last3bytes 196 packet = (scapy.IPv6(src=srcaddr, dst=solicited) / 197 scapy.ICMPv6ND_NS(tgt=tgtaddr) / 198 scapy.ICMPv6NDOptSrcLLAddr(lladdr=srcmac)) 199 return ("ICMPv6 NS", packet) 200 201def NA(srcaddr, dstaddr, srcmac): 202 packet = (scapy.IPv6(src=srcaddr, dst=dstaddr) / 203 scapy.ICMPv6ND_NA(tgt=srcaddr, R=0, S=1, O=1) / 204 scapy.ICMPv6NDOptDstLLAddr(lladdr=srcmac)) 205 return ("ICMPv6 NA", packet) 206 207