class PCAPRUB::Pcap

Constants

DLT_AIRONET_HEADER
DLT_ARCNET
DLT_ATM_RFC1483
DLT_AX25
DLT_BLUETOOTH_HCI_H4
DLT_BLUETOOTH_HCI_H4_WITH_PHDR
DLT_CHAOS
DLT_DOCSIS
DLT_EN10MB
DLT_EN3MB
DLT_FDDI
DLT_IEEE802
DLT_IEEE802_11
DLT_IEEE802_11_RADIO
DLT_IEEE802_11_RADIO_AVS
DLT_IPFILTER
DLT_LINUX_SLL
DLT_LOOP
DLT_NULL
DLT_PPP
DLT_PPP_BSDOS
DLT_PRISM_HEADER
DLT_PRONET
DLT_RAW
DLT_SLIP
DLT_SLIP_BSDOS
DLT_USB
DLT_USB_LINUX
PCAP_ERROR

Pcap Error Codes Error codes for the pcap API. These will all be negative, so you can check for the success or failure of a call that returns these codes by checking for a negative value.

PCAP_ERROR_ACTIVATED
PCAP_ERROR_BREAK
PCAP_ERROR_IFACE_NOT_UP
PCAP_ERROR_NOT_ACTIVATED
PCAP_ERROR_NOT_RFMON
PCAP_ERROR_NO_SUCH_DEVICE
PCAP_ERROR_PERM_DENIED
PCAP_ERROR_RFMON_NOTSUP
PCAP_NETMASK_UNKNOWN

Value to pass to pcap_compile() as the netmask if you don't know what the netmask is.

PCAP_WARNING

Warning codes for the pcap API. These will all be positive and non-zero, so they won't look like errors.

PCAP_WARNING_PROMISC_NOTSUP

Public Class Methods

create(iface) → self click to toggle source
capture = ::Pcap.create(@dev)

Returns the object itself.

static VALUE
rbpcap_create_s(VALUE class, VALUE iface)
{
  VALUE iPcap = rb_funcall(rb_cPcap, rb_intern("new"), 0);
  return rbpcap_create(iPcap, iface);
}
lookupdev() click to toggle source

Return the name of a network device on the system.

The pcap_lookupdev subroutine gets a network device suitable for use with the pcap_open_live and the pcap_lookupnet subroutines. If no interface can be found, or none are configured to be up, Null is returned. In the case of multiple network devices attached to the system, the pcap_lookupdev subroutine returns the first one it finds to be up, other than the loopback interface. (Loopback is always ignored.)

static VALUE
rbpcap_s_lookupdev(VALUE self)
{
  char *dev = NULL;
  char eb[PCAP_ERRBUF_SIZE];
  VALUE ret_dev;  /* device string to return */
#if defined(WIN32)  /* pcap_lookupdev is broken on windows */
  pcap_if_t *alldevs;
  pcap_if_t *d;

  /* Retrieve the device list from the local machine */
  if (pcap_findalldevs(&alldevs,eb) == -1) {
      rb_raise(eBindingError,"%s",eb);
  }

  /* Find the first interface with an address and not loopback */
  for(d = alldevs; d != NULL; d= d->next)  {
      if(d->name && d->addresses && !(d->flags & PCAP_IF_LOOPBACK)) {
          dev=d->name;
          break;
      }
  }

  if (dev == NULL) {
      rb_raise(eBindingError,"%s","No valid interfaces found, Make sure WinPcap is installed.\n");
  }
  ret_dev = rb_str_new2(dev);
  /* We don't need any more the device list. Free it */
  pcap_freealldevs(alldevs);
#else
  dev = pcap_lookupdev(eb);
  if (dev == NULL) {
        rb_raise(eBindingError, "%s", eb);
 }
  ret_dev = rb_str_new2(dev);
#endif
  return ret_dev;
}
lookupnet(p1) click to toggle source

Returns the network address and subnet mask for a network device.

static VALUE
rbpcap_s_lookupnet(VALUE self, VALUE dev)
{
  bpf_u_int32 net, mask, m;
  struct in_addr addr;
  char eb[PCAP_ERRBUF_SIZE];
        VALUE list;

  Check_Type(dev, T_STRING);
  if (pcap_lookupnet(StringValuePtr(dev), &net, &mask, eb) == -1) {
          rb_raise(rb_eRuntimeError, "%s", eb);
  }

  addr.s_addr = net;
  m = ntohl(mask);
  list = rb_ary_new();
        rb_ary_push(list, rb_str_new2((char *) inet_ntoa(addr)));
        rb_ary_push(list, UINT2NUM(m));
        return(list);
}
new() click to toggle source

