Home | History | Annotate | Download | only in test
      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 
     17 import random
     18 
     19 from scapy import all as scapy
     20 from socket import *
     21 
     22 import net_test
     23 
     24 TCP_FIN = 1
     25 TCP_SYN = 2
     26 TCP_RST = 4
     27 TCP_PSH = 8
     28 TCP_ACK = 16
     29 
     30 TCP_WINDOW = 14400
     31 
     32 PTB_MTU = 1280
     33 
     34 PING_IDENT = 0xff19
     35 PING_PAYLOAD = "foobarbaz"
     36 PING_SEQ = 3
     37 PING_TOS = 0x83
     38 
     39 # For brevity.
     40 UDP_PAYLOAD = net_test.UDP_PAYLOAD
     41 
     42 
     43 def _RandomPort():
     44   return random.randint(1025, 65535)
     45 
     46 def _GetIpLayer(version):
     47   return {4: scapy.IP, 5: scapy.IP, 6: scapy.IPv6}[version]
     48 
     49 def _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 
     57 def 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 
     66 def 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 
     78 def 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 
     90 def 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 
    101 def 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 
    110 def 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 
    124 def 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 
    135 def 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 
    143 def 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 
    155 def 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 
    172 def 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 
    180 def 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 
    192 def 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 
    201 def 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