Win32::NetPacket - OO-interface to the WinPcap Packet Driver API.


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:

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:

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:
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 [] |
         -------------------------------

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.

 Win32::NetPacket - OO-interface to the WinPcap Packet Driver API.