NAME
Win32::NetPacket - OO-interface to the WinPcap Packet Driver API.
SYNOPSIS
use Win32::NetPacket;
my $nic = Win32::NetPacket->new();
my ($name, $description, $type, $speed, $ip, $mask, $mac) = $nic->GetInfo();
print "Name: $name\n$description\nType: $type (speed: $speed bits/s)\n";
print "MAC: $mac IP: $ip Net mask: $mask\n";
DESCRIPTION
This module is an Object-Oriented interface to the Packet Driver API (Packet.dll). Packet.dll is a part of WinPcap: the Free Packet Capture Architecture for Windows. To use this module, it is necessary to install WinPcap 3.1 on your system (Go to "SEE ALSO" section).
Methods
- new
- $nic = Win32::NetPacket->new( [option => value] );
-
This method opens a network interface card (nic) adapter, creates a Win32::NetPacket object for this adapter and returns a reference to this object. If the constructor fails, undef will be returned and an error message will be in $@.
The options are passed in a hash like fashion, using key and value pairs. Possible options are:
adapter_name
Set the name of the network interface card adapter to open. If this option is not set, the adapter name returned by GetAdapterNames() is used by default.
The list of all network cards installed on the system can be gotten with the function GetAdapterNames() in a list context.
driver_buffer_size
Set the size, in bytes, of the driver’s circular buffer associated with the adapter. The default value is 256 kbytes. Can be changed later with the SetDriverBufferSize() method.
read_timeout
Set the timeout in milliseconds after which ReceivePacket() will return even though no packet has been captured. The default value is 1 seconde (1000 ms). Can be changed later with the SetReadTimeout() method.
min_to_copy
Set the minimum amount of data, in bytes, in the driver buffer that will cause the method ReceivePacket() to return. The default value is 0. Can be changed later with the SetMinToCopy() method. (Works on WinNT/2000/XP system only)
mode
Set the mode of the adapter: MODE_CAPT for standard capture mode or MODE_STAT for statistics mode. For more details, see SetMode() . The default value is MODE_CAPT.
Example :
use Win32::NetPacket ':mode'; my $nic = Win32::NetPacket->new( adapter_name => '\Device\NPF_{400FA737-5BA8-489F-9FF7-D74B4D3DAA72}', driver_buff_size => 512*1024, read_timeout => 0, min_to_copy => 16*1024, mode => MODE_CAPT ) or die $@;
- SetUserBuffer
- $nic->SetUserBuffer($Buffer, $size);
-
$Buffer
is the user-allocated buffer that will contain the captured data, and$size
is its size. This method returns nothing.Example:
my $buffer; $nic->SetUserBuffer($buffer, 256*1024); # 256 ko buffer for captured packets
- SetHwFilter
- $success = $nic->SetHwFilter( CONSTANT );
-
Sets a hardware filter on the incoming packets. The value returned is true if the operation was successful.
The constants that define the filters are:
NDIS_PACKET_TYPE_DIRECTED NDIS_PACKET_TYPE_MULTICAST NDIS_PACKET_TYPE_ALL_MULTICAST NDIS_PACKET_TYPE_BROADCAST NDIS_PACKET_TYPE_SOURCE_ROUTING NDIS_PACKET_TYPE_PROMISCUOUS NDIS_PACKET_TYPE_SMT NDIS_PACKET_TYPE_ALL_LOCAL NDIS_PACKET_TYPE_MAC_FRAME NDIS_PACKET_TYPE_FUNCTIONAL NDIS_PACKET_TYPE_ALL_FUNCTIONAL NDIS_PACKET_TYPE_GROUP
Example:
use Win32::NetPacket qw/ :ndis /; # NDIS_* constants available my $nic = Win32::NetPacket->new(); # open nic adapter $nic->SetHwFilter(NDIS_PACKET_TYPE_PROMISCUOUS); # set nic in promiscuous mode
- SetReadTimeout
- $success = $nic->SetReadTimeout( $timeout );
-
This method sets the value of the read timeout associated with the
$nic
adapter.$timeout
indicates the timeout in milliseconds after which ReceivePacket() will return (also if no packets have been captured by the driver). Setting timeout to 0 means no timeout, i.e. ReceivePacket() never returns if no packet arrives. A timeout of -1 causes ReceivePacket() to always return immediately.This method works also if the adapter is working in statistics mode, and can be used to set the time interval between two statistic reports.
- SetMinToCopy
- $success = $nic->SetMinToCopy( $nbytes )
-
This method can be used to define the minimum amount of data in the kernel buffer that will cause the driver to release a read (i.e. a ReceivePacket() ) in progress.
$nbytes
specifies this value in bytes.This method has effect only in Windows NT/2000. The driver for Windows 95/98/ME does not offer this possibility to modify the amount of data to unlock a read, therefore this call is implemented under these systems only for compatibility.
- ReceivePacket
- $BytesReceived = $nic->ReceivePacket();
-
This method performs the capture of a set of packets. Returns the length of the buffer’s portion containing valid data. The number of packets received with this method is variable. It depends on the number of packets actually stored in the driver’s buffer, on the size of these packets and on the size of the buffer associated with
$nic
. It is possible to set a timeout on read calls with the SetReadTimeout() method. In this case the call returns even if no packets have been captured if the timeout set by this method expires.The format used by the driver to send packets to the application is as follow:
packet #1 --> --------- ------ bpf_hdr structure ------ | bpf_hdr | ---> | tv_sec l = int | --------- | tv_usec l = int | | data | | bh_caplen I = unsigned int | --------- | bh_datalen I = unsigned int | | Padding | | bh_hdrlen S = unsigned short | packet #2 --> --------- ------------------------------- | bpf_hdr | --------- | data | --------- | Padding | --------- ...etc
Each packet has a header consisting in a
bpf_hdr
structure that defines its length and holds its timestamp. A padding field is used to word-align the data in the buffer (to increase the speed of the copies).The
bpf_hdr
has the following fields:tv_sec
capture date in the standard UNIX time format,
tv_usec
microseconds of the capture,
bh_caplen
the length of captured portion,
bh_datalen
the original length of the packet,
bh_hdrlen
the length of the header that encapsulates the packet.
For example, one can get the values of the first header of the user's buffer
$buffer
with:($tv_sec, $tv_usec, $caplen, $datalen, $hdrlen) = unpack 'llIIS', $buffer;
and then extract the first packet of this buffer with:
my $packet = substr $buffer, 0, $datalen;
Example: this script prints all successive packets of only one capture.
#!/usr/bin/perl -w use strict; use Win32::NetPacket qw/ :ndis /; use constant SizeOfInt => 4; # int = 4 bytes on Win32 my $nic = Win32::NetPacket->new( driver_buffer_size => 512*1024, # 512 kbytes buffer read_timeout => 0, # no timeout min_to_copy => 8*1024, # return if > 8 kbytes ) or die $@; $nic->SetHwFilter(NDIS_PACKET_TYPE_PROMISCUOUS); # nic in promiscuous mode my $Buff; $nic->SetUserBuffer($Buff, 128*1024); # 128 kbytes user's buffer my $BytesReceived = $nic->ReceivePacket(); # capture packets my $offset = 0; while($offset < $BytesReceived) { my ($tv_sec, $tv_usec, $caplen, $datalen, $hdrlen) = unpack 'llIIS', substr $Buff, $offset; # read the bpf_hdr structure printf "\nPacket length, captured portion: %ld, %ld\n", $datalen, $caplen; $offset += $hdrlen; my $data = substr $Buff, $offset, $datalen; # extract the datagram my $i; print map { ++$i % 16 ? "$_ " : "$_\n" } # print the datagram in hexa unpack( 'H2' x length( $data ), $data ), length( $data ) % 16 ? "\n" : ''; # The next packet is at $offset + $caplen + 0, 1, 2 or 3 padding bytes # i.e. $offset must be a multiple of SizeOfInt (word alignment) $offset = (($offset+$caplen)+(SizeOfInt-1)) & ~(SizeOfInt-1); }
Really raw packets, is not it? ;-)
- GetStats
- ($packets_received, $packets_lost) = $nic->GetStats();
-
Returns, in a list, the number of packets that have been received by the adapter, starting at the time in which it was opened and the number of packets received by the adapter but that have been dropped by the kernel. A packet is dropped when the user-level application is not ready to get it and the kernel buffer associated with the adapter is full.
- GetInfo
- ($name, $description, $type, $speed, $ip, $mask, $mac) = $nic->GetInfo();
-
Returns, in a list, the name, the description string, the type, the speed in bits per second, the IP address, the net mask and the MAC address of
$nic
The type is one of the following values:
NdisMedium802_3: Ethernet (802.3) NdisMedium802_5: Token Ring (802.5) NdisMediumFddi: FDDI NdisMediumWan: WAN NdisMediumLocalTalk: LocalTalk NdisMediumDix: DIX NdisMediumAtm: ATM NdisMediumArcnetRaw: ARCNET (raw) NdisMediumArcnet878_2: ARCNET (878.2) NdisMediumWirelessWan: Various types of NdisWirelessXxx media.
- SetMode
- $success = $nic->SetMode( MODE );
-
This method sets the mode of the adapter. MODE can have two possible values:
MODE_CAPT: standard capture mode.
It is set by default after the PacketOpenAdapter call.
MODE_STAT: statistics mode.
It's a particular working mode of the BPF capture driver that can be used to perform real time statistics on the network traffic. The driver does not capture anything when in statistics mode and it limits itself to count the number of packets and the amount of bytes that satisfy the user-defined BPF filter. These counters can be obtained by the application with the ReceivePacket() method, and are received at regular intervals, every time a timeout expires. The default value of this timeout is 1 second, but it can be set to any other value (with a 1 ms precision) with the SetReadTimeout() method. The counters are encapsulated in a
bpf_hdr
structure before being passed to the application. This allows microsecond-precise timestamps in order to have the same time scale among the data capture in this way and the one captured using libpcap. Captures in this mode have a very low impact with the system performance.The data returned by PacketReceivePacket() when the adapter is in this mode is as follow:
-------- bpf_hdr structure --------- | tv_sec l = int | | tv_usec l = int | | bh_caplen I = unsigned int | | bh_datalen I = unsigned int | | bh_hdrlen S = unsigned short | -------- data ---------------------- | PacketsAccepted LL = large int | | BytesAccepted LL = large int | ------------------------------------
The buffer is 34 bytes long under Win9x and 36 bytes long under WinNT (there is 2 padding bytes after bpf_hdr structure). A
Large int
is a 64 bits integer.Example: this script prints the number of bytes received every second. Note the two padding bytes (xx) in the unpack template.
#!/usr/bin/perl -w use strict; use Term::ReadKey; use Win32::NetPacket qw/ :ndis MODE_STAT /; my $nic = Win32::NetPacket->new( driver_buff_size => 0, # no buffer needed read_timeout => 1000, # every second mode => MODE_STAT, # statistics mode ) or die $@; $nic->SetHwFilter(NDIS_PACKET_TYPE_PROMISCUOUS); # set nic in promiscuous mode my $Buff; $nic->SetUserBuffer($Buff, 36); # 36 bytes user's buffer, it's enough # 2 padding bytes (xx) in the bpf_hdr structure under WinNT my $bpf_hdr = Win32::IsWinNT() ? "llIISxxLLLL" : "llIISLLLL"; while( !ReadKey(-1) ) { # press (enter) to terminate $nic->ReceivePacket(); # get stats my ($tv_sec, $tv_usec, $caplen, $datalen, $hdrlen, $p0, $p1, $b0, $b1) = unpack $bpf_hdr, $Buff; # read stats print $b1*2**32+$b0, " bytes/s\n"; }
- SetDriverBufferSize
- $success = $nic->SetDriverBufferSize( $size );
-
This method sets to a new size the driver’s buffer associated with the
$nic
adapter.$size
is the new dimension in bytes. Returns a true value if successfully completed, a false value if there is not enough memory to allocate the new buffer. When a new dimension is set, the data in the old buffer is discarded and the packets stored in it are lost. - SendPacket
- $success = $nic->SendPacket( $packet )
-
This method is used to send a raw
$packet
to the network through the$nic
adapter . "Raw packet" means that the programmer will have to build the various headers because the packet is sent to the network "as is". The user will not have to put abpf_hdr
header before the packet. Either the CRC needs not to be calculated and added to the packet, because it is transparently put after the end of the data portion by the network interface.The optimised sending process is still limited to one packet at a time: for the moment it cannot be used to send a buffer with multiple packets.
- SetNumWrites
- $success = $nic->SetNumWrites( $nwrites );
-
This method sets to
$nwrites
the number of times a single write on the$nic
adapter must be repeated. See SendPacket() for more details. - GetRequest
- $success = $nic->GetRequest( $Oid );
-
This method is used to perform a query operation on the
$nic
adapter. With this method it is possible to obtain various parameters of the network adapter, like the dimension of the internal buffers, the link speed or the counter of corrupted packets.NDIS (Network Device Interface Specification) Object IDentifiers (OIDs) are a set of system-defined constants that take the form OID_XXX. To call GetRequest() or SetRequest() methods, it is necessary to set these OIDs in an OID-structure:
-------- OID structure -------- | Oid L = unsigned long | | Length L = unsigned long | | Data C* = unsigned char [] | -------------------------------
Oid
Oid is a numeric identifier that indicates the type of query/set function to perform on the adapter,
Length
Length indicates the length of the Data field,
Data
Data field, that contains the information passed to or received from the adapter.
The constants that define the Oids are declared in the file ntddndis.h. More details on the argument can be found in the documentation provided with the Microsoft DDK.
Example: The OID_GEN_VENDOR_DESCRIPTION OID points to a zero-terminated string describing the
$nic
. We define, with pack, an OID-structure that contains the OID and a small buffer of 256 characters. After the call to GetRequest(), we unpack the structure to get the string$description
(its length is$len
).use Win32::NetPacket qw/ :oid /; my $nic = Win32::NetPacket->new(); my $Oid = pack "LLC256", OID_GEN_VENDOR_DESCRIPTION, 256; $nic->GetRequest($Oid) or die "Unable to get OID_GEN_VENDOR_DESCRIPTION"; my ($code, $len, $description) = unpack "LLA*", $Oid; print $description;
Not all the network adapters implement all the query/set functions. There is a set of mandatory OID functions that is granted to be present on all the adapters, and a set of facultative functions, not provided by all the adapters (see the DDK to see which functions are mandatory).
Example: OID_GEN_SUPPORTED_LIST is a mandatory OID that specifies an array of OIDs that the underlying driver or its NIC supports. The following script prints the list of all OIDs supported by the default
$nic
.#!/usr/bin/perl -w use strict; use Win32::NetPacket qw/ :oid /; my %OIDTAG; # for conversion OID_num --> OID_tag foreach my $tag ( @{ $Win32::NetPacket::EXPORT_TAGS{'oid'} } ) { my $hexa = scalar Win32::NetPacket::constant($tag); if ( $hexa !~ /Your vendor/ ) { $hexa = sprintf "0x%08X", $hexa; $OIDTAG{$hexa} = $tag unless $tag =~ /^OID_GEN_CO_/; } } my $nic = Win32::NetPacket->new(); my $Oid = pack "LLC1024", OID_GEN_SUPPORTED_LIST, 1024; $nic->GetRequest($Oid) or die "Unable to get OID_GEN_SUPPORTED_LIST"; my ( $code, $num, @supported ) = unpack "LLL*", $Oid; foreach (@supported) { last unless $_; my $hexa = sprintf "0x%08X", $_; printf "$hexa == %s\n", exists $OIDTAG{$hexa} ? $OIDTAG{$hexa} : '??'; }
- SetRequest
- $success = $nic->SetRequest( $Oid );
-
This method is used to perform a set operation on the adapter pointed by
$nic
. For the OID-structure, see GetRequest() .Example: with the OID_GEN_CURRENT_PACKET_FILTER one can set an hardware filter like with SetHwFilter() . To set
$nic
in promiscuous mode:use Win32::NetPacket qw/ :oid :ndis /; my $nic = Win32::NetPacket->new(); my $Oid = pack "LLL", OID_GEN_CURRENT_PACKET_FILTER , NDIS_PACKET_TYPE_PROMISCUOUS ; $nic->SetRequest($Oid) or die "Unable to set OID_GEN_CURRENT_PACKET_FILTER";
- SetBpf
- $success = $nic->SetBpf( @filter );
- $success = $nic->SetBpf( $filter );
-
This method associates a new BPF filter to the
$nic
adapter. The@filter
is a set of instructions that the BPF register-machine of the driver will execute on each incoming packet. This method returns true if the driver is set successfully, false if an error occurs or if the filter program is not accepted. The driver performs a check on every new filter in order to avoid system crashes due to bogus or buggy programs, and it rejects invalid filters.You can launch WinDump with the -ddd parameter to obtain the pseudocode of the filter.
Example: suppose that you want a filter for the tcp packets having the flags SYN and FIN set (why not? ;-). In a console, the command:
windump -ddd "tcp[13] & 3 = 3"
give you the pseudocodes of the filter:
12 40 0 0 12 21 0 9 2048 48 0 0 23 21 0 7 6 40 0 0 20 69 5 0 8191 177 0 0 14 80 0 0 27 84 0 0 3 21 0 1 3 6 0 0 96 6 0 0 0
then you can set this filter:
my @filter = qw/ 12 40 0 0 12 21 0 9 2048 48 0 0 23 21 0 7 6 40 0 0 20 69 5 0 8191 177 0 0 14 80 0 0 27 84 0 0 3 21 0 1 3 6 0 0 96 6 0 0 0 /; $nic->SetBpf(@filter) or die "Unable to set Bpf filter";
You can also pack this filter:
my $filter = pack 'SCCi'x12, qw/40 0 0 12 21 0 9 2048 48 0 0 23 21 0 7 6 40 0 0 20 69 5 0 8191 177 0 0 14 80 0 0 27 84 0 0 3 21 0 1 3 6 0 0 96 6 0 0 0 /;
and then set it:
$nic->SetBpf($filter) or die "Unable to set Bpf filter";
Auxiliary functions
- GetAdapterNames
- @list = GetAdapterNames( [ \%description ] );
- $card = GetAdapterNames( [ \%description ] );
-
In a list context, return the names of all network cards installed on the system. In a scalar context, return the first name of the list.
If you give a reference to a hash
%description
, this hash will establish an association between the system name of the adapter and a "human readable" description of it.Example:
use Win32::NetPacket 'GetAdapterNames'; my %description; foreach ( GetAdapterNames(\%description) ) { print "* $description{$_}\n $_\n\n"; }
- GetAdapterInfo
- ($ip, $mask) = GetNetInfo( $adapter_name )
-
Returns the IP address and the netmask of the named adapter.
Example:
use Win32::NetPacket qw/ GetAdapterNames GetNetInfo /; my $default_adapter = GetAdapterNames(); my ($ip, $mask) = GetNetInfo( $default_adapter ); print "NIC: $default_adapter\nIP: $ip MASK: $mask\n"
- GetVersion
- $version = GetVersion();
-
Returns a string with the packet.dll version.
Example:
use Win32::NetPacket 'GetVersion'; print GetVersion();
Export
By default, Win32::NetPacket exports no symbols into the callers namespace. The following tags can be used to selectively import symbols into the main namespace.
- :mode
-
Exports all symbols
MODE_*
. See SetMode() method. - :ndis
-
Exports all symbols
NDIS_*
. See SetHwFilter() method. - :oid
-
Exports all symbols
OID_*
. See GetRequest() and SetRequest() methods.
SEE ALSO
Win32::NetPacket Home Page: http://www.bribes.org/perl/wNetPacket.html
WinPCap Home Page: http://www.winpcap.org/default.htm
WinPCap download page (download and install the WinPcap 3.1 auto-installer (driver +DLLs):
http://www.winpcap.org/install/default.htm
WinDump (tcpdump for Windows):
http://www.winpcap.org/windump/default.htm
Microsoft DDK doc (NDIS OID):
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/NetXP_r/hh/NetXP_r/21oidovw_b5d8c785-211e-4d39-8007-1d38b3a1c888.xml.asp
(Search for "NDIS Object Identifiers" if this link is broken.)
CREDITS
This module uses WinPCap, a software developed by the Politecnico di Torino, and its contributors.
Licence: http://www.winpcap.org/misc/copyright.htm
AUTHOR
J-L Morel <jl_morel@bribes.org>
COPYRIGHT
Copyright (c) 2003-2006 J-L Morel. All rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
1 POD Error
The following errors were encountered while parsing the POD:
- Around line 626:
Non-ASCII character seen before =encoding in 'driver’s'. Assuming CP1252