Click here to Skip to main content
6,295,667 members and growing! (15,960 online)
Email Password   helpLost your password?
General Programming » Internet / Network » Network     Intermediate License: The Mozilla Public License 1.1 (MPL 1.1)

SharpPcap - A packet capture framework for .NET

By Tamir Gal

A framework for capturing, injecting and analyzing network packets for .NET applications based on the WinPcap packet capture driver.
C#, Windows, .NET, Visual Studio, Dev
Posted:27 Nov 2005
Views:142,231
Bookmarked:175 times
Announcements
Loading...
 
Search    
Advanced Search
printPrint   Broken Article?Report       add Share
  Discuss Discuss   Recommend Article Email
95 votes for this article.
Popularity: 9.61 Rating: 4.86 out of 5
1 vote, 1.1%
1

2
2 votes, 2.1%
3
3 votes, 3.2%
4
89 votes, 93.7%
5

[A .NET sniffer application written with SharpPcap]

Introduction

Packet capturing (or packet sniffing) is the process of collecting all packets of data that pass through a given network interface. Capturing network packets in our applications is a powerful capability which lets us write network monitoring, packet analyzers and security tools. The libpcap library for UNIX based systems and its Windows version WinPcap are the most widely used packet capture drivers that provide API for low-level network monitoring. Among the applications that use libpcap/WinPcap as its packet capture subsystem are the famous tcpdump, ethereal and many more.

In this article, I will introduce the SharpPcap library for accessing the WinPcap capture driver in .NET applications, and will give you a detailed programming tutorial on how to use it.

Background

I first needed to use WinPcap in a .NET application while I was working on my final project for university. I needed to analyze and decode VoIP traffic and wanted to keep coding simple with C#. Accessing the WinPcap API from .NET seemed to be quite a popular requirement, and I found some useful projects on CodeProject's website that let you do just that:

The first project is a great ethereal .NET clone that lets you capture and analyze numerous types of protocol packets. However, a few issues with this project make it almost impossible to be shared among other .NET applications. Firstly, the author did not provide any generic API for capturing packets that can be used by other .NET applications. He didn't separate his UI code and his analyzing and capturing code, making his capturing code depend on the GUI classes such as ListView to operate. Secondly, for some reason the author chose to re-implement some of WinPcap's functions in C# by himself rather than just wrapping them. This means that his application can't take advantage of the new WinPcap versions since he hard coded a certain version of WinPcap in his application.

The second and the third articles are nice starts for wrapper projects for WinPcap, however they didn't provide some important WinPcap features such as handling offline pcap files and applying kernel-level packet filters, and most importantly they provide no parser classes for analyzing protocol packets. Both projects didn't post their library source code together with the article in order to let other people extend their work and add new features and new packet parser classes.

And so, I decided to try and write my own library for the task.

About SharpPcap

The purpose of SharpPcap is to provide a framework for capturing, injecting and analyzing network packets for .NET applications based on the WinPcap packet capture driver. The following list illustrates the features currently supported by SharpPcap:

  • Enumerating and showing details about the physical network interface on a Windows machine.
  • Capturing low-level network packets going through a given interface.
  • Analyzing and parsing the following protocols: Ethernet, ARP, IP, TCP, UDP, ICMP, IGMP.
  • Injecting low-level network packets on a given interface.
  • Handling (reading and writing) offline packet capture files.
  • Injecting packets using send queues.
  • Collecting network statistics on a given network interface.

Please check my homepage for the latest updates and bug fixes.

SharpPcap tutorial: A step by step guide to using SharpPcap

The text of this tutorial is taken directly from WinPcap's official tutorial but is modified to show the C# use of the SharpPcap library. All examples can be downloaded together with the SharpPcap source code from the top of this page. The WinPcap library must be installed before attempting to run any of these examples, so please download and install the latest version from WinPcap's download page.

SharpPcap was written and tested using .NET v1.1 and Windows 2000/XP. I have no idea about other .NET and Windows versions. If you do try it, please report your results.

The following topics are covered in this tutorial:

  1. Obtaining the device list
  2. Obtaining advanced information about the installed devices
  3. Opening an adapter and capturing packets
  4. Capturing packets without the event handler
  5. Filtering the traffic
  6. Interpreting the packets
  7. Handling offline dump files
  8. Sending packets
  9. Gathering statistics on the network traffic

Obtaining the device list

Typically, the first thing that a WinPcap-based application does is get a list of attached network adapters. SharpPcap provides the GetAllDevices() function for this purpose: this function returns a list of PcapDevice objects, each of which contains comprehensive information about an attached adapter. In particular, the fields PcapName and PcapDescription contain the name and a human readable description, respectively, of the corresponding device. The following C# sample shows how to retrieve a list of adapters and print it on the screen, printing an error if no adapters are found:

