Class: FFI::PCap::Live

Inherits:
CaptureWrapper show all
Defined in:
lib/ffi/pcap/live.rb

Constant Summary collapse

DEFAULT_TO_MS =

Default timeout for pcap_open_live()

1000
@@have_setdirection =
PCap.respond_to?(:pcap_setdirection)
@@have_inject =
PCap.respond_to?(:pcap_inject)
@@have_sendpacket =
PCap.respond_to?(:pcap_sendpacket)

Constants inherited from CaptureWrapper

CaptureWrapper::DEFAULT_COUNT

Instance Attribute Summary collapse

Attributes inherited from CaptureWrapper

#handler

Attributes inherited from CommonWrapper

#pcap

Instance Method Summary collapse

Methods inherited from CaptureWrapper

#breakloop, #dispatch, #fileno, #loop, #next, #old_next, #selectable_fd, #selectable_io, #set_filter

Methods inherited from CommonWrapper

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

Constructor Details

#initialize(opts = nil) {|_self| ... } ⇒ Live

Creates a pcap interface for capturing from the network.

Parameters:

  • opts (Hash) (defaults to: nil)

    Options are ignored and passed to the super-class except those below.

Options Hash (opts):

  • :device, (String, nil)

    :dev The device to open. On some platforms, this can be "any". If nil or unspecified FFI::PCap.lookupdev is called to obtain a default device.

  • :snaplen (Integer)

    The snapshot length for the pcap object. Defaults to DEFAULT_SNAPLEN

  • :promisc (Boolean)

    Specifies if the interface is to be put into promiscuous mode. Defaults to false.

  • :timeout (Integer)

    Specifies the read timeout in milliseconds. Defaults to DEFAULT_TO_MS

Yields:

  • (_self)

Yield Parameters:

Raises:

  • (LibError)

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

  • (ArgumentError)

    May raise an exception if a :device cannot be autodetected using FFI::PCap.lookupdev for any reason. This should never happen on most platforms.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/ffi/pcap/live.rb', line 62

def initialize(opts=nil)
  opts ||= {}

  @device = (opts[:device] || opts[:dev] || PCap.lookupdev)

  unless @device
    raise(ArgumentError,"Couldn't detect a device. One must be specified",caller)
  end

  @snaplen   = opts[:snaplen] || DEFAULT_SNAPLEN
  @promisc   = opts[:promisc] ? 1 : 0
  @timeout   = opts[:timeout] || DEFAULT_TO_MS
  @direction = (opts[:direction] || opts[:dir])

  @errbuf = ErrorBuffer.new
  @pcap   = PCap.pcap_open_live(@device, @snaplen, @promisc, @timeout, @errbuf)

  if @pcap.null?
    raise(LibError, "pcap_open_live(): #{@errbuf}",caller)
  end

  # call super to get all our ducks in a row
  super(@pcap, opts)

  set_direction(@direction) if @direction

  # Cache network and netmask from pcap_lookupdev.
  # These pointers may be used internally (and should get autoreleased)
  @netp, @maskp = nil
  begin
    PCap.lookupnet(@device) do |netp, maskp|
      @netp = netp
      @maskp = maskp
    end
  rescue LibError
    warn "Warning: #{$!}"
  end

  yield self if block_given?
end

Instance Attribute Details

#deviceObject (readonly)

Returns the value of attribute device.



24
25
26
# File 'lib/ffi/pcap/live.rb', line 24

def device
  @device
end

#directionObject (readonly)

Returns the value of attribute direction.



24
25
26
# File 'lib/ffi/pcap/live.rb', line 24

def direction
  @direction
end

#promiscObject (readonly)

Returns the value of attribute promisc.



24
25
26
# File 'lib/ffi/pcap/live.rb', line 24

def promisc
  @promisc
end

#timeoutObject (readonly)

Returns the value of attribute timeout.



24
25
26
# File 'lib/ffi/pcap/live.rb', line 24

def timeout
  @timeout
end

Instance Method Details

#inject(pkt) ⇒ Integer

Transmit a packet using pcap_inject()

(not available on all platforms)

Parameters:

  • obj (Packet, String)

    The packet to send. This can be a Packet or String object.

Returns:

  • (Integer)

    The number of bytes sent.

Raises:

  • (ArgumentError)

    An exception is raised if the pkt object type is incorrect or if it is a Packet and the body pointer is null.

  • (LibError)

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

  • (NotImplementedError)

    If the pcap_inject() function is not available from your libpcap library pcap_sendpacket will be tried, if both are missing, this exception will be raised.



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/ffi/pcap/live.rb', line 255

def inject(pkt)
  if @@have_inject
    if pkt.kind_of?(Packet)
      len = pkt.caplen
      bufp = pkt.body_ptr

      if bufp.null?
        raise(ArgumentError,"packet data null pointer",caller)
      end
    elsif pkt.kind_of?(String)
      len = pkt.size
      bufp = FFI::MemoryPointer.from_string(pkt)
    else
      raise(ArgumentError,"Don't know how to inject #{pkt.class}",caller)
    end

    sent = PCap.pcap_inject(_pcap, bufp, len)

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

    return sent
  else 
    # fake it with sendpacket on windows
    if sendpacket(pkt)
      return (Packet === pkt) ? pkt.caplen : pkt.size
    end
  end