Creates a new Pcap instance and returns the object itself.

static VALUE
rbpcap_new_s(VALUE class)
{
  VALUE self;
  rbpcap_t *rbp;

  // need to make destructor do a pcap_close later
  self = Data_Make_Struct(class, rbpcap_t, 0, rbpcap_free, rbp);
  rb_obj_call_init(self, 0, 0);

  memset(rbp, 0, sizeof(rbpcap_t));

  return self;
}
open_dead(linktype, snaplen) → self click to toggle source

open a fake Pcap for compiling filters or opening a capture for output

::Pcap.open_dead() is used for creating a pcap structure to use when calling the other functions like compiling BPF code.

  • linktype specifies the link-layer type

  • snaplen specifies the snapshot length

Returns the object itself.

static VALUE
rbpcap_open_dead_s(VALUE class, VALUE linktype, VALUE snaplen)
{
  VALUE iPcap = rb_funcall(rb_cPcap, rb_intern("new"), 0);

  return rbpcap_open_dead(iPcap, linktype, snaplen);
}
open_live(iface, snaplen, promisc, timeout) → self click to toggle source
capture = ::Pcap.open_live(@dev, @snaplength, @promiscous_mode, @timeout)

Returns the object itself.

static VALUE
rbpcap_open_live_s(VALUE class, VALUE iface, VALUE snaplen, VALUE promisc, VALUE timeout)
{
  VALUE iPcap = rb_funcall(rb_cPcap, rb_intern("new"), 0);
  return rbpcap_open_live(iPcap, iface, snaplen, promisc, timeout);
}
open_offline(filename) → self click to toggle source
capture = ::Pcap.open_offline(filename)

Returns the object itself.

static VALUE
rbpcap_open_offline_s(VALUE class, VALUE filename)
{
  VALUE iPcap = rb_funcall(rb_cPcap, rb_intern("new"), 0);

  return rbpcap_open_offline(iPcap, filename);
}
version() click to toggle source
# File lib/pcaprub/version.rb, line 13
def self.version
  return PCAPRUB::VERSION::STRING
end

Public Instance Methods

activate() → self click to toggle source

Activate the interface

Returns the object itself.

static VALUE
rbpcap_activate(VALUE self)
{
  rbpcap_t *rbp;
  int errcode;
  Data_Get_Struct(self, rbpcap_t, rbp);

  if ((errcode = pcap_activate(rbp->pd)) == 0) {
    return self;
  } else {
    rb_raise(ePCAPRUBError, "unable to activate interface: %d, %s", errcode, rbp->iface);
  }
}
activate() → self click to toggle source

Close the interface

Returns the object itself.

static VALUE
rbpcap_close(VALUE self)
{
  rbpcap_t *rbp;
  Data_Get_Struct(self, rbpcap_t, rbp);

  pcap_close(rbp->pd);
  rbp->pd = NULL;
  return self;
}
compile(filter) click to toggle source

Raises an exception if “filter” has a syntax error

Returns self if the filter is valid

static VALUE
rbpcap_compile(VALUE self, VALUE filter) {
  struct bpf_program bpf;
  u_int32_t mask = 0;
  rbpcap_t *rbp;

  Data_Get_Struct(self, rbpcap_t, rbp);
  if(pcap_compile(rbp->pd, &bpf, RSTRING_PTR(filter), 0, mask) < 0) {
    rb_raise(eBPFilterError, "invalid bpf filter: %s", pcap_geterr(rbp->pd));
  }

  pcap_freecode(&bpf);
  return self;
}
dump(caplen, pktlen, packet) click to toggle source

not sure if this deviates too much from the way the rest of this class works?

Writes packet capture date to a binary file assigned with #dump_open().

Returns the object itself.