/* Retrieve the device list */
PcapDeviceList devices = SharpPcap.GetAllDevices();

/*If no device exists, print error */
if(devices.Count<1)
{
    Console.WriteLine("No device found on this machine");
    return;
}

int i=0;

/* Scan the list printing every entry */
foreach(PcapDevice dev in devices)
{
    /* Description */
    Console.WriteLine("{0}) {1}",i,dev.PcapDescription);
    Console.WriteLine();
    /* Name */
    Console.WriteLine("\tName:\t{0}",dev.PcapName);
    /* IP Address */
    Console.WriteLine("\tIP Address: \t\t{0}",dev.PcapIpAddress);
    /* Is Loopback */
    Console.WriteLine("\tLoopback: \t\t{0}",dev.PcapLoopback);

    Console.WriteLine();
    i++;
}

The output of the above application will be as something like this:

SharpPcap 1.0.2.0, Example4.IfList.cs

The following devices are available on this machine:
----------------------------------------------------

0) Generic dialup adapter

        Name:   \Device\NPF_GenericDialupAdapter
        IP Address:             0.0.0.0
        Loopback:               False

1) Intel(R) PRO/1000 MT Mobile Connection 
   (Microsoft's Packet Scheduler)

    Name:   \Device\NPF_{355BF264-B768-454A-84BC-096A44F0ADA9}
    IP Address:             10.10.10.100
    Loopback:               False

Hit 'Enter' to exit...

Obtaining advanced information about the installed devices

The previous section demonstrated how to get basic information about the available adapters. Actually, for each PcapDevice in the list, SharpPcap will try to use the Win32 IPHelper API (if present) to retrieve some more info about the underlying network adapter. For this purpose, we use the NetworkDevice class which is a subclass of PcapDevice and represents a physical network card on the machine. So each PcapDevice in the list that has IPHelper information can be casted to a NetworkDevice type.

The NetworkDevice class contains useful information about the underlying network adapter. Similar to the 'ipconfig' command available in Windows NT, it holds the IP information (IP address, subnet mask and default gateway), the MAC address (physical address) of the adapter, DHCP and WINS information.

The following sample goes over each PcapDevice in the list and prints its basic pcap information. Later, if the device is actually a NetworkDevice, it casts and then prints the device's advanced information:

/* Scan the list printing every entry */
foreach(PcapDevice dev in devices)
{
    /* Description */
    Console.WriteLine("{0}) {1}",i,dev.PcapDescription);
    Console.WriteLine();
    /* Name */
    Console.WriteLine("\tName:\t\t{0}",dev.PcapName);
    /* Is Loopback */
    Console.WriteLine("\tLoopback:\t\t{0}",dev.PcapLoopback);

    /* 
    If the device is a physical network device,
    lets print some advanced info
    */
    if(dev is NetworkDevice)
    {//Then..


        /* Cast to NetworkDevice */
        NetworkDevice netDev = (NetworkDevice)dev;
        /* Print advanced info */
        Console.WriteLine("\tIP Address:\t\t{0}",
                                     netDev.IpAddress);
        Console.WriteLine("\tSubnet Mask:\t\t{0}",
                                     netDev.SubnetMask);
        Console.WriteLine("\tMAC Address:\t\t{0}",
                                     netDev.MacAddress);
        Console.WriteLine("\tDefault Gateway:\t{0}",
                                     netDev.DefaultGateway);
        Console.WriteLine("\tPrimary WINS:\t\t{0}",
                                     netDev.WinsServerPrimary);
        Console.WriteLine("\tSecondary WINS:\t\t{0}",
                                     netDev.WinsServerSecondary);
        Console.WriteLine("\tDHCP Enabled:\t\t{0}",
                                     netDev.DhcpEnabled);
        Console.WriteLine("\tDHCP Server:\t\t{0}",
                                     netDev.DhcpServer);
        Console.WriteLine("\tDHCP Lease Obtained:\t{0}",
                                     netDev.DhcpLeaseObtained);
        Console.WriteLine("\tDHCP Lease Expires:\t{0}",
                                     netDev.DhcpLeaseExpires);
    }
    Console.WriteLine();
    i++;
}

And here is a sample output:

SharpPcap 1.0.2.0, Example4.IfListAdv.cs

The following devices are available on this machine:
----------------------------------------------------

0) Generic dialup adapter

        Name:     \Device\NPF_GenericDialupAdapter
        Loopback: False

