Class: Capp::Packet

Inherits:
Object
  • Object
show all
Defined in:
lib/capp/packet.rb

Overview

Capp::Packet provides convenient extraction of data from packets.

Packet objects are automatically created when a packet is read from the opened interface. Unfortunately Capp does not understand every type of packet. If Capp doesn’t understand your packet the layer 3 payload can be retrieved from unknown_layer3_header.

If Capp doesn’t understand your packets you can extract the data by editing capp.c and submitting a patch. See README for the source code location.

To look up IP source and destination names Resolv (from the ruby standard library, require ‘resolv’) to avoid blocking on name lookups in a cross-platform manner.

Defined Under Namespace

Classes: ARPHeader, EthernetHeader, ICMPHeader, IPv4Header, IPv6Header, TCPHeader, UDPHeader, UnknownLayer3Header

Constant Summary collapse

ADDRESS_CACHE =

:nodoc:

{}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(timestamp, length, capture_length, captured, datalink, headers) ⇒ Packet

Creates a new packet. Ordinarily this is performed from Capp#loop. The timestamp is the packet capture timestamp, length is the total length of the packet, capture_length is the number of captured bytes from the packet. The datalink is the type of link the packet was captured on. headers is a Hash of parsed headers.



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/capp/packet.rb', line 217

def initialize timestamp, length, capture_length, captured, datalink, headers
  @capture_length = capture_length
  @captured       = captured
  @datalink       = datalink
  @length         = length
  @protocols      = headers.keys
  @timestamp      = timestamp

  @arp_header            = headers[:arp]
  @ethernet_header       = headers[:ethernet]
  @icmp_header           = headers[:icmp]
  @ipv4_header           = headers[:ipv4]
  @ipv6_header           = headers[:ipv6]
  @tcp_header            = headers[:tcp]
  @udp_header            = headers[:udp]
  @unknown_layer3_header = headers[:unknown_layer3]
end

Instance Attribute Details

#arp_headerObject (readonly)

The ARP header if this is an ARP packet.



154
155
156
# File 'lib/capp/packet.rb', line 154

def arp_header
  @arp_header
end

#capture_lengthObject (readonly)

Length of packet that was captured



144
145
146
# File 'lib/capp/packet.rb', line 144

def capture_length
  @capture_length
end

#capturedObject (readonly)

Captured portion of the entire packet including datalink layer.



149
150
151
# File 'lib/capp/packet.rb', line 149

def captured
  @captured
end

#ethernet_headerObject (readonly)

The Ethernet header if this is an Ethernet packet.



159
160
161
# File 'lib/capp/packet.rb', line 159

def ethernet_header
  @ethernet_header
end

#icmp_headerObject (readonly)

ICMP header if this is an ICMP (v4) packet.



170
171
172
# File 'lib/capp/packet.rb', line 170

def icmp_header
  @icmp_header
end

#ipv4_headerObject (readonly)

IPv4 header if this is an IPv4 packet.



175
176
177
# File 'lib/capp/packet.rb', line 175

def ipv4_header
  @ipv4_header
end

#ipv6_headerObject (readonly)

IPv6 header if this is an IPv6 packet.



180
181
182
# File 'lib/capp/packet.rb', line 180

def ipv6_header
  @ipv6_header
end

#lengthObject (readonly)

Total length of packet including the portion not captured.



185
186
187
# File 'lib/capp/packet.rb', line 185

def length
  @length
end

#protocolsObject (readonly)

Array of protocol names in this packet. This list is ordered from lowest to highest level.



165
166
167
# File 'lib/capp/packet.rb', line 165

def protocols
  @protocols
end

#tcp_headerObject (readonly)

TCP header if this is a TCP packet.



190
191
192
# File 'lib/capp/packet.rb', line 190

def tcp_header
  @tcp_header
end

#timestampObject (readonly)

Packet capture timestamp



195
196
197
# File 'lib/capp/packet.rb', line 195

def timestamp
  @timestamp
end

#udp_headerObject (readonly)

UDP header if this is a UDP packet.



200
201
202
# File 'lib/capp/packet.rb', line 200

def udp_header
  @udp_header
end

#unknown_layer3_headerObject (readonly)

Fake header for unknown layer 3 protocols. The datalink type will indicate the layer 3 protocol. For an Ethernet packet see the ethernet_header for the type, etc. This method only provides the payload offset of the packet content.