static VALUE
rbpcap_dump(VALUE self, VALUE caplen, VALUE pktlen, VALUE packet)
{
  rbpcap_t *rbp;
  struct pcap_pkthdr pcap_hdr;

  if(TYPE(packet) != T_STRING)
      rb_raise(rb_eArgError, "packet data must be a string");
  if(TYPE(caplen) != T_FIXNUM)
      rb_raise(rb_eArgError, "caplen must be a fixnum");
  if(TYPE(pktlen) != T_FIXNUM)
      rb_raise(rb_eArgError, "pktlen must be a fixnum");

  Data_Get_Struct(self, rbpcap_t, rbp);

  gettimeofday(&pcap_hdr.ts, NULL);
  pcap_hdr.caplen = NUM2UINT(caplen);
  pcap_hdr.len = NUM2UINT(pktlen);

  if(!rbp->pdt) {
      rb_raise(rb_eRuntimeError, "pcap_dumper not defined. You must open a dump file first");
  }

//capture.next is yeilding an 8Bit ASCII  string
//  ->  return rb_str_new((char *) job.pkt, job.hdr.caplen);
//Call dump such that capture.next{|pk| capture.dump(pk.length, pk.length, pk)}

  pcap_dump(
      (u_char*)rbp->pdt,
      &pcap_hdr,
      (unsigned char *)RSTRING_PTR(packet)
  );

  return self;
}
dump_close() click to toggle source

#dump_close() is called to manually close a “savefile”

static VALUE
rbpcap_dump_close(VALUE self)
{
  rbpcap_t *rbp;

  Data_Get_Struct(self, rbpcap_t, rbp);

  if(! rbpcap_ready(rbp)) return self;

  if(!rbp->pdt)
        rb_raise(eDumperError, "Stream is already closed.");

  if (rbp->pdt)
          pcap_dump_close(rbp->pdt);

  rbp->pdt = NULL;

  return self;

}
dump_open(filename) click to toggle source

#dump_open() is called to open a “savefile” for writing

static VALUE
rbpcap_dump_open(VALUE self, VALUE filename)
{
  rbpcap_t *rbp;

  if(TYPE(filename) != T_STRING)
     rb_raise(rb_eArgError, "filename must be a string");

  Data_Get_Struct(self, rbpcap_t, rbp);

  if(! rbpcap_ready(rbp)) return self;

  rbp->pdt = pcap_dump_open(
      rbp->pd,
      RSTRING_PTR(filename)
  );

  if(!rbp->pdt)
        rb_raise(eDumperError, "Stream could not be initialized or opened.");

  return self;
}
each() click to toggle source

Alias of #each_data

static VALUE
rbpcap_each_data(VALUE self)
{
  rbpcap_t *rbp;
#if defined(WIN32)
  HANDLE fno;
#else
  int fno = -1;
#endif

  Data_Get_Struct(self, rbpcap_t, rbp);

  if(! rbpcap_ready(rbp)) return self;

#if defined(WIN32)
  fno = (int)pcap_getevent(rbp->pd);
#else
  fno = pcap_get_selectable_fd(rbp->pd);
#endif

  for(;;) {
        VALUE packet = rbpcap_next_data(self);
        if(packet == Qnil && rbp->type == OFFLINE) break;
    if(packet == Qnil && rbp->type == DEAD) break;
#if defined(WIN32)
          packet == Qnil ? rbpcap_thread_wait_handle(fno) : rb_yield(packet);
#else
          packet == Qnil ? rb_thread_wait_fd(fno) : rb_yield(packet);
#endif
  }

  return self;
}
each_data() { |packet| ... } click to toggle source

Yields each packet from the capture to the passed-in block in turn.

static VALUE
rbpcap_each_data(VALUE self)
{
  rbpcap_t *rbp;
#if defined(WIN32)
  HANDLE fno;
#else
  int fno = -1;
#endif

  Data_Get_Struct(self, rbpcap_t, rbp);

  if(! rbpcap_ready(rbp)) return self;

#if defined(WIN32)
  fno = (int)pcap_getevent(rbp->pd);
#else
  fno = pcap_get_selectable_fd(rbp->pd);
#endif

  for(;;) {
        VALUE packet = rbpcap_next_data(self);
        if(packet == Qnil && rbp->type == OFFLINE) break;
    if(packet == Qnil && rbp->type == DEAD) break;
#if defined(WIN32)
          packet == Qnil ? rbpcap_thread_wait_handle(fno) : rb_yield(packet);
#else
          packet == Qnil ? rb_thread_wait_fd(fno) : rb_yield(packet);
#endif
  }

  return self;
}
each_packet() { |packet| ... } click to toggle source

Yields a PCAP::Packet from the capture to the passed-in block in turn.