1) Intel(R) PRO/1000 MT Mobile Connection 
   (Microsoft's Packet Scheduler)

        Name:    \Device\NPF_{355BF264-B768-454A-84BC-096A44F0ADA9}
        Loopback:               False
        IP Address:             10.10.10.100
        Subnet Mask:            255.255.255.0
        MAC Address:            000D60CDB2A5
        Default Gateway:        10.10.10.1
        Primary WINS:           1.1.1.1
        Secondary WINS:         2.2.2.2
        DHCP Enabled:           True
        DHCP Server:            10.10.10.1
        DHCP Lease Obtained:    27-Nov-05 11:13:55
        DHCP Lease Expires:     30-Nov-05 11:13:55

Hit 'Enter' to exit...

Opening an adapter and capturing packets

Now that we've seen how to obtain an adapter to play with, let's start the real job, opening an adapter and capturing some traffic. In this section, we'll write a program that prints some information about each packet flowing through the adapter.

The function that opens a device for capture is PcapOpen() which is overloaded with some arguments as follows:

  • PcapOpen()
  • PcapOpen(bool promiscuous_mode)
  • PcapOpen(bool promiscuous_mode, int read_timeout)

The above two arguments deserve some further explanation.

promiscuous_mode: In a normal operation, an adapter only captures packets from the network that are destined to it; the packets exchanged by other hosts are therefore ignored. Instead, when the adapter is in promiscuous mode it captures all packets whether they are destined to it or not. This means that on shared media (like non-switched Ethernet), WinPcap will be able to capture the packets of other hosts. Promiscuous mode is the default for most capture applications, so we enable it in the following example.

read_timeout: Specifies the read timeout, in milliseconds. A read on the adapter (for example, using the PcapGetNextPacket() function) will always return after read_timeout milliseconds, even if no packets are available from the network. read_timeout also defines the interval between statistical reports if the adapter is in statistical mode (see the Gathering Statistics on the network traffic section). Setting read_timeout to 0 means no timeout, a read on the adapter never returns if no packets arrive. A -1 timeout on the other side causes a read on the adapter to always return immediately.

The following example shows the use of the PcapOnPacketArrival event for receiving packets. We create an event handler that is being called whenever a new packet is going through the PcapDevice:

//Extract a device from the list

PcapDevice device = devices[i];

//Register our handler function to the 

//'packet arrival' event

device.PcapOnPacketArrival += 
  new SharpPcap.PacketArrivalEvent(device_PcapOnPacketArrival);

//Open the device for capturing

//true -- means promiscuous mode

//1000 -- means a read wait of 1000ms

device.PcapOpen(true, 1000);

Console.WriteLine(
    "-- Listenning on {0}, hit 'Enter' to stop...",
    device.PcapDescription);

//Start the capturing process

device.PcapStartCapture();

//Wait for 'Enter' from the user.

Console.ReadLine();

//Stop the capturing process

device.PcapStopCapture();

//Close the pcap device

device.PcapClose();

And here is our packet handler implementation:

/// <SUMMARY>

/// Prints the time and length of each received packet

/// </SUMMARY>

private static void device_PcapOnPacketArrival(
                         object sender, Packet packet)
{
    DateTime time = packet.PcapHeader.Date;
    int len = packet.PcapHeader.PacketLength;
    Console.WriteLine("{0}:{1}:{2},{3} Len={4}", 
        time.Hour, time.Minute, time.Second, time.Millisecond, len);
}

Once the adapter is opened, the capture can be started with the PcapStartCapture() or PcapCapture(int packetCount) functions. These two functions are very similar, the difference is that PcapStartCapture() is a non-blocking function that starts the capturing process on a new thread, while PcapCapture(int packetCount) blocks until packetCount packets have been captured. When using PcapStartCapture() we should later call PcapStopCapture() to terminate the capture process. When using PcapCapture(int packetCount) it is possible to pass a SharpPcap.INFINITE value to keep capturing forever.

Both of these functions require that an event handler for processing packets registered prior to calling them. This event handler is invoked by PcapDevice for every new packet coming from the network and receives the sender object that invoked this handler (i.e. the PcapDevice object) and the actual received Packet, including all the protocol headers. Note that the frame CRC is normally not present in the packet, because it is removed by the network adapter after the frame validation. Note also that most adapters discard packets with wrong CRCs, so WinPcap (and therefore SharpPcap) is normally not able to capture them.

The Packet class represents a generic packet captured from the network. Each such packet has a PcapHeader property containing some info (e.g. the timestamp of the capture and the length of the packet) about the captured packet. The above example extracts the timestamp and the length from every Packet object and prints them on the screen.

Please note that there may be a drawback using an event handler for processing packets, mainly related to the fact that the handler is called by the PcapDevice; therefore the user application does not have direct control over it. Another approach (and to have more readable programs) is to use the PcapGetNextPacket() function, which is presented in the next section.

Capturing packets without the event handler

The example program in this section behaves exactly like the previous sample, but it uses PcapGetNextPacket() instead of registering an event handler. The PcapOnPacketArrival event is a good practice and could be a good choice in some situations. However, handling a callback is sometimes not practical - it often makes the program more complex especially in situations with multithreaded applications. In these cases, PcapGetNextPacket() retrieves a packet with a direct call - using PcapGetNextPacket(), packets are received only when the programmer wants them. In the following program, we re-use the event handler code of the previous example and move it into a loop in the main function right after the call to PcapGetNextPacket().

Note: The following example will exit if the timeout of 1000 ms expires with no packets on the network:

//Extract a device from the list

PcapDevice device = devices[i];

//Open the device for capturing

//true -- means promiscuous mode

//1000 -- means a read wait of 1000ms

device.PcapOpen(true, 1000);

Console.WriteLine();
Console.WriteLine("-- Listenning on {0}...",
    device.PcapDescription);

Packet packet = null;

//Keep capture packets using PcapGetNextPacket()

while( (packet=device.PcapGetNextPacket()) != null )
{
    // Prints the time and length of each received packet

    DateTime time = packet.PcapHeader.Date;
    int len = packet.PcapHeader.PacketLength;
    Console.WriteLine("{0}:{1}:{2},{3} Len={4}", 
              time.Hour, time.Minute, time.Second, 
              time.Millisecond, len);
}
//Close the pcap device

device.PcapClose();
Console.WriteLine("-- Capture stopped, device closed.");

Filtering the traffic

One of the most powerful features offered by libpcap and WinPcap is the filtering engine. It provides a very efficient way to receive subsets of the network traffic, and is (usually) integrated with the capture mechanism provided by WinPcap. The WinPcap lib has an integrated compiler that takes a string containing a high-level Boolean (filter) expression and produces a low-level byte code that can be interpreted by the filter engine of WinPcap's packet driver. The syntax (also known as the tcpdump syntax) of the boolean expression is widely used in many applications other than WinPcap and libpcap. You can find its spec in WinPcap's documentation page.

The function PcapSetFilter() associates a filter with a capture session in WinPcap's kernel driver. Once PcapSetFilter() is called, the associated filter will be applied to all the packets coming from the network, and all the conformant packets (i.e., packets for which the boolean expression evaluates to true) will be actually copied to the application. The following code shows how to compile and set a filter.

Note that WinPcap's expression compiler requires that the netmask of the PcapDevice will be passed together with the filter, because some filters created by the compiler require it. However SharpPcap takes care of it for us by automatically extracting the netmask from the adapter.

The filter expression we use in the following snippet is "ip and tcp", which means to "keep only the packets that are both IPv4 and TCP and deliver them to the application":

//Open the device for capturing

//true -- means promiscuous mode

//1000 -- means a read wait of 1000ms

device.PcapOpen(true, 1000);

//tcpdump filter to capture only TCP/IP packets            

string filter = "ip and tcp";
//Associate the filter with this capture

device.PcapSetFilter( filter );

Console.WriteLine();
Console.WriteLine
  ("-- The following tcpdump filter will be applied: \"{0}\"", 
  filter);
Console.WriteLine
  ("-- Listenning on {0}, hit 'Enter' to stop...",
  device.PcapDescription);

//Start capture packets

device.PcapCapture( SharpPcap.INFINITE );

//Close the pcap device

//(Note: this line will never be called since

// we're capturing infinite number of packets

device.PcapClose();

Interpreting the packets

Now that we are able to capture and filter network traffic, we want to put our knowledge to work with a simple "real world" application. In this lesson, we will take the code from the previous sections and use these pieces to build a more useful program. The main purpose of the current program is to show how the protocol headers of a captured packet can be parsed and interpreted. The resulting application, called DumpTCP, prints a summary of the TCP traffic on our network. I have chosen to parse and display the TCP protocol (rather than the UDP example posted in the original tutorial) because it is a bit more interesting than UDP and with SharpPcap it doesn't require too much parsing coding.

/// <SUMMARY>

/// Prints the time, length, src ip, 

/// src port, dst ip and dst port

/// for each TCP/IP packet received on the network

/// </SUMMARY>

private static void device_PcapOnPacketArrival(
                       object sender, Packet packet)
{            
    if(packet is TCPPacket)
    {                
        DateTime time = packet.Timeval.Date;
        int len = packet.PcapHeader.len;

        TCPPacket tcp = (TCPPacket)packet;
        string srcIp = tcp.SourceAddress;
        string dstIp = tcp.DestinationAddress;
        int srcPort = tcp.SourcePort;
        int dstPort = tcp.DestinationPort;

        Console.WriteLine("{0}:{1}:{2},
            {3} Len={4} {5}:{6} -> {7}:{8}", 
            time.Hour, time.Minute, time.Second, 
            time.Millisecond, len, srcIp, srcPort, 
            dstIp, dstPort);
    }
}

If you take a look at the UDP example of the original WinPcap tutorial you will see how complex it is to parse the packets (although UDP is a bit simpler to parse than TCP in our example) directly from the raw data bytes provided by the WinPcap library. Luckily for us, SharpPcap provides some useful packet analyzing classes for some common protocols (e.g. TCP, UDP, ICMP and others). All these analyzing classes are a direct C# translation from JPcap which is a Java wrapper for libpcap/WinPcap similar to SharpPcap. All these analyzing classes can be found under the Tamir.IPLib.Packets namespace.

As you can see, in our packet handler we first do a check to verify that the Packet object received from the PcapDevice is of type TCPPacet, and only then we cast it to a TCPPacet. This check is not really needed if we set the appropriate filter using the PcapSetFilter() function, however we use it here as a good practice.

The TCPPacket is a subclass of the IPPacket class (since TCP runs on top of IP), so all TCP/IP fields are accessible using TCPPacet's properties. In the above example, we extract the TCP and IP values from the TCPPacket class and print them on the screen (of course, the TCPPacket class contains additional info such as TCP flags and sequence number fields, which are not shown in this example). The resulting output is something like this:

Available devices:
------------------

1) Intel(R) PRO/1000 MT Mobile Connection 
   (Microsoft's Packet Scheduler)

-- Please choose a device to capture: 1

-- Listenning on Intel(R) PRO/1000 MT Mobile Connection 
-- (Microsoft's Packet Scheduler)...
1:18:17,675 Len=123 66.102.7.147:80 -> 10.21.98.21:43501
1:18:17,675 Len=80 10.21.98.21:43501 -> 66.102.7.147:80
1:18:17,919 Len=54 66.102.7.147:80 -> 10.21.98.21:43501

Each of the final three lines represents a different packet.

Handling offline dump files

In this section, we are going to learn how to handle packet capture to a file (dump to file). WinPcap offers built-in functions for saving network traffic to a file and to read the content of dumps - this section will teach you how to accomplish this with SharpPcap. The format for dump files is the libpcap one. This format contains the data of the captured packets in binary form and is a standard widely used by many network tools including windump, tcpdump, ethereal and snort. Therefore, any dump file we create using SharpPcap can be opened with any of the above tools and others.

Saving packets to a dump file

First of all, let's see how to write packets in libpcap file format. The following example captures the packets from the selected interface and saves them on a file whose name is provided by the user:

Console.Write("-- Please enter the output file name: ");
string capFile = Console.ReadLine();

PcapDevice device = devices[i];

//Register our handler function to the 'packet arrival' event

device.PcapOnPacketArrival += 
  new SharpPcap.PacketArrivalEvent( device_PcapOnPacketArrival );

//Open the device for capturing

//true -- means promiscuous mode

//1000 -- means a read wait of 1000ms

device.PcapOpen(true, 1000);

//Open or create a capture output file

device.PcapDumpOpen( capFile );

Console.WriteLine();
Console.WriteLine
    ("-- Listenning on {0}, hit 'Ctrl-C' to exit...",
    device.PcapDescription);

//Start capture 'INFINTE' number of packets

device.PcapCapture( SharpPcap.INFINITE );

//Close the pcap device

//(Note: this line will never be called since

// we're capturing infinite number of packets

device.PcapClose();

And here is the packet handler that will dump each received packet to the file:

/// <SUMMARY>

/// Dumps each received packet to a pcap file

/// </SUMMARY>

private static void device_PcapOnPacketArrival(
                        object sender, Packet packet)
{                        
    PcapDevice device = (PcapDevice)sender;
    //if device has a dump file opened

    if( device.PcapDumpOpened )
    {
        //dump the packet to the file

        device.PcapDump( packet );
        Console.WriteLine("Packet dumped to file.");
    }
}

As you can see, the structure of the program is very similar to the ones we have seen in the previous sections. The differences are:

  • The call to device.PcapDumpOpen( capFile ) is issued once the interface is opened. This call opens a dump file and associates it with the interface.
  • The packets are written to this file with the device.PcapDump( packet ) call in the packet handler. Note the use of the sender object parameter passed to the packet handler callback which is casted to a PcapDevice.

Reading packets from a dump file

Now that we have a dump file available, we can try to read its content. The following code opens a WinPcap/libpcap dump file and displays every packet contained in the file. The SharpPcap.GetPcapOfflineDevice( capFile ) function returns a PcapDevice object which represents the offline capture file that we read, then the usual PcapOnPacketArrival event is used to sequence through the packets. As you can see, reading packets from an offline capture is nearly identical to receiving them from a physical interface:

Console.Write("-- Please enter an input capture file name: ");
string capFile = Console.ReadLine();

PcapDevice device;

try
{
    //Get an offline file pcap device

    device = SharpPcap.GetPcapOfflineDevice( capFile );
    //Open the device for capturing

    device.PcapOpen();
} 
catch(Exception e)
{
    Console.WriteLine(e.Message);
    return;
}

//Register our handler function to the 'packet arrival' event

device.PcapOnPacketArrival += 
  new SharpPcap.PacketArrivalEvent(device_PcapOnPacketArrival);

Console.WriteLine();
Console.WriteLine
    ("-- Capturing from '{0}', hit 'Ctrl-C' to exit...",
    capFile);

//Start capture 'INFINTE' number of packets

//This method will return when EOF reached.

device.PcapCapture( SharpPcap.INFINITE );

//Close the pcap device

device.PcapClose();
Console.WriteLine("-- End of file reached.");

Sending packets

One of the coolest things about WinPcap compared to libpcap is its ability to send raw packets out to the network.

Sending a single packet with SharpPcap

The simplest way to send a packet is shown in the following code snippet. After opening an adapter, PcapSendPacket is called to send a hand-crafted packet. PcapSendPacket takes as argument a byte array or a Packet object containing the data to be sent. Notice that the buffer is sent to the net as is, without any manipulation. This means that the application has to create the correct protocol headers in order to send something meaningful:

//Open the device

device.PcapOpen();

//Generate a random packet

byte[] bytes = GetRandomPacket();

try
{
    //Send the packet out the network device

    device.PcapSendPacket( bytes );
    Console.WriteLine("-- Packet sent successfuly.");
}
catch(Exception e)
{
    Console.WriteLine("-- "+ e.Message );
}

//Close the pcap device

device.PcapClose();
Console.WriteLine("-- Device closed.");

Send queues

While PcapSendPacket offers a simple and immediate way to send a single packet, send queues provide an advanced, powerful and optimized mechanism to send a collection of packets. A send queue is a container for a variable number of packets that will be sent to the network. It has a size, that represents the maximum amount of bytes it can store.

SharpPcap represents a send queue using the PcapSendQueue class which is constructed by specifying the size of the new send queue.

Once the send queue is created, PcapSendQueue.Add() can be called to add a packet to the send queue. This function takes a PcapHeader with the packet's timestamp and length and a buffer or a Packet object holding the data of the packet. These parameters are the same as those received by the OnPacketArrival event, therefore queuing a packet that was just captured or a read from a file is a matter of passing these parameters to PcapSendQueue.Add().

To transmit a send queue, SharpPcap provides the PcapDevice.PcapSendQueue(PcapSendQueue q, bool sync) function. Note the second parameter: if true, the send will be synchronized, i.e. the relative timestamps of the packets will be respected. This operation requires a remarkable amount of CPU, because the synchronization takes place in the kernel driver using "busy wait" loops. Although this operation is quite CPU intensive, it often results in very high precision packet transmissions (often around few microseconds or less).

Note that transmitting a send queue with PcapDevice.PcapSendQueue() is much more efficient than performing a series of PcapDevice.PcapSendPacket(), since the send queue buffered at kernel level drastically decreases the number of context switches.

When a queue is no longer needed, it can be deleted with PcapSendQueue.Dispose() that frees all the buffers associated with the send queue.

The next program shows how to use send queues. It opens a capture file with SharpPcap.GetOfflineDevice(), then it stores the packets from the file to a properly allocated send queue. At his point it transmits the queue synchronized.

Note that the link-layer of the dumpfile is compared with one of the interface that will send the packets using the PcapDevice.DataLink property, and a warning is printed if they are different - it is important that the capture-file link-layer be the same as the adapter's link layer for otherwise the transmission is pointless:

PcapDevice device;

try
{
    //Get an offline file pcap device

    device = SharpPcap.GetPcapOfflineDevice( capFile );
    //Open the device for capturing

    device.PcapOpen();
} 
catch(Exception e)
{
    Console.WriteLine(e.Message);
    return;
}

Console.Write("Queueing packets...");

//Allocate a new send queue

PcapSendQueue squeue = new PcapSendQueue
    ( (int)((PcapOfflineDevice)device).PcapFileSize );
Packet packet;

try
{
  //Go through all packets in the file and add to the queue

  while( (packet=device.PcapGetNextPacket()) != null )
  {
     if( !squeue.Add( packet ) )
     {
        Console.WriteLine("Warning: packet buffer too small, "+
                            "not all the packets will be sent.");
        break;
     }
  }
}
catch(Exception e)
{
    Console.WriteLine(e.Message);
    return;
}

Console.WriteLine("OK");

Console.WriteLine();
Console.WriteLine(
   "The following devices are available on this machine:");
Console.WriteLine(
   "----------------------------------------------------");
Console.WriteLine();

int i=0;

PcapDeviceList devices = SharpPcap.GetAllDevices();
/* Scan the list printing every entry */
foreach(PcapDevice dev in devices)
{
    /* Description */
    Console.WriteLine("{0}) {1}",i,dev.PcapDescription);
    i++;
}

Console.WriteLine();
Console.Write("-- Please choose a device to transmit on: ");
i = int.Parse( Console.ReadLine() );
devices[i].PcapOpen();
string resp;

if(devices[i].PcapDataLink != device.PcapDataLink)
{
    Console.Write("Warning: the datalink of the capture"+
           "differs from the one of the selected interface, 
           continue? [YES|no]");
    resp = Console.ReadLine().ToLower();

    if((resp!="")&&( !resp.StartsWith("y")))
    {
        Console.WriteLine("Cancelled by user!");
        devices[i].PcapClose();
        return;
    }
}
device.PcapClose();
device = devices[i];

Console.Write("This will transmit all queued packets through"+
                            "this device, continue? [YES|no]");
resp = Console.ReadLine().ToLower();

if((resp!="")&&( !resp.StartsWith("y")))
{
    Console.WriteLine("Cancelled by user!");
    return;
}

try
{
    Console.Write("Sending packets...");
    int sent = device.PcapSendQueue( squeue, true );
    Console.WriteLine("Done!");
    if( sent < squeue.CurrentLength )
    {
       Console.WriteLine("An error occurred sending the packets: {0}. "+
               "Only {1} bytes were sent\n", device.PcapLastError, sent);
    }
}
catch(Exception e)
{
    Console.WriteLine( "Error: "+e.Message );
}
//Free the queue

squeue.Dispose();
Console.WriteLine("-- Queue is disposed.");
//Close the pcap device

device.PcapClose();
Console.WriteLine("-- Device closed.");

Gathering statistics on the network traffic

This section shows another advanced feature of WinPcap: the ability to collect statistics about network traffic. WinPcap's statistical engine makes use of the kernel-level packet filter to efficiently classify the incoming packet. You can take a look at the NPF driver internals manual if you want to learn more about the details.

In order to use this feature, the programmer must open an adapter and put it in statistical mode. This can be done by setting the PcapDevice.PcapMode property. In particular, PcapMode.Statistics must be used as the mode argument of this property.

With the statistical mode, making an application that monitors the TCP traffic load is a matter of few lines of code. The following sample shows how to do this:

//Register our handler function to the 

//'pcap statistics' event

device.PcapOnPcapStatistics += 
  new Tamir.IPLib.SharpPcap.PcapStatisticsEvent( 
                         device_PcapOnPcapStatistics );

//Open the device for capturing

//true -- means promiscuous mode

//1000 -- means stats will be collected 1000ms

device.PcapOpen(true, 1000);

//Handle TCP packets only

device.PcapSetFilter( "tcp" );
            
//Set device to statistics mode

device.PcapMode = PcapMode.Statistics;

Console.WriteLine();
Console.WriteLine("-- Gathering statistics on \"{0}\", 
      hit 'Enter' to stop...", device.PcapDescription);

//Start the capturing process

device.PcapStartCapture();

//Wait for 'Enter' from the user.

Console.ReadLine();

//Stop the capturing process

device.PcapStopCapture();

//Close the pcap device

device.PcapClose();
Console.WriteLine("Capture stopped, 
                     device closed.");

And our event handler will print the statistics:

static int oldSec=0;
static int oldUsec=0;
/// <SUMMARY>

/// Gets a pcap stat object and calculate bps and pps

/// </SUMMARY>

private static void device_PcapOnPcapStatistics(
             object sender, PcapStatistics statistics)
{
    /* Calculate the delay in microseconds */
    /* from the last sample. */
    /* This value is obtained from the timestamp */
    /* that's associated with the sample. */
    int delay=(statistics.Seconds - oldSec) * 
              1000000 - oldUsec + statistics.MicroSeconds;
    /* Get the number of Bits per second */
    long bps =  
        ( statistics.RecievedBytes * 8 * 1000000) / delay;
    /*                                  ^        ^
                                     |        |
                                     |        |
                                     |        |
            converts bytes in bits      -----         |
          delay is expressed in microseconds ---------
    */

    /* Get the number of Packets per second */
    long pps=
        (statistics.RecievedPackets * 1000000) / delay;

    /* Convert the timestamp to readable format */
    string ts = statistics.Date.ToLongTimeString();

    /* Print Statistics */
    Console.WriteLine("{0}: bps={1}, pps={2}", 
                                     ts, bps, pps); 

    //store current timestamp

    oldSec=statistics.Seconds;
    oldUsec=statistics.MicroSeconds;
}

Note that this example is by far more efficient than a program that captures the packets in the traditional way and calculates statistics at the user-level. Statistical mode requires minimum amount of data copies and context switches and therefore the CPU is optimized. Moreover, a very small amount of memory is required.

References

  • SharpPcap - the homepage of the SharpPcap library.
  • WinPcap - a packet capture framework for Windows.
  • JPcap - A Java wrapper for libpcap and WinPcap. All the packet parser classes of SharpPcap were translated from JPcap.

History

  • 2005-Nov-27
    • Initial version posted.

License

This article, along with any associated source code and files, is licensed under The Mozilla Public License 1.1 (MPL 1.1)

About the Author

Tamir Gal


Member
Works as a Network Engineer for a leading networking company.
Occupation: Software Developer
Location: Israel Israel

Other popular Internet / Network articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 25 of 243 (Total in Forum: 243) (Refresh)FirstPrevNext
GeneralBug with small MTU size PinmemberGuy Shtub23:42 22 Jun '09  
Questionreassembly of TCP packets PinmemberGuy Shtub5:00 18 Jun '09  
GeneralMy vote of 1 Pinmemberbalajigcs21:14 9 Jun '09  
GeneralMy vote of 1 Pinmemberbalajigcs21:14 9 Jun '09  
Generalping problem PinmemberMember 398265023:00 24 Apr '09  
GeneralRe: ping problem Pinmemberchmorgan18:57 11 May '09  
GeneralRe: ping problem Pinmemberfreakfp18:20 18 May '09  
GeneralFilter for offline files? PinmemberMember 420371918:56 4 Apr '09  
GeneralRe: Filter for offline files? Pinmemberchmorgan19:00 11 May '09  
Questionpacket header and data PinmemberCalvin Teh7:46 4 Apr '09  
General[Message Deleted] Pinmemberit.ragester6:35 28 Mar '09  
QuestionNamespace problem PinmemberCalvin Teh7:10 14 Mar '09  
AnswerRe: Namespace problem PinmemberTamir Gal12:53 15 Mar '09  
GeneralSharpPcap v2.0.2 has been released Pinmemberchmorgan8:59 27 Feb '09  
GeneralRe: SharpPcap v2.0.2 has been released PinmemberTamir Gal2:21 28 Feb '09  
QuestionClosing an Established Connection PinmemberRiyazAhemed4:08 25 Feb '09  
AnswerRe: Closing an Established Connection Pinmemberchmorgan17:37 26 Feb '09  
GeneralRe: Closing an Established Connection PinmemberRiyazAhemed4:52 27 Feb '09  
GeneralMissing NetworkDevice class PinmemberRiyazAhemed1:07 25 Feb '09  
AnswerRe: Missing NetworkDevice class Pinmemberchmorgan8:24 25 Feb '09  
GeneralRe: Missing NetworkDevice class PinmemberRiyazAhemed4:16 26 Feb '09  
GeneralRe: Missing NetworkDevice class PinmemberRiyazAhemed4:18 26 Feb '09  
GeneralRe: Missing NetworkDevice class Pinmemberchmorgan11:39 26 Feb '09  
AnswerRe: Missing NetworkDevice class Pinmemberchmorgan8:30 27 Feb '09  
NewsSharpPcap is still active, v2.0.1 has been released, please visit http://sharppcap.sf.net SharpPcap now supports Mono Pinmemberchmorgan7:56 16 Feb '09  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 27 Nov 2005
Editor: Rinish Biju
Copyright 2005 by Tamir Gal
Everything else Copyright © CodeProject, 1999-2009
Web11 | Advertise on the Code Project