The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

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 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.

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