static VALUE
rbpcap_each_packet(VALUE self)
{
  rbpcap_t *rbp;

#if defined(WIN32)
  HANDLE fno;
#else
  int fno = -1;
#endif

  Data_Get_Struct(self, rbpcap_t, rbp);
  if(! rbpcap_ready(rbp)) return self;

#if defined(WIN32)
  fno = (int)pcap_getevent(rbp->pd);
#else
  fno = pcap_get_selectable_fd(rbp->pd);
#endif

  for(;;) {
        VALUE packet = rbpcap_next_packet(self);
        if(packet == Qnil && rbp->type == OFFLINE) break;
    if(packet == Qnil && rbp->type == DEAD) break;
#if defined(WIN32)
          packet == Qnil ? rbpcap_thread_wait_handle(fno) : rb_yield(packet);
#else
          packet == Qnil ? rb_thread_wait_fd(fno) : rb_yield(packet);
#endif
  }

  return self;
}
inject(payload) click to toggle source

inject() transmit a raw packet through the network interface

Returns the number of bytes written on success else raise failure.

static VALUE
rbpcap_inject(VALUE self, VALUE payload)
{
  rbpcap_t *rbp;

  if(TYPE(payload) != T_STRING)
        rb_raise(rb_eArgError, "pupayload must be a string");

  Data_Get_Struct(self, rbpcap_t, rbp);

        if(! rbpcap_ready(rbp)) return self;
#if defined(WIN32)
  /* WinPcap does not have a pcap_inject call we use pcap_sendpacket, if it suceedes
   * we simply return the amount of packets request to inject, else we fail.
   */
  if(pcap_sendpacket(rbp->pd, RSTRING_PTR(payload), RSTRING_LEN(payload)) != 0) {
        rb_raise(rb_eRuntimeError, "%s", pcap_geterr(rbp->pd));
  }
  return INT2NUM(RSTRING_LEN(payload));
#else
  return INT2NUM(pcap_inject(rbp->pd, RSTRING_PTR(payload), RSTRING_LEN(payload)));
#endif
}
next() click to toggle source

Alias of #next_data

static VALUE
rbpcap_next_data(VALUE self)
{
        rbpcap_t *rbp;
        rbpcapjob_t job;
        char eb[PCAP_ERRBUF_SIZE];
        int ret;

        Data_Get_Struct(self, rbpcap_t, rbp);

        if(! rbpcap_ready(rbp)) return self;
        pcap_setnonblock(rbp->pd, 1, eb);

#ifdef MAKE_TRAP
        TRAP_BEG;
#endif

  // ret will contain the number of packets captured during the trap (ie one) since this is an iterator.
        ret = pcap_dispatch(rbp->pd, 1, (pcap_handler) rbpcap_handler, (u_char *)&job);

#ifdef MAKE_TRAP
        TRAP_END;
#endif

        if(rbp->type == OFFLINE && ret <= 0)
          return Qnil;

  if(rbp->type == DEAD && ret <= 0)
    return Qnil;


        if(ret > 0 && job.hdr.caplen > 0)
    return rb_str_new((char *) job.pkt, job.hdr.caplen);

        return Qnil;
}
next_data() click to toggle source

*

Returns the next packet from the packet capture device.

Returns a string with the packet data.

If the next_data() is unsuccessful, Null is returned.
static VALUE
rbpcap_next_data(VALUE self)
{
        rbpcap_t *rbp;
        rbpcapjob_t job;
        char eb[PCAP_ERRBUF_SIZE];
        int ret;

        Data_Get_Struct(self, rbpcap_t, rbp);

        if(! rbpcap_ready(rbp)) return self;
        pcap_setnonblock(rbp->pd, 1, eb);

#ifdef MAKE_TRAP
        TRAP_BEG;
#endif

  // ret will contain the number of packets captured during the trap (ie one) since this is an iterator.
        ret = pcap_dispatch(rbp->pd, 1, (pcap_handler) rbpcap_handler, (u_char *)&job);

#ifdef MAKE_TRAP
        TRAP_END;
#endif

        if(rbp->type == OFFLINE && ret <= 0)
          return Qnil;

  if(rbp->type == DEAD && ret <= 0)
    return Qnil;


        if(ret > 0 && job.hdr.caplen > 0)
    return rb_str_new((char *) job.pkt, job.hdr.caplen);

        return Qnil;
}
next_packet() click to toggle source

