Lorenzo Colitti | 3e6c2d9 | 2015-10-28 16:56:12 +0900 | [diff] [blame] | 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 | |
Lorenzo Colitti | 3e6c2d9 | 2015-10-28 16:56:12 +0900 | [diff] [blame] | 30 | TCP_WINDOW = 14400 |
| 31 | |
Lorenzo Colitti | 7cd46d3 | 2018-01-28 23:06:50 +0900 | [diff] [blame] | 32 | PTB_MTU = 1280 |
| 33 | |
Lorenzo Colitti | 3e6c2d9 | 2015-10-28 16:56:12 +0900 | [diff] [blame] | 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 | |
Lorenzo Colitti | 7e19898 | 2016-04-08 13:14:08 +0900 | [diff] [blame] | 43 | def _RandomPort(): |
Lorenzo Colitti | 3e6c2d9 | 2015-10-28 16:56:12 +0900 | [diff] [blame] | 44 | return random.randint(1025, 65535) |
| 45 | |
| 46 | def _GetIpLayer(version): |
Lorenzo Colitti | 1d486ca | 2017-12-01 21:29:51 +0900 | [diff] [blame] | 47 | return {4: scapy.IP, 5: scapy.IP, 6: scapy.IPv6}[version] |
Lorenzo Colitti | 3e6c2d9 | 2015-10-28 16:56:12 +0900 | [diff] [blame] | 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: |
Lorenzo Colitti | 7e19898 | 2016-04-08 13:14:08 +0900 | [diff] [blame] | 61 | sport = _RandomPort() |
Lorenzo Colitti | 3e6c2d9 | 2015-10-28 16:56:12 +0900 | [diff] [blame] | 62 | return ("UDPv%d packet" % version, |
| 63 | ip(src=srcaddr, dst=dstaddr) / |
| 64 | scapy.UDP(sport=sport, dport=53) / UDP_PAYLOAD) |
| 65 | |
Subash Abhinov Kasiviswanathan | d6cdfeb | 2017-07-07 17:05:42 -0600 | [diff] [blame] | 66 | def UDPWithOptions(version, srcaddr, dstaddr, sport=0, lifetime=39): |
Lorenzo Colitti | 3e6c2d9 | 2015-10-28 16:56:12 +0900 | [diff] [blame] | 67 | if version == 4: |
Subash Abhinov Kasiviswanathan | d6cdfeb | 2017-07-07 17:05:42 -0600 | [diff] [blame] | 68 | packet = (scapy.IP(src=srcaddr, dst=dstaddr, ttl=lifetime, tos=0x83) / |
Lorenzo Colitti | 3e6c2d9 | 2015-10-28 16:56:12 +0900 | [diff] [blame] | 69 | scapy.UDP(sport=sport, dport=53) / |
| 70 | UDP_PAYLOAD) |
| 71 | else: |
| 72 | packet = (scapy.IPv6(src=srcaddr, dst=dstaddr, |
Subash Abhinov Kasiviswanathan | d6cdfeb | 2017-07-07 17:05:42 -0600 | [diff] [blame] | 73 | fl=0xbeef, hlim=lifetime, tc=0x83) / |
Lorenzo Colitti | 3e6c2d9 | 2015-10-28 16:56:12 +0900 | [diff] [blame] | 74 | scapy.UDP(sport=sport, dport=53) / |
| 75 | UDP_PAYLOAD) |
| 76 | return ("UDPv%d packet with options" % version, packet) |
| 77 | |
Lorenzo Colitti | 826405a | 2016-11-09 00:11:48 +0900 | [diff] [blame] | 78 | def SYN(dport, version, srcaddr, dstaddr, sport=0, seq=-1): |
Lorenzo Colitti | 3e6c2d9 | 2015-10-28 16:56:12 +0900 | [diff] [blame] | 79 | ip = _GetIpLayer(version) |
| 80 | if sport == 0: |
Lorenzo Colitti | 7e19898 | 2016-04-08 13:14:08 +0900 | [diff] [blame] | 81 | sport = _RandomPort() |
Lorenzo Colitti | 826405a | 2016-11-09 00:11:48 +0900 | [diff] [blame] | 82 | if seq == -1: # Can't use None because it means unspecified. |
| 83 | seq = random.getrandbits(32) |
Lorenzo Colitti | 3e6c2d9 | 2015-10-28 16:56:12 +0900 | [diff] [blame] | 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") |
Lorenzo Colitti | 424425d | 2015-12-09 22:23:58 +0900 | [diff] [blame] | 93 | was_syn_or_fin = (original.flags & (TCP_SYN | TCP_FIN)) != 0 |
Lorenzo Colitti | 3e6c2d9 | 2015-10-28 16:56:12 +0900 | [diff] [blame] | 94 | return ("TCP RST", |
| 95 | ip(src=srcaddr, dst=dstaddr) / |
| 96 | scapy.TCP(sport=original.dport, dport=original.sport, |
Lorenzo Colitti | 3e8e23f | 2017-09-20 21:38:44 +0900 | [diff] [blame] | 97 | ack=original.seq + was_syn_or_fin, |
| 98 | seq=original.ack, |
Lorenzo Colitti | 3e6c2d9 | 2015-10-28 16:56:12 +0900 | [diff] [blame] | 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") |
Lorenzo Colitti | 03d2366 | 2015-11-06 16:42:06 +0900 | [diff] [blame] | 127 | was_syn_or_fin = (original.flags & (TCP_SYN | TCP_FIN)) != 0 |
| 128 | ack_delta = was_syn_or_fin + len(original.payload) |
Lorenzo Colitti | 3e6c2d9 | 2015-10-28 16:56:12 +0900 | [diff] [blame] | 129 | return ("TCP FIN", |
| 130 | ip(src=srcaddr, dst=dstaddr) / |
| 131 | scapy.TCP(sport=original.dport, dport=original.sport, |
Lorenzo Colitti | 03d2366 | 2015-11-06 16:42:06 +0900 | [diff] [blame] | 132 | ack=original.seq + ack_delta, seq=original.ack, |
Lorenzo Colitti | 3e6c2d9 | 2015-10-28 16:56:12 +0900 | [diff] [blame] | 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: |
Lorenzo Colitti | 7cd46d3 | 2018-01-28 23:06:50 +0900 | [diff] [blame] | 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 |
Lorenzo Colitti | 3e6c2d9 | 2015-10-28 16:56:12 +0900 | [diff] [blame] | 167 | else: |
Lorenzo Colitti | 3e6c2d9 | 2015-10-28 16:56:12 +0900 | [diff] [blame] | 168 | return ("ICMPv6 Packet Too Big", |
| 169 | scapy.IPv6(src=srcaddr, dst=dstaddr) / |
Lorenzo Colitti | 7cd46d3 | 2018-01-28 23:06:50 +0900 | [diff] [blame] | 170 | scapy.ICMPv6PacketTooBig(mtu=PTB_MTU) / str(packet)[:1232]) |
Lorenzo Colitti | 3e6c2d9 | 2015-10-28 16:56:12 +0900 | [diff] [blame] | 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 | |