Class: PacketGen::Packet

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

Overview

An object of type Packet handles a network packet. This packet may contain multiple protocol headers, starting from MAC layer or from Network (OSI) layer.

Creating a packet is fairly simple:

Packet.gen 'IP', src: '192.168.1.1', dst: '192.168.1.2'

Create a packet

Packets may be hand-made or parsed from a binary string:

Packet.gen('IP', src: '192.168.1.1', dst: '192.168.1.2').add('UDP', sport: 45000, dport: 23)
Packet.parse(binary_string)

Access packet information

pkt = Packet.gen('IP').add('UDP')
# read information
pkt.udp.sport
pkt.ip.ttl
# set information
pkt.udp.dport = 2323
pkt.ip.ttl = 1
pkt.ip(ttl: 1, id: 1234)

Save a packet to a file

pkt.write('file.pcapng')

Get packets

Packets may be captured from wire:

Packet.capture do |packet|
  do_some_stuffs
end
packets = Packet.capture(iface: 'eth0', max: 5)  # get 5 packets from eth0

Packets may also be read from a file:

packets = Packet.read(file.pcapng)

Save packets to a file

Packet.write 'file.pcapng', packets

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializePacket

Returns a new instance of Packet.



114
115
116
# File 'lib/packetgen/packet.rb', line 114

def initialize
  @headers = []
end

Instance Attribute Details

#headersArray<Header::Base] (readonly)

Returns Array<Header::Base].

Returns:



48
49
50
# File 'lib/packetgen/packet.rb', line 48

def headers
  @headers
end

Class Method Details

.capture(options = {}) {|packet| ... } ⇒ Array<Packet>

Capture packets

Parameters:

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

    capture options

Options Hash (options):

  • :iface (String)

    interface on which capture packets on. Default: Use default interface lookup.

  • :max (Integer)

    maximum number of packets to capture

  • :timeout (Integer)

    maximum number of seconds before end of capture

  • :filter (String)

    bpf filter

  • :promiscuous (Boolean)

Yield Parameters:

  • packet (Packet)

    if a block is given, yield each captured packet

Returns:

  • (Array<Packet>)

    captured packet



82
83
84
85
86
87
88
89
90
# File 'lib/packetgen/packet.rb', line 82

def self.capture(options={})
  capture = Capture.new(options)
  if block_given?
    capture.start { |packet| yield packet }
  else
    capture.start
  end
  capture.packets
end

.gen(protocol, options = {}) ⇒ Packet

Create a new Packet

Parameters:

  • protocol (String)

    base protocol for packet

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

    specific options for protocol

Returns:



54
55
56
# File 'lib/packetgen/packet.rb', line 54

def self.gen(protocol, options={})
  self.new.add protocol, options
end

.parse(binary_str, first_header: nil) ⇒ Packet

Parse a binary string and generate a Packet from it.

# auto-detect first header
Packet.parse str
# force decoding a Ethernet header for first header
Packet.parse str, first_header: 'Eth'

Parameters:

  • binary_str (String)
  • first_header (String, nil) (defaults to: nil)

    First protocol header. nil means discover it!

Returns:

Raises:

  • (ArgumentError)

    first_header is an unknown header



67
68
69
# File 'lib/packetgen/packet.rb', line 67

def self.parse(binary_str, first_header: nil)
  new.parse binary_str, first_header: first_header
end

.read(filename) ⇒ Array<Packet>

Read packets from filename.

For more control, see PacketGen::PcapNG::File.

Parameters:

  • filename (String)

    PcapNG file

Returns:



97
98
99
# File 'lib/packetgen/packet.rb', line 97

def self.read(filename)
  PcapNG::File.new.read_packets filename
end

.write(filename, packets) ⇒ void

This method returns an undefined value.

Write packets to filename

For more options, see PacketGen::PcapNG::File.

Parameters:

  • filename (String)
  • packets (Array<Packet>)

    packets to write



107
108
109
110
111
# File 'lib/packetgen/packet.rb', line 107

def self.write(filename, packets)
  pf = PcapNG::File.new
  pf.array_to_file packets
  pf.to_f filename
end

Instance Method Details

#==(other) ⇒ Boolean

Parameters:

Returns:

  • (Boolean)


267
268
269
# File 'lib/packetgen/packet.rb', line 267

def ==(other)
  to_s == other.to_s
end

#add(protocol, options = {}) ⇒ self

Add a protocol on packet stack

Parameters:

  • protocol (String)
  • options (Hash) (defaults to: {})

    protocol specific options

Returns:

  • (self)

Raises:

  • (ArgumentError)

    unknown protocol



123
124
125
126
127
128
129
# File 'lib/packetgen/packet.rb', line 123

