Stream, datagram, and raw sockets
Most networking applications communicate using
stream (TCP) or datagram (UDP) sockets because they are easy to use:
most of the networking details are handled by the socket implementation.
For some specialized applications,
especially network testing,
stream and datagram sockets do not provide
enough control over the frame contents.
For example, with TCP sockets,
the MAC source address is determined by the Ethernet driver,
the IP source address and fragmentation fields are controlled
by the IP implementation, and
the TCP flags are controlled by the TCP implementation.
Raw sockets provide direct control over these fields and others as well.
Unfortunately, raw sockets are harder to use:
the programmer must explicitly assign every header field
and deal with header layouts, byte swapping, and checksum calculation.
The Raw Socket Toolkit (RST) is a C API
which makes it easier to build, transmit, receive, and parse
packets with raw sockets.
The RST way
RST provides three kinds of services:
- Socket IO
-
provides access to raw sockets with a simple
open/close/send/receive interface,
similar to the C interface for low-level file I/O.
- Packet Header
-
provides functions to build and parse packet headers,
including byte swapping and checksum computation.
Six protocols are supported: Ethernet, ARP, IP, ICMP, TCP, and UDP.
- Utilities
-
provide ASCII/integer conversions for MAC and IP addresses,
and retrieval of the MAC address, IP address, and IP mask
for a local interface.
Unlike packet generation APIs like libnet,
RST does no encapsulation of packet buffers.
Instead, the packet buffers are created by the RST user
and are always directly readable and writeable.
Typically, most header fields are assigned using packetHeader
initialization functions, and then a few fields are overwritten
by the RST programmer.
RST documentation consists of this file, detailed
API documentation
for each function,
and a series of executable examples, described below.
Installing RST
After copying and untar'ing the RST directory tree:
cd rawSocketToolkit/
make
Running the examples
In the next few sections are a variety of examples illustrating the use of RST.
Each example consists of one or more short C programs,
designed to be read, and then executed as root.
The results are seen using Ethereal.
Each example is briefly described,
followed by the shell command(s)
used to execute it and the expected results.
If you wish to run the examples below, in a root window
in directory rawSocketToolkit/bin type:
./tapConfig.sh
This script creates two Ethernet taps: simulated Ethernet devices.
With taps we can send and receive real network packets without
having to set up a private network or risk sending damaging
packets on a production network.
Taps are extremely useful for tests and demonstrations.
The first set of examples uses taps but can be understood
without any knowledge of taps.
The second set of examples explains tap operation.
The third set of examples uses two taps to demonstrate
packet forwarding through the kernel.
First examples
First invoke Ethereal and begin capture
on the Ethernet interface tap1.
Then cd to the examples directory,
run the shell commands shown,
and look to Ethereal for the expected results.
Packet transmission involves the following steps:
- Initialize an rst_buf, R.
R contains a pointer to a character buffer
and pointers to the headers contained in R.
- Initialize the protocol headers in R.
There is an initialization function for each of the supported protocols.
- Optionally override the default values for selected header fields.
- Call htonp ("host-to-network-protocol") to handle byte swaps and checksums.
- Send R using socketIO.
In the examples below,
- generate.c
-
creates 3 packets: raw Ethernet, ICMP, and TCP,
each with header fields unlikely to occur normally.
- Shell commands
-
./generate tap1
- Expected results
-
The first packet contains artificial MAC addresses,
the second packet has an incorrect IP checksum,
and the third packet has all 6 TCP flags set.
- synFlood.c
-
mounts a TCP SYN flood,
designed to disable a host by overflowing its TCP connection table.
- Shell commands
-
./synFlood tap1 00:00:00:00:00:01 10.1.0.99 2000 5
- Expected results
-
There will be 5 packets, identical except for the TCP source port.
The source port will be 2000 in the first packet, 2001 in the second,
etc.
Tap examples
The examples in the previous section used Ethernet taps,
allowing packet generation without using any Ethernet hardware.
This section focuses on the use of taps.
A tap is a virtual ethernet device.
Like a real Ethernet device, a tap
can be given an IP address and mask,
can be monitored with Ethereal, and
can send and receive Ethernet packets with raw, stream, or datagram sockets.
Unlike a real Ethernet device, a tap has no underlying network hardware.
Instead a tap has a character device closely associated with the network device.
This character device takes the place of the Ethernet hardware.
If you write buffer B to the character device,
B will arrive through the network device.
From the kernel's point of view,
it will seem that B has just arrived on an Ethernet card.
To work properly, B must contain a complete frame,
including at least an Ethernet header.
Similarly, if you send a frame to the network device,
reading from the character device will yield the frame,
including all the headers.
Taps are extremely useful for testing and demonstrations.
For example, you can test software designed for a multihomed machine
on a machine with a single Ethernet card or
even with no network hardware at all.
The Raw Socket Toolkit works well with taps, for generating frames
to send to the network device or to write to the character device,
and parsing frames received on the network device or
read from the character device.
In the examples below, packets are read and parsed.
In RST, packet reception involves the following steps:
- Create a character buffer C.
- Read a character C buffer using socketIO.
- Initialize an rst_buf, containing a pointer to C.
- Invoke ntohp (network-to-host-protocol) to parse
the headers and to handle byte swaps.
- Process the packet header fields as needed.
- sendOne.c
-
sends a frame to the network side of a tap
and then reads that frame from the character device.
- Shell commands
-
./sendOne tap1
- Expected results
-
Ethereal will display a single Ethernet packet with protocol 0x9000.
The sendOne application will write to stdout:
read. length: 60 protocol: 0x9000
- receiveOne.c
-
writes a frame to the character device
and then reads the frame from the network device.
- Shell commands
-
./receiveOne tap1
- Expected results
-
Ethereal will display a single Ethernet packet with protocol 0x9000.
The receiveOne application will write to stdout:
read. length: 60 protocol: 0x9000
- echoRequest.c
-
writes an ICMP echo request to the char device.
The packet is sufficiently realistic to stimulate an echo reply
from the local host, observable with Ethereal.
- Shell commands
-
./echoRequest tap1 10.1.0.99
- Expected results
-
Ethereal will display the echo request written to the char side
of the tap by the echoRequest application,
followed by the echo reply sent to the network side of the tap
by the ICMP code in the Linux kernel.
Forwarding examples
For the second set of examples, invoke Ethereal twice,
with one instance capturing on tap1 and
the other capturing on tap2.
- forward.c
-
writes an IP packet to the character side of one tap,
which is forwarded by the kernel and sent to the
network side of another tap.
To get a raw packet forwarded by the kernel requires
careful attention to detail.
For packet P arriving on tap1
to be forwarded by the kernel and sent out on tap2:
-
Forwarding must be enabled in the Linux kernel.
This is done by tapConfig.sh.
-
P's destination MAC address must be the same as tap1's MAC address.
-
P's destination IP address must be on the same subnet as tap2.
-
P's destination IP address must not be on the same as tap2's.
-
P's destination IP address must appear in the ARP cache.
This is done in tapConfig.sh by creating static ARP entries
for IP addresses 10.1.0.99 and 10.2.0.99.
-
There must be no firewall rules blocking P.
On many Linux hosts, firewall rules are installed during reboot.
The firewall rules may be cleared by invoking
clearFirewall.sh in the bin directory.
- Shell commands
-
./forward tap1 10.1.0.99 10.2.0.99
- Expected results
-
On tap1, Ethereal will display the packet written to the char side
of the tap by the forward application.
On tap2, Ethereal will display the packet
sent to the network side of tap2
by the IP forwarding code in the Linux kernel.
- ipOptions.c
-
writes an IP packet to the character side of one tap,
which is forwarded by the kernel and sent to the
network side of another tap.
In this example, however, the packet carries the IP Record Route option.
With this option, each time the packet is forwarded,
the IP address of the outgoing interface is added to the
IP options portion of the packet.
- Shell commands
-
./ipOptions tap1 10.1.0.99 10.2.0.99
- Expected results
-
On tap1, Ethereal will display the packet written to the char side
of the tap by the forward application.
On tap2, Ethereal will display the packet
sent to the network side of tap2
by the IP forwarding code in the Linux kernel.