Returns the next packet from the packet capture device.

Returns a string with the packet data.

If the #next_packet() is unsuccessful, Null is returned.

static VALUE
rbpcap_next_packet(VALUE self)
{
        rbpcap_t *rbp;
        rbpcapjob_t job;
        char eb[PCAP_ERRBUF_SIZE];
        int ret;

        rbpacket_t* rbpacket;

        Data_Get_Struct(self, rbpcap_t, rbp);

        if(! rbpcap_ready(rbp)) return self;

        pcap_setnonblock(rbp->pd, 1, eb);

#ifdef MAKE_TRAP
        TRAP_BEG;
#endif

        ret = pcap_dispatch(rbp->pd, 1, (pcap_handler) rbpcap_handler, (u_char *)&job);

#ifdef MAKE_TRAP
        TRAP_END;
#endif

        if(rbp->type == OFFLINE && ret <= 0)
          return Qnil;

  if(rbp->type == DEAD && ret <= 0)
    return Qnil;

        if(ret > 0 && job.hdr.caplen > 0)
    {
      rbpacket = ALLOC(rbpacket_t);
      rbpacket->hdr = job.hdr;
      rbpacket->pkt = (u_char *)job.pkt;
      return Data_Wrap_Struct(rb_cPkt, 0, rbpacket_free, rbpacket);
    }

        return Qnil;
}
pcap_major_version() click to toggle source

Returns the integer PCAP MAJOR LIBRARY value unless capture

static VALUE
rbpcap_major_version(VALUE self)
{
  rbpcap_t *rbp;

  Data_Get_Struct(self, rbpcap_t, rbp);

        if(! rbpcap_ready(rbp)) return self;

  return INT2NUM(pcap_major_version(rbp->pd));
}
pcap_minor_version() click to toggle source

Returns the integer PCAP MINOR LIBRARY value unless capture

static VALUE
rbpcap_minor_version(VALUE self)
{
  rbpcap_t *rbp;

  Data_Get_Struct(self, rbpcap_t, rbp);

        if(! rbpcap_ready(rbp)) return self;

  return INT2NUM(pcap_minor_version(rbp->pd));
}
setfilter(filter) click to toggle source

Provide a valid bpf-filter to apply to the packet capture

# Show me all SYN packets:
bpf-filter = "tcp[13] & 2 != 0"
capture.setfilter(bpf-filter)

Examples:

  • “net 10.0.0.0/8”

  • “not tcp and dst host 192.168.1.1”

Returns the object itself.

static VALUE
rbpcap_setfilter(VALUE self, VALUE filter)
{
  char eb[PCAP_ERRBUF_SIZE];
  rbpcap_t *rbp;
  u_int32_t mask = 0, netid = 0;
  struct bpf_program bpf;

  Data_Get_Struct(self, rbpcap_t, rbp);

  if(TYPE(filter) != T_STRING)
        rb_raise(eBPFilterError, "filter must be a string");

  if(! rbpcap_ready(rbp)) return self;

  if(rbp->type == DEAD) {
        rb_raise(eBPFilterError, "unable to set bpf filter on OPEN_DEAD");
  }

  if(rbp->type == LIVE)
        if(pcap_lookupnet(rbp->iface, &netid, &mask, eb) < 0) {
                netid = 0;
                mask = 0;
                rb_warn("unable to get IP: %s", eb);
        }

  if(pcap_compile(rbp->pd, &bpf, RSTRING_PTR(filter), 0, mask) < 0) {
        rb_raise(eBPFilterError, "invalid bpf filter: %s", pcap_geterr(rbp->pd));
  }

  if(pcap_setfilter(rbp->pd, &bpf) < 0) {
        pcap_freecode(&bpf);
        rb_raise(eBPFilterError, "unable to set bpf filter: %s", pcap_geterr(rbp->pd));
  }

  pcap_freecode(&bpf);
  return self;
}
setmonitor(true) click to toggle source

Set monitor mode for the capture.

Returns the object itself.

