Class: PacketGen::Packet
- Inherits:
-
Object
- Object
- PacketGen::Packet
- 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.
A Packet is created using Packet.gen method. Headers may be added to this packet using #add. It may also be created from parsing a binary string, using Packet.parse method.
Information for each level is accessible through the associated header. Header is accessible through a method defined with its name at packet level (i.e. #ip
for a IP header).
Packets may be written to files using #write:
pkt = PacketGen::Packet.gen('Eth')
pkt.write('file.pcapng')
Packets may be captured from network interfaces:
# Capture all packets from default interface. Never end.
PacketGen::Packet.capture do |packet|
do_some_stuffs
end
# Get 5 packets from eth0 interface
packets = Packet.capture(iface: 'eth0', max: 5)
Finally, packets may also be read from a file:
packets = Packet.read(file.pcapng)
Instance Attribute Summary collapse
-
#cache_headers ⇒ Boolean
Activaye or deactivate header cache (activated by default).
-
#headers ⇒ Array<Headerable>
readonly
Get packet headers, ordered as they appear in the packet.
Class Method Summary collapse
-
.capture(**kwargs) {|packet, timestamp| ... } ⇒ Array<Packet>
Capture packets from wire.
-
.gen(protocol, options = {}) ⇒ Packet
Create a new Packet.
-
.parse(binary_str, first_header: nil) ⇒ Packet
Parse a binary string and generate a Packet from it.
-
.read(filename) ⇒ Array<Packet>
Read packets from
filename
. -
.write(filename, packets) ⇒ void
Write packets to
filename
, as pcap or PcapNG file.
Instance Method Summary collapse
-
#<<(header) ⇒ self
Append an already defined header to packet.
-
#==(other) ⇒ Boolean
Check equality at binary level.
-
#===(other) ⇒ Boolean
true
if #== istrue
with another packet, or ifother
is a protocol name String, whose protocol is in Packet. -
#add(protocol, options = {}) ⇒ self
Add a protocol header in packet.
-
#body ⇒ Headerable, ...
Get packet body.
-
#body=(str) ⇒ void
Set packet body.
-
#calc ⇒ void
Recalculate all calculatable fields (for now: length and checksum).
-
#calc_checksum ⇒ void
Recalculate all packet checksums.
-
#calc_length ⇒ void
Recalculate all packet length fields.
-
#decapsulate(*hdrs) ⇒ self
Remove headers from
self
. -
#encapsulate(other, parsing: false) ⇒ self
Encapulate another packet in
self
. -
#initialize ⇒ Packet
constructor
A new instance of Packet.
-
#insert(prev, protocol, options = {}) ⇒ self
Insert a header in packet.
-
#inspect ⇒ String
Get packet as a pretty formatted string.
-
#is?(protocol) ⇒ Boolean
Check if a protocol header is embedded in packet.
-
#parse(binary_str, first_header: nil) ⇒ self
Parse a binary string and populate Packet from it.
-
#reply ⇒ Packet
Forge a new packet from current one with all possible fields inverted.
-
#reply! ⇒ self
Invert all possible attributes in packet to create a reply.
-
#to_f(filename) ⇒ Array
(also: #write)
Write packet to a PCapNG file on disk.
-
#to_s ⇒ String
Get binary string (i.e. binary string sent on or received from network).
-
#to_w(iface = nil, calc: true, number: 1, interval: 1) ⇒ void
Send packet on wire.
Constructor Details
#initialize ⇒ Packet
Returns a new instance of Packet.
143 144 145 146 147 |
# File 'lib/packetgen/packet.rb', line 143 def initialize @headers = [] @header_cache = {} @cache_headers = true end |
Instance Attribute Details
#cache_headers ⇒ Boolean
Activaye or deactivate header cache (activated by default)
65 66 67 |
# File 'lib/packetgen/packet.rb', line 65 def cache_headers @cache_headers end |
#headers ⇒ Array<Headerable> (readonly)
Get packet headers, ordered as they appear in the packet.
62 63 64 |
# File 'lib/packetgen/packet.rb', line 62 def headers @headers end |
Class Method Details
.capture(**kwargs) {|packet, timestamp| ... } ⇒ Array<Packet>
Capture packets from wire. Same arguments as Capture#initialize
96 97 98 99 100 101 102 103 104 |
# File 'lib/packetgen/packet.rb', line 96 def self.capture(**kwargs, &block) capture = Capture.new(**kwargs) if block capture.start(&block) else capture.start end capture.packets end |
.gen(protocol, options = {}) ⇒ Packet
Create a new Packet
71 72 73 |
# File 'lib/packetgen/packet.rb', line 71 def self.gen(protocol, ={}) self.new.add(protocol, ) end |
.parse(binary_str, first_header: nil) ⇒ Packet
84 85 86 |
# File 'lib/packetgen/packet.rb', line 84 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
. May read Pcap and Pcap-NG formats.
For more control (on Pcap-ng only), see PacketGen::PcapNG::File.
115 116 117 118 119 120 121 |
# File 'lib/packetgen/packet.rb', line 115 def self.read(filename) PcapNG::File.new.read_packets(filename) rescue StandardError => e raise ArgumentError, e unless File.extname(filename.downcase) == '.pcap' Pcap.read(filename) end |
.write(filename, packets) ⇒ void
This method returns an undefined value.
Write packets to filename
, as pcap or PcapNG file
For more options, see PacketGen::PcapNG::File.
132 133 134 135 136 137 138 139 140 |
# File 'lib/packetgen/packet.rb', line 132 def self.write(filename, packets) if filename.end_with?('.pcap') Pcap.write(filename, packets) else pf = PcapNG::File.new pf.read_array(packets) pf.to_f(filename) end end |
Instance Method Details
#<<(header) ⇒ self
Append an already defined header to packet
424 425 426 427 |
# File 'lib/packetgen/packet.rb', line 424 def <<(header) add_header(header) self end |
#==(other) ⇒ Boolean
Check equality at binary level
370 371 372 |
# File 'lib/packetgen/packet.rb', line 370 def ==(other) to_s == other.to_s end |
#===(other) ⇒ Boolean
true
if #== is true
with another packet, or if other
is a protocol name String, whose protocol is in Packet.
378 379 380 381 382 383 384 385 386 387 |
# File 'lib/packetgen/packet.rb', line 378 def ===(other) case other when PacketGen::Packet self == other when String is?(other) else false end end |
#add(protocol, options = {}) ⇒ self
Add a protocol header in packet.
162 163 164 165 166 167 168 169 170 |
# File 'lib/packetgen/packet.rb', line 162 def add(protocol, ={}) klass = check_protocol(protocol) # options[:packet]= self is speedier than options.merge(packet: self) [:packet] = self header = klass.new() add_header(header) self end |
#body ⇒ Headerable, ...
Get packet body. If packet (i.e. last header) has no :body
field, return nil
.
232 233 234 |
# File 'lib/packetgen/packet.rb', line 232 def body last_header[:body] if last_header.respond_to?(:body) end |
#body=(str) ⇒ void
To set a Headerable object, prefer ##<<
This method returns an undefined value.
Set packet body
243 244 245 246 247 |
# File 'lib/packetgen/packet.rb', line 243 def body=(str) raise Error, 'no body in last headeré' unless last_header.respond_to?(:body) last_header.body = str end |
#calc ⇒ void
This method returns an undefined value.
Recalculate all calculatable fields (for now: length and checksum)
223 224 225 226 227 228 |
# File 'lib/packetgen/packet.rb', line 223 def calc headers.reverse_each do |header| header.calc_length if header.respond_to?(:calc_length) header.calc_checksum if header.respond_to?(:calc_checksum) end end |
#calc_checksum ⇒ void
This method returns an undefined value.
Recalculate all packet checksums
206 207 208 209 210 |
# File 'lib/packetgen/packet.rb', line 206 def calc_checksum headers.reverse_each do |header| header.calc_checksum if header.respond_to?(:calc_checksum) end end |
#calc_length ⇒ void
This method returns an undefined value.
Recalculate all packet length fields
214 215 216 217 218 |
# File 'lib/packetgen/packet.rb', line 214 def calc_length headers.reverse_each do |header| header.calc_length if header.respond_to?(:calc_length) end end |
#decapsulate(*hdrs) ⇒ self
Remove headers from self
321 322 323 324 325 326 327 328 329 330 331 332 |
# File 'lib/packetgen/packet.rb', line 321 def decapsulate(*hdrs) hdrs.each do |hdr| prev_hdr = previous_header(hdr) next_hdr = next_header(hdr) headers.delete(hdr) add_header(next_hdr, previous_header: prev_hdr) if prev_hdr && next_hdr end invalidate_header_cache self rescue ArgumentError => e raise FormatError, e. end |
#encapsulate(other, parsing: false) ⇒ self
Encapulate another packet in self
303 304 305 306 307 |
# File 'lib/packetgen/packet.rb', line 303 def encapsulate(other, parsing: false) other.headers.each_with_index do |h, i| add_header(h, parsing: i.positive? || parsing) end end |
#insert(prev, protocol, options = {}) ⇒ self
Insert a header in packet
178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/packetgen/packet.rb', line 178 def insert(prev, protocol, ={}) klass = check_protocol(protocol) nxt = prev.body # options[:packet]= self is speedier than options.merge(packet: self) [:packet] = self header = klass.new() add_header(header, previous_header: prev) idx = headers.index(prev) + 1 headers[idx, 0] = header header[:body] = nxt self end |
#inspect ⇒ String
Get packet as a pretty formatted string.
359 360 361 362 363 364 365 |
# File 'lib/packetgen/packet.rb', line 359 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.
199 200 201 202 |
# File 'lib/packetgen/packet.rb', line 199 def is?(protocol) klass = check_protocol(protocol) headers.any?(klass) end |
#parse(binary_str, first_header: nil) ⇒ self
Parse a binary string and populate Packet from it.
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 |
# File 'lib/packetgen/packet.rb', line 340 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) raise ParseError, "cannot identify first header in string: #{binary_str.inspect}" if first_header.nil? end add(first_header) headers[-1, 1] = last_header.read(binary_str) # Decode upper headers recursively decode_bottom_up self end |
#reply ⇒ Packet
Only modify headers responding to #reply!
.
Forge a new packet from current one with all possible fields inverted. The new packet may be a reply to current one.
405 406 407 408 |
# File 'lib/packetgen/packet.rb', line 405 def reply pkt = dup pkt.reply! end |
#reply! ⇒ self
Only modify headers responding to #reply!
.
Invert all possible attributes in packet to create a reply.
393 394 395 396 397 398 |
# File 'lib/packetgen/packet.rb', line 393 def reply! headers.each do |header| header.reply! if header.respond_to?(:reply!) end self end |
#to_f(filename) ⇒ Array Also known as: write
Write packet to a PCapNG file on disk.
259 260 261 |
# File 'lib/packetgen/packet.rb', line 259 def to_f(filename) PcapNG::File.new.read_array([self]).to_f(filename) end |
#to_s ⇒ String
Get binary string (i.e. binary string sent on or received from network).
251 252 253 |
# File 'lib/packetgen/packet.rb', line 251 def to_s first_header.to_s end |
#to_w(iface = nil, calc: true, number: 1, interval: 1) ⇒ void
This method returns an undefined value.
Send packet on wire. Use first header #to_w
method.
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/packetgen/packet.rb', line 272 def to_w(iface=nil, calc: true, number: 1, interval: 1) iface ||= PacketGen.default_iface if first_header.respond_to?(:to_w) self.calc if calc number.times do first_header.to_w(iface) sleep(interval) if number > 1 end else type = first_header.protocol_name raise WireError, "don't known how to send a #{type} packet on wire" end end |