perltutorial
rob_au
<p>
<font size="+1"><b>Introduction</b></font><p>
<p>
Throughout its history, Perl has always found a home in the suite of tools employed by system administrators in the maintenance, monitoring and administration of computer systems. As the diversity of demands and requirements placed upon computer systems has grown, so too has the requirement for system administrators to take full advantage of the tools at their disposal. In this respect, Perl has proven to be a most valuable asset to system administrators with an ever expanding base of diverse application and tool components available from the comprensive perl archive network (CPAN, [http://www.cpan.org]).<p>
This tutorial is intended to introduce the reader to another such component available from CPAN, 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. Thus, it is with a focus on network administration that this tutorial on the usage of [cpan://Net::Pcap] is presented.<p>
<p><readmore>
<font size="+1"><b>Setting the Device</b></font><p>
<p>
The first step in building an application or tool using [cpan://Net::Pcap] and the underlying libpcap library for network monitoring is to determine an available network interface which can be used for this monitoring network traffic. This device can be specified by the user for specific network monitoring, particularly on multi-homed machines, or can be determined by the <code>lookupdev</code> method from [cpan://Net::Pcap].<p>
The syntax of the <code>lookupdev</code> method is as follows:<p>
<dl><dd><code>
$dev = Net::Pcap::lookupdev(\$err)
</code></dd></dl><p>
This method returns the name of a network device which can be used for monitoring network traffic - For example:<p>
<dl><dd><code>
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;
}
</code></dd></dl><p>
The string reference <code>$err</code> 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.<p>
A second method from [cpan://Net::Pcap] which warrants introduction at this point is <code>lookupnet</code>, 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.<p>
The syntax of the <code>lookupnet</code> method is as follows:<p>
<dl><dd><code>
Net::Pcap::lookupnet($dev, \$net, \$mask, \$err)
</code></dd></dl><p>
This method returns the network address and netmask for the device specified, <code>$dev</code>. This method also follows the conventions of the underlying library of returning <code>0</code> for success and <code>-1</code> for failure and as such error checking for this and other [cpan://Net::Pcap] functions may use the pseudo-reverse mentality of the <code>die if ..</code> idiom. For example:<p>
<dl><dd><code>
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";
</code></dd></dl><p>
<p>
<font size="+1"><b>Capturing Packets</b></font><p>
<p>
Once an appropriate network device has been determined, the process of packet capturing can be initiated. The [cpan://Net::Pcap] function <code>open_live</code> returns a packet capture descriptor which can be used for capturing and examining network packets.<p>
The syntax of the <code>open_live</code> method is as follows:<p>
<dl><dd><code>
$object = Net::Pcap::open_live($dev, $snaplen, $promisc, $to_ms, \$err)
</code></dd></dl><p>
The <code>$dev</code> parameter specifies the network interface from which to capture network packets while the <code>$snaplen</code> and <code>$promisc</code> 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 non-switched network environment, this could effectively capture all network traffic! The <code>$to_ms</code> parameter specifies a read time-out for packet capturing in milliseconds - A <code>$to_ms</code> value of <code>0</code> captures packets until an error occurs while a value of <code>-1</code> captures packets indefinitely.<p>
The next step in the packet capture process is to establish a callback function for [cpan://Net::Pcap] to pass captured packets to for analysis and reporting - For this, the <code>loop</code> method of [cpan://Net::Pcap] is called:<p>
<dl><dd><code>
Net::Pcap::loop($object, $count, \&callback_function, $user_data)
</code></dd></dl><p>
This method takes four mandatory arguments, the [cpan://Net::Pcap] object returned from the <code>Net::Pcap::open_live</code> method, <code>$object</code>, a numeric indicating the number of packets to capture, <code>$count</code> and a subroutine reference to the callback function. If the numeric passed to this function is negative, the <code>Net::Pcap::loop</code> will capture packets indefinitely (or until an error occurs if the <code>$to_ms</code> argument of the <code>open_live</code> method is set to <code>0</code>). The fourth argument passed to this method with arbitrary data that is passed with the callback function with captured packets and can be used as a method to 'tag' captured packets or distinguish between several open packet capture sessions.<p>
The callback function specified by the <code>Net::Pcap::loop</code> method receives the following arguments when called:<p>
<dl><ul>
<li>The <code>$user_data</code> string passed to the <code>Net::Pcap::loop</code> method.</li><p>
<li>A reference to a hash containing packet header information - The fields of this hash of packet header information are as follows:<p>
<dl><ul>
<li><code>len</code> - the total length of the packet,</li><p>
<li><code>caplen</code> - the captured length of the packet; this corresponds to the <code>$snaplen</code> argument passed to the <code>Net::Pcap::open_live</code> method</li><p>
<li><code>tv_sec</code> - the seconds value of the packet timestamp</li><p>
<li><code>tv_usec</code> - the microseconds value of the packet timestamp</li><p>
</ul></dl></li><p>
<li>A copy of the entire packet</li><p>
</ul></dl><p>
An example of the callback function associated with packet capture may look like the following:<p>
<dl><dd><code>
sub callback_function {
my ($user_data, $header, $packet) = @_;
...
}
</code></dd></dl><p>
<p>
<font face="+1"><b>Filtering Packets</b></font><p>
<p>
While the methods described above, provide the means by which to capture all network traffic, the real power offered by the libpcap library is to selective filter network packets to monitor specific traffic. The filtering of network packets can be set through 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 <code>tcpdump(8)</code> 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 - The description of this filter language in detail is beyond the scope of this tutorial.<p>
The [cpan://Net::Pcap] module provides methods for the compilation and setting of filters for network packet capture by means of the <code>Net::Pcap::compile</code> and <code>Net::Pcap::setfilter</code> methods.<p>
The arguments of the <code>Net::Pcap::compile</code> method are as follows:<p>
<dl><dd><code>
Net::Pcap::compile($object, \$filter_compiled, $filter_string, $optimise, $netmask)
</code></dd></dl><p>
This method will compile and check the filter specified in $filter_string for [cpan://Net::Pcap] object <code>$object</code> and return the compiled filter in the scalar <code>$filter_compiled</code>. The filter is optimised where possible if the <code>$optimise</code> variable is true. This function, like other [cpan://Net::Pcap] functions, returns <code>0</code> if successful or <code>-1</code> if an error occurs.<p>
The compiled filter string, <code>$filter_compiled</code>, can then be applied against the [cpan://Net::Pcap] object using the <code>Net::Pcap::setfilter</code> method - For example:<p>
<dl><dd><code>
Net::Pcap::setfilter($object, $filter_compiled);
</code></dd></dl><p>
<p>
<font face="+1"><b>Decoding Captured Packets</b></font><p>
<p>
Once packets have been captured using the [cpan://Net::Pcap] interface to libpcap, the next step is to decode this 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 [cpan://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 <code>decode</code> method - This method returns a hash of meta-data about the passed packet, specific to the packet type.<p>
For example, the <code>NetPacket::Ethernet::decode</code> method will return the following information on captured ethernet packets:<p>
<dl><ul>
<li><code>src_mac</code> - the source MAC address for the ethernet packet as a hex string</li><p>
<li><code>dest_mac</code> - the destination MAC address for the ethernet packet as a hex string</li><p>
<li><code>type</code> - the protocol type of the ethernet packet, for example, IP, ARP, PPP, SNMP</li><p>
<li><code>data</code> - the data payload for the ethernet packet</li>
</ul></dl><p>
Further information on each of the [cpan://NetPacket::] modules and the information returned by the decode function can be found on their respective man pages.<p>
In addition to this, each of the [cpan://NetPacket::] modules also contain a <code>strip</code> 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.<p>
<p>
<font face="+1"><b>Cleaning Up</b></font><p>
<p>
Once finished capturing packets, the <code>Net::Pcap::close</code> method should be called to close the packet capture device. For example:<p>
<dl><dd><code>
Net::Pcap::close($object)
</code></dd></dl><p>
<p>
<font face="+1"><b>Putting It All Together</b></font><p>
<p>
The following example shows a way of putting all of the techniques described above and putting them 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 [http://www.ibiblio.org/pub/docs/rfc/rfc793.txt|RFC793].<p>
<dl><dd><code>
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";
}
</code></dd></dl><p>
<p>
<font face="+1"><b>From Here</b></font><p>
<p>
This tutorial has touched upon the basic functionality of the [cpan://Net::Pcap] module and how it can be used in network administration. Other features of this excellent module which have not been covered in tutorial include saving captured network packets to files and interface statistics handling - These features will be covered, if there is sufficient interest and demand, in a follow-up tutorial.<p>
<p>
<font face="+1"><b>References</b></font><p>
<p>
Blank-Edelman, David N. (2000) "Perl for System Administration" O'Reilly, ISBN 1-56592-609-9<p>
Carstens, Tim (2002) "Programming with pcap" [http://broker.dhs.org/pcap.html]<p>
Casado, Martin "Packet Capture with libpcap and other Low Level Network Tricks" [http://www.dbaseiv.net/sockets/pcap/Tutorials/section1.html]<p>
<p>