Win32::NetPacket - OO-interface to the WinPcap Packet Driver API. |
Win32::NetPacket - OO-interface to the WinPcap Packet Driver API.
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";
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).
The options are passed in a hash like fashion, using key and value pairs. Possible options are:
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.
SetDriverBufferSize()
method.
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.
ReceivePacket()
to return. The default value is 0.
Can be changed later with the SetMinToCopy()
method.
(Works on WinNT/2000/XP system only)
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 $@;
$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
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
$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.
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.
$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:
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? ;-)
$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.
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"; }
$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.
$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 a bpf_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.
$nwrites
the number of times a single write on the $nic
adapter must be repeated. See SendPacket()
for more details.
$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 [] | -------------------------------
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} : '??'; }
$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";
$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";
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"; }
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"
Example:
use Win32::NetPacket 'GetVersion'; print GetVersion();
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_*
. See SetMode()
method.
NDIS_*
. See SetHwFilter()
method.
OID_*
. See GetRequest()
and SetRequest()
methods.
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.)
This module uses WinPCap, a software developed by the Politecnico di Torino, and its contributors.
Licence: http://www.winpcap.org/misc/copyright.htm
J-L Morel <jl_morel@bribes.org>
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.
Win32::NetPacket - OO-interface to the WinPcap Packet Driver API. |