Class: FFI::PCap::CaptureWrapper

Inherits:
CommonWrapper show all
Includes:
Enumerable
Defined in:
lib/ffi/pcap/capture_wrapper.rb

Overview

A superclass for both offline and live interfaces, but not dead interfaces. This class provides all the features necessary for receiving packets through libpcap.

The loop and dispatch methods default to using a CopyHandler object when preparing values to the callback block. This is done to safely provide references to packets outside of the callback blocks.
See CopyHandler for more information.

Note that for performance reasons, you may not need or want to incur the extra overhead of creating a copy for every Packet. You can supply a nil value for the loop handler which will simply pass volatile references to packets directly to your block. You can also write custom handlers which implement the receive_pcap method and implement custom defined behaviors.

Direct Known Subclasses

Live, Offline

Constant Summary collapse

DEFAULT_COUNT =

Default packet count (-1: infinite loop)

-1

Instance Attribute Summary collapse

Attributes inherited from CommonWrapper

#pcap

Instance Method Summary collapse

Methods inherited from CommonWrapper

#close, #closed?, #compile, #datalink, #geterr, #open_dump, #ready?, #snaplen, #supported_datalinks, #to_ptr

Constructor Details

#initialize(pcap, opts = {}, &block) ⇒ CaptureWrapper

Adds an extra parameter :handler for specifying a capture handler when using loop or dispatch. The handler defaults to FFI::PCap::CopyHandler, which always yields a copy of each packet to a block.

Setting :handler to nil will pass packets directly to a block without copying them, which may be desirable if the packets are only ever processed within the block, and code does not need to retain a reference to them elsewhere.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/ffi/pcap/capture_wrapper.rb', line 42

def initialize(pcap, opts={}, &block)
  @handler = opts.fetch(handler,CopyHandler)

  trap('INT') do
    stop()
    close()

    raise(SignalException,'INT',caller)
  end

  trap('TERM') do
    stop()
    close()

    raise(SignalException,'TERM',caller)
  end

  super(pcap, opts, &block)
end

Instance Attribute Details

#handlerObject

Returns the value of attribute handler.



30
31
32
# File 'lib/ffi/pcap/capture_wrapper.rb', line 30

def handler
  @handler
end

Instance Method Details

#breakloopObject Also known as: stop

Sets a flag that will force #dispatch or #loop to return rather than looping; they will return the number of packets that have been processed so far, or nil if no packets have been processed so far.

breakloop does not guarantee that no further packets will be processed by #dispatch or #loop after it is called. At most one more packet may be processed.



238
239
240
# File 'lib/ffi/pcap/capture_wrapper.rb', line 238

def breakloop
  PCap.pcap_breakloop(_pcap)
end

#dispatch(opts = {}) {|self, pkt| ... } ⇒ Integer?

Processes packets from a live capture or savefile until cnt packets are processed, the end of the current bufferful of packets is reached when doing a live capture, the end of the savefile is reached (when reading from a savefile), pcap_breakloop() is called, or an error occurs.

Thus, when doing a live capture, cnt is the maximum number of packets to process before returning, but is not a minimum number; when reading a live capture, only one bufferful of packets is read at a time, so fewer than cnt packets may be processed. A value of -1 or 0 for cnt causes all the packets received in one buffer to be processed when reading a live capture, and causes all the packets in the file to be processed when reading a savefile.

Note: In older versions of libpcap, the behavior when cnt was 0 was undefined; different platforms and devices behaved differently, so code that must work with older versions of libpcap should use -1, nor 0, as the value of cnt.

Yields:

  • (self, pkt)

Yield Parameters:

  • self (CaptureWrapper)

    A reference to self is passed to the block.

  • pkt (Packet)

    A packet object is yielded which references the header and bytes.

Returns:

  • (Integer, nil)

    Returns the number of packets processed on success; this can be 0 if no packets were read from a live capture or if no more packets are available in a savefile. It returns nil if the loop terminated due to a call to CommonWrapper#stop before any packets were processed.

Raises:

  • (ReadError)

    An exception is raised if an error occurs or if libpcap returns an unexpected value.



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/ffi/pcap/capture_wrapper.rb', line 159

def dispatch(opts={}, &block)
  cnt = (opts[:count] || DEFAULT_COUNT) # default to infinite loop
  h = opts[:handler]

  ret = PCap.pcap_dispatch(_pcap, cnt, _wrap_callback(h, block),nil)

  if ret == -1
    raise(ReadError,"pcap_dispatch(): #{geterr}",caller)
  elsif ret -2
    return nil
  elsif ret > -1
    return ret
  else
    raise(ReadError,"unexpected return from pcap_dispatch() -> #{ret}",caller)
  end
end

#filenoObject



276
277
278
# File 'lib/ffi/pcap/capture_wrapper.rb', line 276

def fileno
  PCap.pcap_fileno(_pcap)