208
209
210
# File 'lib/capp/packet.rb', line 208

def unknown_layer3_header
  @unknown_layer3_header
end

Instance Method Details

#destination(resolver = nil) ⇒ Object

Returns the destination of the packet regardless of protocol

If a Resolv-compatible resolver is given the name will be looked up.



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/capp/packet.rb', line 240

def destination resolver = nil
  destination =
    if ipv4? then
      @ipv4_header
    elsif ipv6? then
      @ipv6_header
    else
      raise NotImplementedError
    end.destination

  destination = resolve destination, resolver

  if tcp? then
    destination << ".#{@tcp_header.destination_port}"
  elsif udp? then
    destination << ".#{@udp_header.destination_port}"
  end

  destination
end

#dumpObject

Returns the captured bytes with non-printing characters replaced by “.”



264
265
266
# File 'lib/capp/packet.rb', line 264

def dump
  @captured.tr "\000-\037\177-\377", "."
end

#hexdump(offset = 0) ⇒ Object

Dumps the captured packet from offset with offsets, hexadecimal output for the bytes and the ASCII content with non-printing characters replaced by “.”



273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/capp/packet.rb', line 273

def hexdump offset = 0
  data = @captured[offset, @capture_length]

  data.scan(/.{,16}/m).map.with_index do |chunk, index|
    next nil if chunk.empty?
    hex  = chunk.unpack('C*').map { |byte| '%02x' % byte }
    dump = chunk.tr "\000-\037\177-\377", "."

    length = hex.length
    hex.fill '  ', length, 16 - length if length < 16

    "\t0x%04x:  %s%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s  %s" % [
      index * 16, *hex, dump
    ]
  end.join "\n"
end

#ipv4?Boolean

Is this an IPv4 packet?

Returns:

  • (Boolean)


376
377
378
# File 'lib/capp/packet.rb', line 376

def ipv4?
  @ipv4_header
end

#ipv6?Boolean

Is this an IPv6 packet?

Returns:

  • (Boolean)


383
384
385
# File 'lib/capp/packet.rb', line 383

def ipv6?
  @ipv6_header
end

#payloadObject

The payload of the packet.

For example, for a UDP packet captured from an Ethernet interface this is payload after the Ethernet, IP and UDP headers



296
297
298
# File 'lib/capp/packet.rb', line 296

def payload
  @captured[payload_offset, @capture_length]
end

#payload_offsetObject

The offset into the captured data where the payload starts.

Note that this method does not work properly for IPv6 packets with options set, but I have yet to encounter such an example in the wild.



306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/capp/packet.rb', line 306

def payload_offset
  offset =
    case @datalink
    when Capp::DLT_NULL then
      4
    when Capp::DLT_EN10MB then
      14
    end

  case
  when ipv4? then offset += @ipv4_header.ihl * 4
  when ipv6? then offset += 40
  else            raise NotImplementedError
  end

  case
  when tcp? then offset += @tcp_header.offset * 4
  when udp? then offset += 8
  else           raise NotImplementedError
  end

  offset
end

#resolve(address, resolver) ⇒ Object

:nodoc:



330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/capp/packet.rb', line 330

def resolve address, resolver # :nodoc:
  return address.dup unless resolver

  if name = ADDRESS_CACHE[address] then
    return name.dup
  end

  name = resolver.getname address

  ADDRESS_CACHE[address] = name

  name.dup
rescue Resolv::ResolvError
  ADDRESS_CACHE[address] = address
  address.dup
end

#source(resolver = nil) ⇒ Object

Returns the source of the packet regardless of protocol.

If a Resolv-compatible resolver is given the name will be looked up.



352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/capp/packet.rb', line 352

def source resolver = nil
  source =
    if ipv4? then
      @ipv4_header
    elsif ipv6? then
      @ipv6_header
    else
      raise NotImplementedError
    end.source.dup

  source = resolve source, resolver

  if tcp? then
    source << ".#{@tcp_header.source_port}"
  elsif udp? then
    source << ".#{@udp_header.source_port}"
  end

  source
end

#tcp?Boolean

Is this a TCP packet?

Returns:

  • (Boolean)


390
391
392
# File 'lib/capp/packet.rb', line 390

def tcp?
  @tcp_header
end

#udp?Boolean

Is this a UDP packet?

Returns:

  • (Boolean)


397
398
399
# File 'lib/capp/packet.rb', line 397

def udp?
  @udp_header
end