def add(protocol, options={})
  klass = check_protocol(protocol)

  header = klass.new(options)
  add_header header
  self
end

#bodyTypes

Get packet body

Returns:



164
165
166
# File 'lib/packetgen/packet.rb', line 164

def body
  @headers.last.body if @headers.last.respond_to? :body
end

#body=(str) ⇒ void

This method returns an undefined value.

Set packet body

Parameters:

  • str (String)


171
172
173
# File 'lib/packetgen/packet.rb', line 171

def body=(str)
  @headers.last.body = str
end

#calcvoid

This method returns an undefined value.

Recalculate all calculatable fields (for now: length and checksum)



157
158
159
160
# File 'lib/packetgen/packet.rb', line 157

def calc
  calc_length
  calc_checksum
end

#calc_checksumvoid

This method returns an undefined value.

Recalculate all packet checksums



141
142
143
144
145
# File 'lib/packetgen/packet.rb', line 141

def calc_checksum
  @headers.reverse.each do |header|
    header.calc_checksum if header.respond_to? :calc_checksum
  end
end

#calc_lengthvoid

This method returns an undefined value.

Recalculate all packet length fields



149
150
151
152
153
# File 'lib/packetgen/packet.rb', line 149

def calc_length
  @headers.each do |header|
    header.calc_length if header.respond_to? :calc_length
  end
end

#decapsulate(*headers) ⇒ self

Remove headers from self

Parameters:

Returns:

  • (self)

    self with some headers removed

Raises:

Since:

  • 1.1.0



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

def decapsulate(*headers)
  headers.each do |header|
    idx = @headers.index(header)
    raise FormatError, 'header not in packet!' if idx.nil?

    prev_header = idx > 0 ? @headers[idx - 1] : nil
    next_header = (idx+1) < @headers.size ? @headers[idx + 1] : nil
    @headers.delete_at(idx)
    if prev_header and next_header
      add_header(next_header, previous_header: prev_header)
    end
  end
rescue ArgumentError => ex
  raise FormatError, ex.message
end

#encapsulate(other) ⇒ self

Encapulate another packet in self

Parameters:

Returns:

  • (self)

    self with new headers from other

Since:

  • 1.1.0



207
208
209
# File 'lib/packetgen/packet.rb', line 207

def encapsulate(other)
  other.headers.each { |h| add_header h }
end

#inspectString

Returns:

  • (String)


257
258
259
260
261
262
263
# File 'lib/packetgen/packet.rb', line 257

def inspect
  str = Inspect.dashed_line(self.class)
  @headers.each do |header|
    str << header.inspect
  end
  str << Inspect.inspect_body(body)
end

#is?(protocol) ⇒ Boolean

Check if a protocol header is embedded in packet

Returns:

  • (Boolean)

Raises:

  • (ArgumentError)

    unknown protocol



134
135
136
137
# File 'lib/packetgen/packet.rb', line 134

def is?(protocol)
  klass = check_protocol protocol
  @headers.any? { |h| h.is_a? klass }
end

#parse(binary_str, first_header: nil) ⇒ Packet

Parse a binary string and populate Packet from it.

Parameters:

  • binary_str (String)
  • first_header (String, nil) (defaults to: nil)

    First protocol header. nil means discover it!

Returns:

Raises:

  • (ArgumentError)

    first_header is an unknown header



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/packetgen/packet.rb', line 238

def parse(binary_str, first_header: nil)
  @headers.clear

  if first_header.nil?
    # No decoding forced for first header. Have to guess it!
    first_header = guess_first_header(binary_str)
    if first_header.nil?
      raise ParseError, 'cannot identify first header in string'
    end
  end
  add first_header
  @headers[-1, 1] = @headers.last.read(binary_str)

  # Decode upper headers recursively
  decode_bottom_up
  self
end

#to_f(filename) ⇒ Array Also known as: write

Write a PCapNG file to disk.

Parameters:

  • filename (String)

Returns:

See Also:

  • File


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

def to_f(filename)
  File.new.array_to_file(filename: filename, array: [self])
end

#to_sString

Get binary string

Returns:

  • (String)


177
178
179
# File 'lib/packetgen/packet.rb', line 177

def to_s
  @headers.first.to_s
end

#to_w(iface = nil) ⇒ void

This method returns an undefined value.

send packet on wire. Use first header #to_w method.

Parameters:

  • iface (String) (defaults to: nil)

    interface name. Default to first non-loopback interface



193
194
195
196
197
198
199
200
201
# File 'lib/packetgen/packet.rb', line 193

def to_w(iface=nil)
  iface ||= PacketGen.default_iface
  if @headers.first.respond_to? :to_w
    @headers.first.to_w(iface)
  else
    type = @headers.first.protocol_name
    raise WireError, "don't known how to send a #{type} packet on wire"
  end
end