Skip to content

Commit

Permalink
Merge pull request #575 from chrgraf/lwaftr
Browse files Browse the repository at this point in the history
added howto craft packets via scapy tool
  • Loading branch information
kbara authored Nov 14, 2016
2 parents 37ebdba + 30f2ee6 commit a83a8ea
Showing 1 changed file with 324 additions and 0 deletions.
324 changes: 324 additions & 0 deletions src/program/snabbvmx/doc/README.scapy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
# crafting packets via scapy

The scapy tool can be used to craft packets (http://www.secdev.org/projects/scapy/).
If using scapy, please make sure to run at minimum version 2.3.2 - versions below did not work smoothly with the lw4o6 packets.

For below commands, please have scapy installed and started via "scapy".
Note: if you intend to send crafted packets to wire, start scapy via "sudo" or as root.

## starting scapy
```
lab@cgrafubuntu2:~/cg-ubuntu2/testing1.0$ scapy
INFO: Can't import python gnuplot wrapper . Won't be able to plot.
INFO: Can't import PyX. Won't be able to use psdump() or pdfdump().
INFO: Can't import python Crypto lib. Won't be able to decrypt WEP.
INFO: Can't import python Crypto lib. Disabled certificate manipulation tools
Welcome to Scapy (2.3.2)
>>>
```

### define IP packet**
Scapy allows to craft any layer of a packet. Below variable a_ip defines an IP Header. Scapy will any required field prepopulate if not defined otherwise.
```
a_ip=IP()
```

To see how the actual packets look like, several commands can be used. The list below is not a complete reference:

- ls(pkt) have the list of fields values
- pkt.summary() for a one-line summary
- pkt.show() for a developped view of the packet
- pkt.show2() same as show but on the assembled packet (checksum is calculated, for instance)

**several options to display the crafted packet**

ls(pkt)
```
>>> ls (a_ip)
version : BitField (4 bits) = 4 (4)
ihl : BitField (4 bits) = None (None)
tos : XByteField = 0 (0)
len : ShortField = None (None)
id : ShortField = 1 (1)
flags : FlagsField (3 bits) = 0 (0)
frag : BitField (13 bits) = 0 (0)
ttl : ByteField = 64 (64)
proto : ByteEnumField = 0 (0)
chksum : XShortField = None (None)
src : SourceIPField (Emph) = '127.0.0.1' (None)
dst : IPField (Emph) = '127.0.0.1' ('127.0.0.1')
options : PacketListField = [] ([])
```

pkt.summary()
```
>>> a_ip.summary()
'192.168.1.1 > 127.0.0.1 hopopt'
```

pkt.show()
```
>>> a_ip.show()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= hopopt
chksum= None
src= 192.168.1.1
dst= 127.0.0.1
\options\
```

pkt.show2()
```
>>> a_ip.show2()
###[ IP ]###
version= 4L
ihl= 5L
tos= 0x0
len= 20
id= 1
flags=
frag= 0L
ttl= 64
proto= hopopt
**chksum= 0x3a3f**
src= 192.168.1.1
dst= 127.0.0.1
\options\
```

## change packet fields
a_ip holds the complete IP-header. Based on the output below the "src" does contain the src-address.
Any part can be changed as seen:
```
>>> a_ip.src='192.168.1.1'
>>> a_ip.show()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= hopopt
chksum= None
src= 192.168.1.1 <<< got modified via a_ip.src=192.168.1.1
dst= 127.0.0.1
\options\
```

## crafting complete ethernet-frame (ICMP echo-request)
Lw4o6 requires to to look into the UDP/TCP ports to define the software the packets belongs to.
For ICMP the icmp-id field is being looked up to assign the correct softwire. Below iptables show how the B4-device is src-nat'ing subscribers packet by with overwriting the src udp,tcp and icmp-id field.

**exemplary iptables rules as done by the B4-device**
```
-A POSTROUTING -o mytun -p tcp -j SNAT --to-source 193.5.1.2:1024-2047
-A POSTROUTING -o mytun -p udp -j SNAT --to-source 193.5.1.2:1024-2047
-A POSTROUTING -o mytun -p icmp -j SNAT --to-source 193.5.1.2:1024-2047
```

**ICMP echo-request with icmp-id 1024**
In the initial example only the IP-header is created. Scapy allows as well to define all layers in a single step.
```
>>> a_l2l3=Ether(src='90:e2:ba:94:2a:bc', dst='5c:45:27:15:a0:0d')/IP(src='10.0.1.100',dst='10.10.0.0')/ICMP(type=8, code=0, id=1024)
>>> a_l2l3.show()
###[ Ethernet ]###
dst= 5c:45:27:15:a0:0d
src= 90:e2:ba:94:2a:bc
type= IPv4
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= icmp
chksum= None
src= 10.0.1.100
dst= 10.10.0.0
\options\
###[ ICMP ]###
type= echo-request
code= 0
chksum= None
id= 0x400
seq= 0x0
>>>
```

## sending the frame to wire
Please make sure you have started scapy as root, otherwise sending the frame will lead into an error (error: [Errno 1] Operation not permitted).
Scapy provides a "send" and "sendp" function. Sendp requires to craft the l2-header accordingly and requires the interface to sent-out the packets.

```
>>> sendp(a_l2l3,iface='p4p1')
.
Sent 1 packets.
```

checking the result via tcpdump
```
lab@cgrafubuntu2:~/cg-ubuntu2/testing1.0$ sudo tcpdump -n -i p4p1 -e
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on p4p1, link-type EN10MB (Ethernet), capture size 262144 bytes
04:33:23.800977 90:e2:ba:94:2a:bc > 5c:45:27:15:a0:0d, ethertype IPv4 (0x0800), length 42: 10.0.1.100 > 10.10.0.0: ICMP echo request, id 1024, seq 0, length 8
```

## crafting lw4o6 encapsulated packet

scapy allows to craft as well the lw4o6 encapsulated packet.
This time a udp-packet with src-port 1024 is crafted:

```
lw4o6=Ether(src='90:e2:ba:94:2a:bc', dst='5c:45:27:15:a0:0d')/IPv6(src='2a02:587:f710::40',dst='2a02:587:f700::100')/IP(src='10.0.1.100',dst='10.10.0.0')/UDP(sport=1024)
>>> lw4o6.summary()
'Ether / IPv6 / IP / UDP 10.0.1.100:1024 > 10.10.0.0:domain'
>>> lw4o6.show()
###[ Ethernet ]###
dst= 5c:45:27:15:a0:0d
src= 90:e2:ba:94:2a:bc
type= IPv6
###[ IPv6 ]###
version= 6
tc= 0
fl= 0
plen= None
nh= IP
hlim= 64
src= 2a02:587:f710::40
dst= 2a02:587:f700::100
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= udp
chksum= None
src= 10.0.1.100
dst= 10.10.0.0
\options\
###[ UDP ]###
sport= 1024
dport= domain
len= None
chksum= None
```

sending this packet
```
>>> sendp(lw4o6,iface='p4p1')
.
Sent 1 packets.
```

checking the result via tcpdump
```
lab@cgrafubuntu2:~/cg-ubuntu2/testing1.0$ sudo tcpdump -n -i p4p1 -e
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on p4p1, link-type EN10MB (Ethernet), capture size 262144 bytes
04:40:27.745410 90:e2:ba:94:2a:bc > 5c:45:27:15:a0:0d, ethertype IPv6 (0x86dd), length 82: 2a02:587:f710::40 > 2a02:587:f700::100: 10.0.1.100.1024 > 10.10.0.0.53: [|domain]
```


## Fragmentation

### sample python script to create IPv4 and lw4o6 frags
scapy allows to generate overlapping, overwriting, incomplete and just valid fragments.
Below code just generates plain non-overlapping IPv4 and IPv6 fragments. It is important to notice the added Fragmentheader, otherwise scapy will not fragment the packet.

### writing directly to a pcap instead of sending to wire
Below script uses the pcapwriter function to write the fragmented packets directly into a pcap instead of sending them to wire.

```
lab@cgrafubuntu2:~/cg-ubuntu2/testing1.0$ cat send-v4-v6-frags.py
#!/usr/bin/python
from scapy.all import *
payload="A"*500+"B"*500
pktdump = PcapWriter("scapy.pcap", append=True, sync=False)
a_lw4o6=Ether(src='90:e2:ba:94:2a:bc', dst='5c:45:27:15:a0:0d')/IPv6(src='2a02:587:f710::40',dst='2a02:587:f700::100',nh=44)/IPv6ExtHdrFragment(nh=4)/IP(src='10.0.1.100',dst='10.10.0.0')/UDP(sport=1024,dport=2000)/payload
a_ipv4=Ether(src='90:e2:ba:94:2a:bc', dst='5c:45:27:15:a0:0d')/IP(src='10.0.1.100',dst='10.10.0.0')/ICMP(type=8, code=0, id=1024)/payload
a_lw4o6.summary()
a_ipv4.summary()
frags4=fragment(a_ipv4,fragsize=500)
frags6=fragment6(a_lw4o6,500)
# IPv4
counter=1
for fragment4 in frags4:
print "Packet no#"+str(counter)
print "==================================================="
fragment4.show() #displays each fragment
counter+=1
#sendp(fragment4,iface='p4p1') <<< uncomment to sent to wire...
pktdump.write(fragment4)
# IPv6
counter=1
for fragment6 in frags6:
print "Packet no#"+str(counter)
print "==================================================="
fragment6.show() #displays each fragment
counter+=1
#sendp(fragment6,iface='p4p1') <<< uncomment to sent to wire...
pktdump.write(fragment6)
```

The resulting IPv6 packet:
```
>>> a_lw4o6.summary()
'Ether / IPv6 / IPv6ExtHdrFragment / IP / UDP 10.0.1.100:1024 > 10.10.0.0:cisco_sccp / Raw'
```

The resulting IPv4 packet
```
>>> a_ipv4.summary()
'Ether / IP / ICMP 10.0.1.100 > 10.10.0.0 echo-request 0 / Raw'
```

**verify generated IPv4 and IPv6 fragments**
When the above script is started, both the IPv4 packet and the IPv6 packet get fragmented and sent to wire:

```
lab@cgrafubuntu2:~/cg-ubuntu2/testing1.0$ tcpdump -n -e -r scapy.pcap
reading from file scapy.pcap, link-type EN10MB (Ethernet)
10:13:30.938939 90:e2:ba:94:2a:bc > 5c:45:27:15:a0:0d, ethertype IPv4 (0x0800), length 538: 10.0.1.100 > 10.10.0.0: ICMP echo request, id 1024, seq 0, length 504
10:13:30.939553 90:e2:ba:94:2a:bc > 5c:45:27:15:a0:0d, ethertype IPv4 (0x0800), length 538: 10.0.1.100 > 10.10.0.0: ip-proto-1
10:13:30.944438 90:e2:ba:94:2a:bc > 5c:45:27:15:a0:0d, ethertype IPv6 (0x86dd), length 494: 2a02:587:f710::40 > 2a02:587:f700::100: frag (0|432) truncated-ip - 596 bytes missing! 10.0.1.100.1024 > 10.10.0.0.2000: UDP, length 1000
10:13:30.945226 90:e2:ba:94:2a:bc > 5c:45:27:15:a0:0d, ethertype IPv6 (0x86dd), length 494: 2a02:587:f710::40 > 2a02:587:f700::100: frag (432|432)
10:13:30.946022 90:e2:ba:94:2a:bc > 5c:45:27:15:a0:0d, ethertype IPv6 (0x86dd), length 226: 2a02:587:f710::40 > 2a02:587:f700::100: frag (864|164)
```

## usecase for scapy
Please see the README.troubleshooting.md. This doc requires pcap-files to run end-to-end tests. Scapy allows to generate such pcaps.



0 comments on commit a83a8ea

Please sign in to comment.