Channels ▼
RSS

Web Development

Monitoring Network Traffic with Net::Pcap


July, 2004: Monitoring Network Traffic with Net::Pcap

Rob is technical manager for Bluebottle Solutions, a verification technology company based in Melbourne, Australia. He can be reached at rob.casey@bluebottle.com.


Throughout its history, Perl has always found a home in the suite of tools employed by systems administrators in the maintenance, monitoring, and administration of computer systems. As the needs of systems administrators have grown, Perl has kept pace with an ever-expanding base of diverse application and tool components available from the comprehensive Perl archive network (CPAN, http://www.cpan.org/).

One tool available from CPAN of particular utility to systems administrators is the Net::Pcap library. This module provides an interface for Perl to the Lawrence Berkeley National Laboratory Network Research Group's pcap library, which is a system-independent interface for user-level packet capture. This library provides a portable framework for low-level network monitoring and can be used for a variety of network monitoring functions, including network statistics collection, security monitoring, and network debugging.

Setting the Device

The first step in building an application or tool using Net::Pcap and the underlying libpcap library for network monitoring is to determine an available network interface that can be used for monitoring this network traffic. This device can be specified by the user for specific network monitoring, particularly on multihomed machines, or can be determined via the lookupdev method of Net::Pcap.

The syntax of the lookupdev method is as follows:

$dev = Net::Pcap::lookupdev(\$err)

This method returns the name of a network device, which can be used for monitoring network traffic. For example:

use Net::Pcap;
use strict;

my $err;
my $dev = Net::Pcap::lookupdev(\$err);
if (defined $err) {
    die 'Unable to determine network device for monitoring - ', $err;
}

The string reference $err is passed as an argument to this method and is returned with an error description in the event of method failure. Upon method failure, the returned device name is also undefined.

With the latest version of the Net::Pcap module (0.05), a list of all network devices that can be used for network monitoring can be retrieved using the findalldevs function. The syntax of this function is similar to that of the lookupdev function:

@devs = Net::Pcap::findalldevs(\$err)

Another useful method available from the Net::Pcap library is lookupnet, which can be used to determine the network address and netmask for a device. This method is useful for the validation of a device name supplied for network monitoring by a user.

The syntax of the lookupnet method is as follows:

Net::Pcap::lookupnet($dev, \$net, \$mask, \$err)

This method returns the network address and netmask for the device specified—$dev. This method also follows the conventions of the underlying library of returning 0 for success and -1 for failure and, as such, error checking for this and other Net::Pcap functions may use the pseudoreverse mentality of the die if... idiom. For example:

my ($address, $netmask, $err);
if (Net::Pcap::lookupnet($dev, \$address, \$netmask, \$err)) {
    die 'Unable to look up device information for ', $dev, ' - ', $err;
}
print STDOUT "$dev: addr/mask -> $addr/$mask\n";

Capturing Packets

Once an appropriate network device has been determined, the process of packet capturing can be initiated. The Net::Pcap function open_live returns a packet capture descriptor, which can be used for capturing and examining network packets.

The syntax of the open_live method is as follows:

$object = Net::Pcap::open_live($dev, $snaplen, $promisc, $to_ms, \$err)

The $dev parameter specifies the network interface from which to capture network packets while the $snaplen and $promisc parameters specify the maximum number of bytes to capture from each packet and whether to put the interface into promiscuous mode, respectively. The latter of these parameters, the promiscuous mode, places the network card into a "snooping" mode where network packets not necessarily directed to the packet-capturing machine are captured. In a nonswitched network environment, this can capture all network traffic. The $to_ms parameter specifies a read time-out for packet capturing in milliseconds—a $to_ms value of 0 captures packets until an error occurs, while a value of -1 captures packets indefinitely.

Individual packets can be retrieved from the network interface using the next method of the Net::Pcap module in the following manner:

$packet = Net::Pcap::next($object, \%header)

This method will return the next packet available on the network interface registered with the Net::Pcap packet descriptor object. The %header hash reference is populated with details relating to packet header information, namely:

  • len, the total length of the packet.
  • caplen, the captured length of the packet; this corresponds to the $snaplen argument passed to the Net::Pcap::open_live method.
  • tv_sec, the seconds value of the packet timestamp.
  • tv_usec, the microseconds value of the packet timestamp.

If no packets are available on the network interface for capture, the return value of the Net::Pcap::next method is undefined.

Alternatively, continuous packet capture can be performed by establishing and registering a callback function to which Net::Pcap can pass captured packets to for analysis and reporting. For this, the loop method of Net::Pcap is called:

Net::Pcap::loop($object, $count, \&callback_function, $user_data)

This method takes four mandatory arguments. $object is the Net::Pcap object returned from the Net::Pcap::open_live method. $count is a number indicating the number of packets to capture. If the number passed to this function is negative, Net::Pcap::loop will capture packets indefinitely (or until an error occurs if the $to_ms argument of the open_live method is set to 0). The third parameter is a subroutine reference to the callback function. The fourth argument represents arbitrary data that is passed with the callback function (along with captured packets) and can be used as a method to tag captured packets or distinguish between several open-packet capture sessions.

The callback function specified by the Net::Pcap::loop method receives the following arguments when called:

  • The $user_data string passed to the Net::Pcap::loop method.
  • A reference to a hash containing packet header information (as described in association to the Net::Pcap::next method above).
  • A copy of the entire packet.

An example of the callback function associated with packet capture may look like the following:

sub callback_function {
    my ($user_data, $header, $packet) = @_;

    ...
}

One important limitation of this module in its current version, however, is that only a single callback function and user data scalar can be registered at any given time, as both elements are stored within global variables within the Net::Pcap namespace.

Filtering Packets

While the previously methods described provide the means by which to capture all network traffic, the real power offered by the libpcap library is to selectively filter network packets to monitor specific traffic. The filtering of network packets can be set through the use of a filter language specific to the libpcap library. A description of this filter language can be found in the libpcap source code or on the tcpdump(8) man page. The use of this filter language for the selective capture of network packets does require some knowledge of TCP/IP networking and the underlying packet structure, and a detailed description of this filter language would be beyond the scope of this article.

The Net::Pcap module provides methods for the compilation and setting of filters for network packet capture by means of the Net::Pcap::compile and Net::Pcap::setfilter methods.

The arguments of the Net::Pcap::compile method are as follows:

Net::Pcap::compile($object, \$filter_compiled, $filter_string, $optimize, $netmask)

This method will compile and check the filter specified in $filter_string for the Net::Pcap object $object and return the compiled filter in the scalar $filter_compiled. The filter is optimized where possible if the $optimize variable is True. This function, like other Net::Pcap functions, returns 0 if successful or -1 if an error occurs.

The compiled filter string, $filter_compiled, can then be applied against the Net::Pcap object using the Net::Pcap::setfilter method. For example:

Net::Pcap::setfilter($object, $filter_compiled)

Decoding Captured Packets

Once packets have been captured using the Net::Pcap interface to libpcap, the next step is to decode these packets and make sense of the network packet data collected. This can be performed by constructing unpack templates for captured data or (more easily) through the NetPacket:: collection of modules. These modules each contain methods for extracting information from and about network packets, the most useful of which is (arguably) the decode method. This method returns a hash of metadata about the passed packet specific to the packet type.

For example, the NetPacket::Ethernet::decode method will return the following information on captured ethernet packets:

  • src_mac, the source MAC address for the Ethernet packet as a hex string.
  • dest_mac, the destination MAC address for the Ethernet packet as a hex string.
  • type, the protocol type of the Ethernet packet; for example, IP, ARP, PPP, and SNMP.
  • data, the data payload for the Ethernet packet.

Further information on each of the NetPacket:: modules and the information returned by the decode function can be found on their respective man pages.

In addition to this, each of the NetPacket:: modules also contain a strip method, which simply returns the data payload of the network packet. This is useful when the network encapsulation is of little or no concern to your application.

Error Handling

While scalar references can be passed as arguments to a number of the Net::Pcap methods (namely lookupdev, findalldevs, lookupnet, and open_live), in order to return error messages, there are a number of dedicated methods supplied by this module for more generic error handling:

Net::Pcap::geterr($object)
Net::Pcap::perror($object, $prefix)
Net::Pcap::strerror($errno);
The <i>Net::Pcap::geterr</i> function returns an error message for the last error associated with the packet capture object <i>$object</i>. The <i>Net::Pcap::perror</i> function prints the test of the last error associated with the packet capture object <i>$object</i> on standard error prefixed by the string in <i>$prefix</i>. <i>Net::Pcap::strerror</i> returns a string-describing error number <i>$errno</i>.

Cleaning Up

Once finished capturing packets, the Net::Pcap::close method should be called to close the packet capture device. For example:

Net::Pcap::close($object)

Putting It All Together

Listing 1 shows a way of putting all of the aforementioned techniques to use for network administration. In this example, details of all TCP packets with the SYN header flag set captured by a machine will be reported. These network packets are used by a client in initiating a connection with a server and can be used to initiate denial of service attacks against a network host. For further information on TCP packet structure and the SYN header flag, see RFC793.

From Here

This article has touched upon the basic functionality of the Net::Pcap module and how it can be used in network administration. Other features of this excellent module that have not been covered in this article include saving captured network packets to files and interface statistics handling. These features are well described in the documentation for this module, and readers should have no trouble employing these extended features of the Net::Pcap module.

References

Blank-Edelman, David N. Perl for System Administration, O'Reilly, 2000, ISBN 1-56592-609-9.

TPJ



Listing 1

use Net::Pcap;
use NetPacket::Ethernet;
use NetPacket::IP;
use NetPacket::TCP;
use strict;

my $err;

#   Use network device passed in program arguments or if no 
#   argument is passed, determine an appropriate network 
#   device for packet sniffing using the 
#   Net::Pcap::lookupdev method

my $dev = $ARGV[0];
unless (defined $dev) {
    $dev = Net::Pcap::lookupdev(\$err);
    if (defined $err) {
        die 'Unable to determine network device for monitoring - ', $err;
    }
}

#   Look up network address information about network 
#   device using Net::Pcap::lookupnet - This also acts as a 
#   check on bogus network device arguments that may be 
#   passed to the program as an argument

my ($address, $netmask);
if (Net::Pcap::lookupnet($dev, \$address, \$netmask, \$err)) {
    die 'Unable to look up device information for ', $dev, ' - ', $err;
}

#   Create packet capture object on device

my $object;
$object = Net::Pcap::open_live($dev, 1500, 0, 0, \$err);
unless (defined $object) {
    die 'Unable to create packet capture on device ', $dev, ' - ', $err;
}

#   Compile and set packet filter for packet capture 
#   object - For the capture of TCP packets with the SYN 
#   header flag set directed at the external interface of 
#   the local host, the packet filter of '(dst IP) && (tcp
#   [13] & 2 != 0)' is used where IP is the IP address of 
#   the external interface of the machine.  For 
#   illustrative purposes, the IP address of 127.0.0.1 is 
#   used in this example.

my $filter;
Net::Pcap::compile(
    $object, 
    \$filter, 
    '(dst 127.0.0.1) && (tcp[13] & 2 != 0)', 
    0, 
    $netmask
) && die 'Unable to compile packet capture filter';
Net::Pcap::setfilter($object, $filter) &&
    die 'Unable to set packet capture filter';

#   Set callback function and initiate packet capture loop

Net::Pcap::loop($object, -1, \&syn_packets, '') ||
    die 'Unable to perform packet capture';

Net::Pcap::close($object);


sub syn_packets {
    my ($user_data, $header, $packet) = @_;

    #   Strip ethernet encapsulation of captured packet 

    my $ether_data = NetPacket::Ethernet::strip($packet);

    #   Decode contents of TCP/IP packet contained within 
    #   captured ethernet packet

    my $ip = NetPacket::IP->decode($ether_data);
    my $tcp = NetPacket::TCP->decode($ip->{'data'});

    #   Print all out where its coming from and where its 
    #   going to!

    print
        $ip->{'src_ip'}, ":", $tcp->{'src_port'}, " -> ",
        $ip->{'dest_ip'}, ":", $tcp->{'dest_port'}, "\n";
}
Back to article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 
Dr. Dobb's TV