A Quick Tour Through Pktsrc - Part 1. The Basics
pktsrc is a Tcl package for Linux that was created to build network protocol
test tools. The goal was to add packet handling to a script language to
allow network engineers and test developers to easily craft protocol packets
in simple scripts. Traditionally, this is done using a 'socket' interface on
the host OS. However, pktsrc uses an interface to the host OS that allows
packets to bypass the OS protocol stack. This gives you the control
you want in a testing situation.
Getting Started
To get started with pktsrc, your Tcl script must load pktsrc.
#!/usr/bin/tclsh
# -- tell your script about pktsrc
package require pktsrc
Once pktsrc is available in your script, a pktsrc 'handle' can be created
that is used to read and write packets.
set handle [ pktsrc open eth0 ]
The pktsrc 'open' command takes the name of a physical interface. You can create
multiple handles on the same or different physical interfaces. Here is a
powerful example script that acts like a dumb bridge.
set port1 [ pktsrc open eth0 ]
set port2 [ pktsrc open eth1 ]
while { 1 } {
# -- read all packets from port1 (eth0)
set packet [ $port1 read ]
# -- I got a packet from port1, send it to port2 (eth1)
$port2 send $packet
}
When pktsrc reads from the network, it receives an entire packet starting at the
Ethernet frame level. Filters can be added to pktsrc handles so that only
certain packets are received. The filter syntax is the same as tcpdump.
set port1 [ pktsrc open eth0 ]
$port1 filter "host 192.168.1.1 or arp"
By adding this filter, only packets from host 192.168.1.1 or arp packets
will be read on port1. You can learn more about tcpdump style filters at
www.tcpdump.org.
However, this example still has a number of drawbacks. First, the read call blocks
on port1 until a packet is received. No packets from port2 are read
at all. Blocking style read calls are problematic once you have multiple
interfaces. Here is the same example reworked using a read callback mechanism.
set port1 [ pktsrc open eth0 ]
set port2 [ pktsrc open eth1 ]
proc process_packets { inPort outPort packet } {
puts "packet received on [ $inPort device ]"
puts "sending packet to [ $outPort device ]"
$outPort send $packet
}
$port1 read { process_packets $port1 $port2 %P }
$port2 read { process_packets $port2 $port1 %P }
# -- enter event loop
vwait forever
This example now uses Tcl's event loop to drive the process. Once the script
enters the event loop (vwait forever), pktsrc will execute the callback
script when a packet arrives on an interface. The %P argument will be
substituted with the contents of the packet. You can also use %S and %U to
provide the second and microsecond timestamps to your callback.
Looking at Packets
Chances are you don't want to receive and send packets without looking at their
contents. Its up to your script to decode the packet and do something useful.
pktsrc returns the contents of a packet as a Tcl string that has been converted
from binary into an ascii representation of hex. This allows you to process
the packet using Tcl string commands. This is an important concept for pktsrc
scripts. pktsrc also has some built-in procedures that help you display
certain network types like IPv4 addresses, MAC addresses, etc.
Let's look at an example of how an ethernet packet could be decoded.
set port1 [ pktsrc open eth0 ]
set packet [ $port1 read ]
# -- decode the destination mac address
set dest [ string range $packet 0 11 ]
# -- decode the source mac address
set src [ string range $packet 12 23 ]
# -- decode the ethernet protocol
set eth [ string range $packet 24 27 ]
puts "packet from $src to $dest with eth proto 0x$eth"
The output from the example looks like
packet from 00062501ff61 to 02e03b1207e4 with eth proto 0x0800
The script uses the string range command to grab parts of the packet.
Since the binary has been converted to an ascii string representation, reading
1 byte from the packet involves 2 characters in a Tcl string. The output from
this example can be slightly improved using the hextomac proc.
puts "packet from [hextomac $src] to [hextomac $dest] with eth proto 0x$eth"
Resulting in
packet from 00:06:25:01:ff:61 to 02:e0:3b:12:07:e4 with eth proto 0x0800
Sending Packets
You can send packets out an pktsrc handle using the send command. The
entire ethernet packet must be defined and sent. Here is an example that
builds an IPv4/UDP packet.
set dstmac "000000020304"
set srcmac "000000010203"
set proto "0800"
set header "45"
set tos "08"
set ip "002ea5ea00003e"
set ipproto "11"
set cksum "d2d2"
set ip_src "1.1.1.1"
set ip_dst "2.0.0.1"
set udp "00010001001a0000"
# -- open a packet source handle
set handle [ pktsrc open eth0 ]
set src [ iptohex $ip_src ]
set dst [ iptohex $ip_dst ]
# -- build the packet
set packet [ format "%s%s%s%s%s%s%s%s%s%s%s" \
$dstmac $srcmac $proto $header $tos $ip $ipproto $cksum $src $dst $udp ]
# -- now send the packet
$handle send-ip $packet
If you look at the last example, it uses the send-ip command instead
of the send command. With the send commmand, a packet is sent with
out any modification. The send-ip, send-tcp, and send-udp
commands each calculate the correct IPv4 checksum, and/or TCP/UDP checksum.
Your script can change a few fields of a packet and resend it without
computing the new checksum.
The last example also uses the iptohex command. This command ( and
its counterpart hextoip ) make it easier to move between dotted quad
notation for IPv4 addresses (1.1.1.1) and its hex representation (01010101).
Moving Beyond Simple Scripts
pktsrc has several built in protocols that can be controlled using the pktsrc
'Stack' interface. This way you do not have re-implement ARP every time you
want to sent an IP packet. There are also detailed packet decodes for many of these
protocols. Here is an example of a decoded DHCP packet.
PACKET-IN(eth1): received eth proto type 0800
ETH: ---- Ethernet Packet (length = 590) ----
ETH:
ETH: Source = 00:20:78:d2:bf:a5
ETH: Destination = ff:ff:ff:ff:ff:ff
ETH: Type = IPv4 (0x0800)
ETH:
IPv4: ---- IPv4 Header ----
IPv4:
IPv4: Version = 4
IPv4: Header Length = 5
IPv4: TOS byte = 0x00
IPv4: Total Length = 576
IPv4: ID = 0x0792
IPv4: Flags/Offset = 0x0000
IPv4: Don't Fragment = 0
IPv4: More Fragments = 0
IPv4: Offset = 0
IPv4: TTL = 150
IPv4: Protocol = UDP (0x11)
IPv4: Checksum = 0x5972
IPv4: Src = 192.168.1.1
IPv4: Dest = 255.255.255.255
IPv4:
UDP: ---- UDP Packet ----
UDP:
UDP: Source Port = 67
UDP: Destination Port = 68
UDP: Length = 556
UDP: Checksum = 0x131f
UDP:
DHCP: ---- DHCP Packet ----
DHCP:
DHCP: Type = Reply (0x02)
DHCP: Hardware Type = 0x01
DHCP: Hardware Address Len = 0x06
DHCP: Hops = 0x00
DHCP: Transaction ID = 0x75300120 (xid)
DHCP: Seconds = 0x0000
DHCP: Flags = 0x0000
DHCP: Client IP = 0.0.0.0 (ciaddr)
DHCP: Your IP = 192.168.1.11 (yiaddr)
DHCP: Server IP = 0.0.0.0 (siaddr)
DHCP: Relay IP = 0.0.0.0 (giaddr)
DHCP: Client HW Addr = 00:05:04:03:00:09 (chaddr)
DHCP: ---- Options ----
DHCP:
DHCP: Magic Cookie = 99.130.83.99
DHCP: Message Type = DHCPOFFER
DHCP: Mask = 255.255.255.0
DHCP: Routers
DHCP: Router 1 = 192.168.1.1
DHCP: DNS Servers
DHCP: DNS Server 1 = 1.1.1.1
DHCP: DNS Server 2 = 1.1.1.2
DHCP: IP Address Lease Time = 86400 seconds (24.0 hours)
DHCP: Server Identifier = 192.168.1.1
DHCP:
DHCP: ---- End DHCP Packet ----
The 'Stack' interface will be explored further in Part 2.
More Information
You can learn more about pktsrc from
http://qacafe.com/pktsrc.htm
|