static VALUE
rbpcap_setmonitor(VALUE self, VALUE mode)
{
  rbpcap_t *rbp;
  int rfmon_mode = 0;
  Data_Get_Struct(self, rbpcap_t, rbp);
  if (mode == Qtrue) {
    rfmon_mode = 1;
  } else if (mode == Qfalse) {
    rfmon_mode = 0;
  } else {
    rb_raise(rb_eArgError, "Monitor mode must be a boolean");
  }

#if defined(WIN32)
  // monitor mode support was disabled in WinPcap 4.0.2
  rb_raise(ePCAPRUBError, "set monitor mode not supported in WinPcap");
#else
  if (pcap_set_rfmon(rbp->pd, rfmon_mode) == 0) {
    return self;
  } else {
    rb_raise(ePCAPRUBError, "unable to set monitor mode");
  }
#endif
}
setpromisc(true) click to toggle source

Set promiscuous mode for the capture.

Returns the object itself.

static VALUE
rbpcap_setpromisc(VALUE self, VALUE mode)
{
  rbpcap_t *rbp;
  int promisc_mode = 0;
  Data_Get_Struct(self, rbpcap_t, rbp);
  if (mode == Qtrue) {
    promisc_mode = 1;
  } else if (mode == Qfalse) {
    promisc_mode = 0;
  } else {
    rb_raise(rb_eArgError, "Promisc mode must be a boolean");
  }

  if (pcap_set_promisc(rbp->pd, promisc_mode) == 0) {
    return self;
  } else {
    rb_raise(ePCAPRUBError, "unable to set promiscuous mode");
  }
}
setsnaplen(true) click to toggle source

Set snap length for the capture.

Returns the object itself.

static VALUE
rbpcap_setsnaplen(VALUE self, VALUE snaplen)
{
  rbpcap_t *rbp;
  Data_Get_Struct(self, rbpcap_t, rbp);

  if(TYPE(snaplen) != T_FIXNUM)
    rb_raise(rb_eArgError, "snaplen must be a fixnum");

  if (pcap_set_snaplen(rbp->pd, NUM2INT(snaplen)) == 0) {
    return self;
  } else {
    rb_raise(ePCAPRUBError, "unable to set snap length");
  }
}
settimeout(1234) click to toggle source

Set timeout for the capture.

Returns the object itself.

static VALUE
rbpcap_settimeout(VALUE self, VALUE timeout)
{
  rbpcap_t *rbp;
  Data_Get_Struct(self, rbpcap_t, rbp);

  if(TYPE(timeout) != T_FIXNUM)
    rb_raise(rb_eArgError, "timeout must be a fixnum");

  if (pcap_set_timeout(rbp->pd, NUM2INT(timeout)) == 0) {
    return self;
  } else {
    rb_raise(ePCAPRUBError, "unable to set timeout");
  }
}
snaplen() click to toggle source

Alias of snapshot

static VALUE
rbpcap_snapshot(VALUE self)
{
  rbpcap_t *rbp;

  Data_Get_Struct(self, rbpcap_t, rbp);

        if(! rbpcap_ready(rbp)) return self;

  return INT2NUM(pcap_snapshot(rbp->pd));
}
snapshot() click to toggle source

Returns the snapshot length, which is the number of bytes to save for each packet captured.

static VALUE
rbpcap_snapshot(VALUE self)
{
  rbpcap_t *rbp;

  Data_Get_Struct(self, rbpcap_t, rbp);

        if(! rbpcap_ready(rbp)) return self;

  return INT2NUM(pcap_snapshot(rbp->pd));
}
stats() click to toggle source

Returns a hash with statistics of the packet capture

  • “recv”

    # number of packets received

  • “drop”

    # number of packets dropped

  • “idrop”

    # number of packets dropped by interface

static VALUE
rbpcap_stats(VALUE self)
{
  rbpcap_t *rbp;
  struct pcap_stat stat;
  VALUE hash;

  Data_Get_Struct(self, rbpcap_t, rbp);

        if(! rbpcap_ready(rbp)) return self;

  if (pcap_stats(rbp->pd, &stat) == -1)
        return Qnil;

  hash = rb_hash_new();
  rb_hash_aset(hash, rb_str_new2("recv"), UINT2NUM(stat.ps_recv));
  rb_hash_aset(hash, rb_str_new2("drop"), UINT2NUM(stat.ps_drop));
  rb_hash_aset(hash, rb_str_new2("idrop"), UINT2NUM(stat.ps_ifdrop));
  // drops by interface XXX not yet supported under pcap.h 2.4

//#if defined(WIN32)
//    rb_hash_aset(hash, rb_str_new2("bs_capt"), UINT2NUM(stat.bs_capt));
//#endif

  return hash;
}