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


Copyright © 2002 QA Cafe All rights reserved.
joe@qacafe.com