end

#loop(opts = {}) {|self, pkt| ... } ⇒ Integer? Also known as: each

Processes packets from a live capture or savefile until cnt packets are processed, the end of the savefile is reached (when reading from a savefile), pcap_breakloop() is called, or an error occurs.

It does not return when live read timeouts occur. A value of -1 or 0 for cnt is equivalent to infinity, so that packets are processed until another ending condition occurs.

(In older versions of libpcap, the behavior when cnt was 0 was undefined; different platforms and devices behaved differently, so code that must work with older versions of libpcap should use -1, nor 0, as the value of cnt.)

Parameters:

  • opts (Hash) (defaults to: {})

    Receive options.

  • [optional, (Hash)

    a customizable set of options

Yields:

  • (self, pkt)

Yield Parameters:

  • self (CaptureWrapper)

    A reference to self is passed to the block.

  • pkt (Packet)

    A packet object is yielded which references the header and bytes.

Returns:

  • (Integer, nil)

    Returns 0 if cnt is exhausted, or nil if the loop terminated due to a call to pcap_breakloop() before any packets were processed. It does not return when live read timeouts occur; instead, it attempts to read more packets.

Raises:

  • (ReadError)

    An exception is raised if an error occurs or if libpcap returns an unexpected value.



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/ffi/pcap/capture_wrapper.rb', line 101

def loop(opts={}, &block)
  cnt = (opts[:count] || DEFAULT_COUNT)
  h = opts[:handler]

  ret = PCap.pcap_loop(_pcap, cnt, _wrap_callback(h, block), nil)

  if ret == -1
    raise(ReadError,"pcap_loop(): #{geterr}",caller)
  elsif ret -2
    return nil
  elsif ret > -1
    return ret
  else
    raise(ReadError,"unexpected return from pcap_loop(): #{ret}",caller)
  end
end

#nextPacket? Also known as: next_extra, next_ex

Reads the next packet from a pcap device and returns a success/failure indication.

Returns:

  • (Packet, nil)

    A packet is returned on success or a nil if the timeout expired or all packets in a dump file have been exhausted when reading from a savefile.

Raises:

  • (ReadError)

    This exception is raised if there was an error calling pcap_next_ex().

  • (TimeoutError)

    This exception is raised if the timeout expires



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/ffi/pcap/capture_wrapper.rb', line 209

def next
  hdr_p = MemoryPointer.new(:pointer)
  buf_p = MemoryPointer.new(:pointer)

  case PCap.pcap_next_ex(_pcap, hdr_p, buf_p)
  when -1 # error
    raise(ReadError,"pcap_next_ex(): #{geterr}",caller)
  when 0  # live capture read timeout expired
    return nil
  when -2 # savefile packets exhausted
    return nil
  when 1
    hdr = PacketHeader.new(hdr_p.get_pointer(0))
    return Packet.new(hdr, buf_p.get_pointer(0))
  end
end

#old_nextObject

This method uses the older pcap_next() function which has been deprecated in favor of pcap_next_ex(). It is included only for backward compatability purposes.

Important Note. According to libpcap documentation:

Unfortunately, there is no way to determine whether an error 
occured or not when using pcap_next().


186
187
188
189
190
191
# File 'lib/ffi/pcap/capture_wrapper.rb', line 186

def old_next
  header = PacketHeader.new
  bytes = PCap.pcap_next(_pcap, header)

  return Packet.new(header, bytes) unless bytes.null?
end

#selectable_fdObject



280
281
282
283
284
285
286
# File 'lib/ffi/pcap/capture_wrapper.rb', line 280

def selectable_fd
  if PCap.respond_to?(:pcap_get_selectable_fd)
    PCap.pcap_get_selectable_fd(pcap)
  else
    raise(NotImplementedError, "selectable pcap IO is not available for your platform")
  end
end

#selectable_ioObject



288
289
290
# File 'lib/ffi/pcap/capture_wrapper.rb', line 288

def selectable_io
  ::IO.new(self.selectable_fd, 'r')
end

#set_filter(expression, opts = {}) ⇒ Object Also known as: setfilter, filter=

Used to specify a pcap filter for the pcap interface. This method compiles a filter expression and applies it on the wrapped pcap interface.

Parameters:

  • expression (String)

    A pcap filter expression. See pcap-filter(7) manpage for syntax.

  • opts (Hash) (defaults to: {})

    Compile options. See compile()

Raises:

  • (LibError)

    On failure, an exception is raised with the relevant error message from libpcap.



259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/ffi/pcap/capture_wrapper.rb', line 259

def set_filter(expression, opts={})
  code = compile(expression, opts)
  ret = PCap.pcap_setfilter(_pcap, code)

  # done with this, we can free it
  code.free!

  if ret < 0
    raise(LibError, "pcap_setfilter(): #{geterr}",caller)
  end

  return expression
end