end

#netmaskObject

Returns the dotted notation string for the IPv4 netmask for the device used by this pcap interface.



117
118
119
120
121
# File 'lib/ffi/pcap/live.rb', line 117

def netmask
  if @maskp
    @netmask ||= @maskp.get_array_of_uchar(0,4).join('.')
  end
end

#netmask_n32Object

Returns the 32-bit numeric representation of the IPv4 network address for this device.



137
138
139
140
141
# File 'lib/ffi/pcap/live.rb', line 137

def netmask_n32
  if @maskp
    ::FFI::DRY::NetEndian.ntohl(@maskp.get_uint32(0))
  end
end

#networkObject

Returns the dotted notation string for the IPv4 network address for the device used by this pcap interface.



107
108
109
110
111
# File 'lib/ffi/pcap/live.rb', line 107

def network
  if @netp
    @network ||= @netp.get_array_of_uchar(0,4).join('.')
  end
end

#network_n32Object

Returns the 32-bit numeric representation of the IPv4 network address for this device.



127
128
129
130
131
# File 'lib/ffi/pcap/live.rb', line 127

def network_n32
  if @netp
    ::FFI::DRY::NetEndian.ntohl(@netp.get_uint32(0))
  end
end

#non_blockingBoolean Also known as: non_blocking?

get the state of non-blocking mode on a capture device

Returns:

  • (Boolean)

    non-blocking mode

Raises:

  • (LibError)

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



198
199
200
201
202
203
204
205
206
# File 'lib/ffi/pcap/live.rb', line 198

def non_blocking
  mode = PCap.pcap_getnonblock(_pcap, @errbuf)

  if mode == -1
    raise(LibError,"pcap_getnonblock(): #{@errbuf}",caller)
  else
    return mode == 1
  end
end

#sendpacket(pkt) ⇒ True Also known as: send_packet

Transmit a packet using pcap_sendpacket()

(not available on all platforms)

Parameters:

  • obj (Packet, String)

    The packet to send. This can be a Packet or String object.

Returns:

  • (True)

    True is returned on success. Otherwise an exception is raised.

Raises:

  • (ArgumentError)

    An exception is raised if the pkt object type is incorrect or if it is a Packet and the body pointer is null.

  • (LibError)

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

  • (NotImplementedError)

    If the pcap_sendpacket() function is not available from your libpcap library this exception will be raised.



311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/ffi/pcap/live.rb', line 311

def sendpacket(pkt)
  unless @@have_sendpacket
    raise(NotImplementedError,"packet injectors are not avaiable from your pcap library",caller)
  end

  if pkt.kind_of? Packet
    len = pkt.caplen
    bufp = pkt.body_ptr

    if bufp.null?
      raise(ArgumentError,"packet data null pointer",caller)
    end
  elsif pkt.kind_of? String
    len = pkt.size
    bufp = FFI::MemoryPointer.from_string(pkt)
  else
    raise(ArgumentError,"Don't know how to send #{pkt.class}",caller)
  end

  if PCap.pcap_sendpacket(_pcap, bufp, len) != 0
    raise(LibError,"pcap_sendpacket(): #{geterr}",caller)
  end

  return true
end

#set_direction(dir) ⇒ Object Also known as: direction=

Sets the direction for which packets will be captured.

(Not supported on all platforms)



150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/ffi/pcap/live.rb', line 150

def set_direction(dir)
  unless @@have_setdirection
    raise(NotImplementedError,"pcap_setdirection() is not avaiable from your pcap library",caller) 
  end

  dirs = PCap.enum_type(:pcap_direction_t)

  if PCap.pcap_setdirection(_pcap, dirs[:"pcap_d_#{dir}"]) == 0
    return true
  else
    raise(LibError,"pcap_setdirection(): #{geterr}",caller)
  end
end

#set_non_blocking(mode) ⇒ Object Also known as: non_blocking=, nonblocking=

set the state of non-blocking mode on a capture device

Parameters:

  • mode (Boolean)

Raises:

  • (LibError)

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



175
176
177
178
179
180
181
182
183
# File 'lib/ffi/pcap/live.rb', line 175

def set_non_blocking(mode)
  mode =  mode ? 1 : 0

  if PCap.pcap_setnonblock(_pcap, mode, @errbuf) == 0
    return (mode == 1)
  else
    raise(LibError,"pcap_setnonblock(): #{@errbuf}",caller)
  end
end

#statsStats

Get capture statistics

Returns:

  • (Stats)

Raises:

  • (LibError)

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



219
220
221
222
223
224
225
226
227
# File 'lib/ffi/pcap/live.rb', line 219

def stats
  stats = Stat.new

  unless PCap.pcap_stats(_pcap, stats) == 0
    raise(LibError,"pcap_stats(): #{geterr}",caller)
  end

  return stats
end