packetgen 3.1.1 → 3.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/pgconsole +1 -0
- data/lib/packetgen.rb +33 -4
- data/lib/packetgen/capture.rb +51 -28
- data/lib/packetgen/config.rb +17 -11
- data/lib/packetgen/deprecation.rb +34 -8
- data/lib/packetgen/header.rb +2 -9
- data/lib/packetgen/header/arp.rb +2 -2
- data/lib/packetgen/header/asn1_base.rb +2 -2
- data/lib/packetgen/header/base.rb +70 -74
- data/lib/packetgen/header/bootp.rb +3 -3
- data/lib/packetgen/header/dhcp.rb +2 -2
- data/lib/packetgen/header/dhcp/option.rb +2 -2
- data/lib/packetgen/header/dhcp/options.rb +2 -2
- data/lib/packetgen/header/dhcpv6.rb +12 -12
- data/lib/packetgen/header/dhcpv6/duid.rb +11 -5
- data/lib/packetgen/header/dhcpv6/option.rb +8 -16
- data/lib/packetgen/header/dhcpv6/options.rb +2 -2
- data/lib/packetgen/header/dhcpv6/relay.rb +2 -2
- data/lib/packetgen/header/dns.rb +9 -9
- data/lib/packetgen/header/dns/name.rb +20 -9
- data/lib/packetgen/header/dns/opt.rb +2 -2
- data/lib/packetgen/header/dns/option.rb +2 -2
- data/lib/packetgen/header/dns/qdsection.rb +3 -3
- data/lib/packetgen/header/dns/question.rb +37 -35
- data/lib/packetgen/header/dns/rr.rb +3 -3
- data/lib/packetgen/header/dns/rrsection.rb +2 -2
- data/lib/packetgen/header/dot11.rb +30 -51
- data/lib/packetgen/header/dot11/control.rb +5 -5
- data/lib/packetgen/header/dot11/data.rb +11 -7
- data/lib/packetgen/header/dot11/element.rb +16 -16
- data/lib/packetgen/header/dot11/management.rb +2 -2
- data/lib/packetgen/header/dot11/sub_mngt.rb +2 -12
- data/lib/packetgen/header/dot1q.rb +2 -2
- data/lib/packetgen/header/dot1x.rb +7 -20
- data/lib/packetgen/header/eap.rb +30 -33
- data/lib/packetgen/header/eap/fast.rb +2 -2
- data/lib/packetgen/header/eap/md5.rb +2 -2
- data/lib/packetgen/header/eap/tls.rb +2 -2
- data/lib/packetgen/header/eap/ttls.rb +2 -2
- data/lib/packetgen/header/eth.rb +13 -11
- data/lib/packetgen/header/gre.rb +2 -2
- data/lib/packetgen/header/http.rb +2 -0
- data/lib/packetgen/header/http/headers.rb +6 -4
- data/lib/packetgen/header/http/request.rb +36 -21
- data/lib/packetgen/header/http/response.rb +7 -7
- data/lib/packetgen/header/http/verbs.rb +3 -3
- data/lib/packetgen/header/icmp.rb +2 -2
- data/lib/packetgen/header/icmpv6.rb +2 -2
- data/lib/packetgen/header/igmp.rb +4 -4
- data/lib/packetgen/header/igmpv3.rb +3 -3
- data/lib/packetgen/header/igmpv3/group_record.rb +8 -6
- data/lib/packetgen/header/igmpv3/mq.rb +2 -2
- data/lib/packetgen/header/igmpv3/mr.rb +2 -2
- data/lib/packetgen/header/ip.rb +30 -31
- data/lib/packetgen/header/ip/addr.rb +10 -3
- data/lib/packetgen/header/ip/option.rb +8 -10
- data/lib/packetgen/header/ip/options.rb +3 -5
- data/lib/packetgen/header/ipv6.rb +2 -2
- data/lib/packetgen/header/ipv6/addr.rb +9 -2
- data/lib/packetgen/header/ipv6/extension.rb +2 -2
- data/lib/packetgen/header/ipv6/hop_by_hop.rb +3 -3
- data/lib/packetgen/header/llc.rb +2 -2
- data/lib/packetgen/header/mdns.rb +2 -2
- data/lib/packetgen/header/mld.rb +2 -2
- data/lib/packetgen/header/mldv2.rb +2 -2
- data/lib/packetgen/header/mldv2/mcast_address_record.rb +4 -2
- data/lib/packetgen/header/mldv2/mlq.rb +2 -2
- data/lib/packetgen/header/mldv2/mlr.rb +2 -2
- data/lib/packetgen/header/ospfv2.rb +9 -9
- data/lib/packetgen/header/ospfv2/db_description.rb +2 -2
- data/lib/packetgen/header/ospfv2/hello.rb +2 -2
- data/lib/packetgen/header/ospfv2/ls_ack.rb +2 -2
- data/lib/packetgen/header/ospfv2/ls_request.rb +4 -2
- data/lib/packetgen/header/ospfv2/ls_update.rb +2 -2
- data/lib/packetgen/header/ospfv2/lsa.rb +9 -5
- data/lib/packetgen/header/ospfv2/lsa_header.rb +8 -7
- data/lib/packetgen/header/ospfv3.rb +2 -2
- data/lib/packetgen/header/ospfv3/db_description.rb +2 -2
- data/lib/packetgen/header/ospfv3/hello.rb +2 -2
- data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +4 -2
- data/lib/packetgen/header/ospfv3/ls_ack.rb +2 -2
- data/lib/packetgen/header/ospfv3/ls_request.rb +4 -2
- data/lib/packetgen/header/ospfv3/ls_update.rb +2 -2
- data/lib/packetgen/header/ospfv3/lsa.rb +5 -5
- data/lib/packetgen/header/ospfv3/lsa_header.rb +9 -8
- data/lib/packetgen/header/snmp.rb +33 -29
- data/lib/packetgen/header/tcp.rb +4 -23
- data/lib/packetgen/header/tcp/option.rb +11 -11
- data/lib/packetgen/header/tcp/options.rb +2 -2
- data/lib/packetgen/header/tftp.rb +6 -6
- data/lib/packetgen/header/udp.rb +3 -3
- data/lib/packetgen/headerable.rb +5 -4
- data/lib/packetgen/inject.rb +23 -0
- data/lib/packetgen/inspect.rb +23 -20
- data/lib/packetgen/packet.rb +96 -53
- data/lib/packetgen/pcap.rb +29 -0
- data/lib/packetgen/pcapng.rb +13 -13
- data/lib/packetgen/pcapng/block.rb +26 -13
- data/lib/packetgen/pcapng/epb.rb +25 -22
- data/lib/packetgen/pcapng/file.rb +260 -138
- data/lib/packetgen/pcapng/idb.rb +36 -38
- data/lib/packetgen/pcapng/shb.rb +51 -53
- data/lib/packetgen/pcapng/spb.rb +19 -19
- data/lib/packetgen/pcapng/unknown_block.rb +5 -13
- data/lib/packetgen/pcaprub_wrapper.rb +81 -0
- data/lib/packetgen/proto.rb +2 -2
- data/lib/packetgen/types.rb +3 -0
- data/lib/packetgen/types/abstract_tlv.rb +28 -8
- data/lib/packetgen/types/array.rb +22 -15
- data/lib/packetgen/types/cstring.rb +40 -16
- data/lib/packetgen/types/enum.rb +8 -3
- data/lib/packetgen/types/fieldable.rb +65 -0
- data/lib/packetgen/types/fields.rb +182 -117
- data/lib/packetgen/types/int.rb +18 -6
- data/lib/packetgen/types/int_string.rb +10 -2
- data/lib/packetgen/types/length_from.rb +20 -12
- data/lib/packetgen/types/oui.rb +4 -2
- data/lib/packetgen/types/string.rb +46 -8
- data/lib/packetgen/types/tlv.rb +4 -2
- data/lib/packetgen/utils.rb +4 -4
- data/lib/packetgen/utils/arp_spoofer.rb +2 -2
- data/lib/packetgen/version.rb +3 -3
- metadata +35 -50
- data/.gitignore +0 -13
- data/.rubocop.yml +0 -28
- data/.travis.yml +0 -17
- data/Gemfile +0 -4
- data/Rakefile +0 -21
- data/packetgen.gemspec +0 -36
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file is part of PacketGen
|
4
|
+
# See https://github.com/sdaubert/packetgen for more informations
|
5
|
+
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
6
|
+
# This program is published under MIT license.
|
7
|
+
require_relative 'pcaprub_wrapper'
|
8
|
+
|
9
|
+
module PacketGen
|
10
|
+
# Module to read PCAP files
|
11
|
+
# @author Sylvain Daubert
|
12
|
+
# @api private
|
13
|
+
# @since 3.1.4
|
14
|
+
module Pcap
|
15
|
+
# Read a PCAP file
|
16
|
+
# @param [String] filename
|
17
|
+
# @return [Array<Packet>]
|
18
|
+
# @author Kent Gruber
|
19
|
+
def self.read(filename)
|
20
|
+
packets = []
|
21
|
+
PCAPRUBWrapper.read_pcap(filename: filename) do |packet|
|
22
|
+
next unless (packet = PacketGen.parse(packet.to_s))
|
23
|
+
|
24
|
+
packets << packet
|
25
|
+
end
|
26
|
+
packets
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/packetgen/pcapng.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This file is part of PacketGen
|
2
4
|
# See https://github.com/sdaubert/packetgen for more informations
|
3
5
|
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
4
6
|
# This program is published under MIT license.
|
5
7
|
|
6
|
-
# frozen_string_literal: true
|
7
|
-
|
8
8
|
require 'stringio'
|
9
9
|
|
10
10
|
module PacketGen
|
@@ -13,13 +13,13 @@ module PacketGen
|
|
13
13
|
# @author Sylvain Daubert
|
14
14
|
module PcapNG
|
15
15
|
# Section Header Block type number
|
16
|
-
SHB_TYPE = Types::Int32.new(0x0A0D0D0A, :little)
|
16
|
+
SHB_TYPE = Types::Int32.new(0x0A0D0D0A, :little).freeze
|
17
17
|
# Interface Description Block type number
|
18
|
-
IDB_TYPE = Types::Int32.new(1, :little)
|
18
|
+
IDB_TYPE = Types::Int32.new(1, :little).freeze
|
19
19
|
# Simple Packet Block type number
|
20
|
-
SPB_TYPE = Types::Int32.new(3, :little)
|
20
|
+
SPB_TYPE = Types::Int32.new(3, :little).freeze
|
21
21
|
# Enhanced Packet Block type number
|
22
|
-
EPB_TYPE = Types::Int32.new(6, :little)
|
22
|
+
EPB_TYPE = Types::Int32.new(6, :little).freeze
|
23
23
|
|
24
24
|
# IEEE 802.3 Ethernet (10Mb, 100Mb, 1000Mb, and up)
|
25
25
|
LINKTYPE_ETHERNET = 1
|
@@ -46,10 +46,10 @@ module PacketGen
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
require_relative 'pcapng/block
|
50
|
-
require_relative 'pcapng/unknown_block
|
51
|
-
require_relative 'pcapng/shb
|
52
|
-
require_relative 'pcapng/idb
|
53
|
-
require_relative 'pcapng/epb
|
54
|
-
require_relative 'pcapng/spb
|
55
|
-
require_relative 'pcapng/file
|
49
|
+
require_relative 'pcapng/block'
|
50
|
+
require_relative 'pcapng/unknown_block'
|
51
|
+
require_relative 'pcapng/shb'
|
52
|
+
require_relative 'pcapng/idb'
|
53
|
+
require_relative 'pcapng/epb'
|
54
|
+
require_relative 'pcapng/spb'
|
55
|
+
require_relative 'pcapng/file'
|
@@ -1,10 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This file is part of PacketGen
|
2
4
|
# See https://github.com/sdaubert/packetgen for more informations
|
3
5
|
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
4
6
|
# This program is published under MIT license.
|
5
7
|
|
6
|
-
# frozen_string_literal: true
|
7
|
-
|
8
8
|
module PacketGen
|
9
9
|
module PcapNG
|
10
10
|
# @abstract Base class for all block types
|
@@ -34,7 +34,7 @@ module PacketGen
|
|
34
34
|
# @return [Boolean]
|
35
35
|
# @since 2.7.0
|
36
36
|
def options?
|
37
|
-
@fields.key?(:options) && @fields[:options].sz
|
37
|
+
@fields.key?(:options) && @fields[:options].sz.positive?
|
38
38
|
end
|
39
39
|
|
40
40
|
# Calculate block length and update :block_len and block_len2 fields
|
@@ -49,9 +49,9 @@ module PacketGen
|
|
49
49
|
# @return [void]
|
50
50
|
def pad_field(*fields)
|
51
51
|
fields.each do |field|
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
obj = @fields[field]
|
53
|
+
pad_size = (obj.sz % 4).zero? ? 0 : (4 - (obj.sz % 4))
|
54
|
+
obj << "\x00" * pad_size
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
@@ -61,10 +61,8 @@ module PacketGen
|
|
61
61
|
# Must be called by all subclass #initialize method.
|
62
62
|
# @param [:little, :big] endian
|
63
63
|
# @return [:little, :big] returns endian
|
64
|
-
def
|
65
|
-
unless %i[little big].include?
|
66
|
-
raise ArgumentError, "unknown endianness for #{self.class}"
|
67
|
-
end
|
64
|
+
def endianness(endian)
|
65
|
+
raise ArgumentError, "unknown endianness for #{self.class}" unless %i[little big].include?(endian)
|
68
66
|
|
69
67
|
@endian = endian
|
70
68
|
@fields.each { |_f, v| v.endian = endian if v.is_a?(Types::Int) }
|
@@ -72,9 +70,24 @@ module PacketGen
|
|
72
70
|
end
|
73
71
|
|
74
72
|
def check_len_coherency
|
75
|
-
unless self.block_len == self.block_len2
|
76
|
-
|
77
|
-
|
73
|
+
raise InvalidFileError, 'Incoherency in Block length' unless self.block_len == self.block_len2
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_io(str_or_io)
|
77
|
+
return str_or_io if str_or_io.respond_to? :read
|
78
|
+
|
79
|
+
StringIO.new(force_binary(str_or_io.to_s))
|
80
|
+
end
|
81
|
+
|
82
|
+
def remove_padding(io, data_len)
|
83
|
+
data_pad_len = (4 - (data_len % 4)) % 4
|
84
|
+
io.read data_pad_len
|
85
|
+
data_pad_len
|
86
|
+
end
|
87
|
+
|
88
|
+
def read_blocklen2_and_check(io)
|
89
|
+
self[:block_len2].read io.read(4)
|
90
|
+
check_len_coherency
|
78
91
|
end
|
79
92
|
end
|
80
93
|
end
|
data/lib/packetgen/pcapng/epb.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This file is part of PacketGen
|
2
4
|
# See https://github.com/sdaubert/packetgen for more informations
|
3
5
|
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
4
6
|
# This program is published under MIT license.
|
5
7
|
|
6
|
-
# frozen_string_literal: true
|
7
|
-
|
8
8
|
module PacketGen
|
9
9
|
module PcapNG
|
10
10
|
# {EPB} represents a Enhanced Packet Block (EPB) of a pcapng file.
|
@@ -73,7 +73,7 @@ module PacketGen
|
|
73
73
|
# @option options [Integer] :block_len2 block total length
|
74
74
|
def initialize(options={})
|
75
75
|
super
|
76
|
-
|
76
|
+
endianness(options[:endian] || :little)
|
77
77
|
recalc_block_len
|
78
78
|
self.type = options[:type] || PcapNG::EPB_TYPE.to_i
|
79
79
|
end
|
@@ -82,29 +82,16 @@ module PacketGen
|
|
82
82
|
# @param [::String,IO] str_or_io
|
83
83
|
# @return [self]
|
84
84
|
def read(str_or_io)
|
85
|
-
io =
|
86
|
-
str_or_io
|
87
|
-
else
|
88
|
-
StringIO.new(force_binary(str_or_io.to_s))
|
89
|
-
end
|
85
|
+
io = to_io(str_or_io)
|
90
86
|
return self if io.eof?
|
91
87
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
self[:tsh].read io.read(4)
|
96
|
-
self[:tsl].read io.read(4)
|
97
|
-
self[:cap_len].read io.read(4)
|
98
|
-
self[:orig_len].read io.read(4)
|
88
|
+
%i[type block_len interface_id tsh tsl cap_len orig_len].each do |attr|
|
89
|
+
self[attr].read io.read(self[attr].sz)
|
90
|
+
end
|
99
91
|
self[:data].read io.read(self.cap_len)
|
100
|
-
|
101
|
-
io
|
102
|
-
options_len = self.block_len - self.cap_len - data_pad_len
|
103
|
-
options_len -= MIN_SIZE
|
104
|
-
self[:options].read io.read(options_len)
|
105
|
-
self[:block_len2].read io.read(4)
|
92
|
+
read_options(io)
|
93
|
+
read_blocklen2_and_check(io)
|
106
94
|
|
107
|
-
check_len_coherency
|
108
95
|
self
|
109
96
|
end
|
110
97
|
|
@@ -114,6 +101,16 @@ module PacketGen
|
|
114
101
|
Time.at((self.tsh << 32 | self.tsl) * ts_resol)
|
115
102
|
end
|
116
103
|
|
104
|
+
# Set timestamp from a Time object
|
105
|
+
# @param [Time] time
|
106
|
+
# @return [Time] time
|
107
|
+
def timestamp=(time)
|
108
|
+
tstamp = (time.to_r / ts_resol).to_i
|
109
|
+
self.tsh = (tstamp & 0xffffffff00000000) >> 32
|
110
|
+
self.tsl = tstamp & 0xffffffff
|
111
|
+
time
|
112
|
+
end
|
113
|
+
|
117
114
|
# Return the object as a String
|
118
115
|
# @return [String]
|
119
116
|
def to_s
|
@@ -131,6 +128,12 @@ module PacketGen
|
|
131
128
|
@interface.ts_resol
|
132
129
|
end
|
133
130
|
end
|
131
|
+
|
132
|
+
def read_options(io)
|
133
|
+
data_pad_len = remove_padding(io, self.cap_len)
|
134
|
+
options_len = self.block_len - self.cap_len - data_pad_len - MIN_SIZE
|
135
|
+
self[:options].read io.read(options_len)
|
136
|
+
end
|
134
137
|
end
|
135
138
|
end
|
136
139
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This file is part of PacketGen
|
2
4
|
# See https://github.com/sdaubert/packetgen for more informations
|
3
5
|
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
4
6
|
# This program is published under MIT license.
|
5
7
|
|
6
|
-
# frozen_string_literal: true
|
7
|
-
|
8
8
|
module PacketGen
|
9
9
|
module PcapNG
|
10
10
|
# PcapNG::File is a complete Pcap-NG file handler.
|
@@ -20,6 +20,15 @@ module PacketGen
|
|
20
20
|
LINKTYPE_IPV6 => 'IPv6'
|
21
21
|
}.freeze
|
22
22
|
|
23
|
+
# @private
|
24
|
+
BLOCK_TYPES = Hash[
|
25
|
+
PcapNG.constants(false).select { |c| c.to_s.include?('_TYPE') }.map do |c|
|
26
|
+
type_value = PcapNG.const_get(c).to_i
|
27
|
+
klass = PcapNG.const_get(c.to_s[0..-6]) # @todo use delete_suffix('_TYPE') when support for Ruby 2.4 will stop
|
28
|
+
[type_value, klass]
|
29
|
+
end
|
30
|
+
].freeze
|
31
|
+
|
23
32
|
# Get file sections
|
24
33
|
# @return [Array]
|
25
34
|
attr_accessor :sections
|
@@ -57,24 +66,15 @@ module PacketGen
|
|
57
66
|
# @return [Integer] return number of yielded blocks (only if a block is given)
|
58
67
|
# @raise [ArgumentError] cannot read +fname+
|
59
68
|
def readfile(fname, &blk)
|
60
|
-
unless ::File.readable?(fname)
|
61
|
-
raise ArgumentError, "cannot read file #{fname}"
|
62
|
-
end
|
63
|
-
|
64
|
-
::File.open(fname, 'rb') do |f|
|
65
|
-
parse_section(f) until f.eof?
|
66
|
-
end
|
69
|
+
raise ArgumentError, "cannot read file #{fname}" unless ::File.readable?(fname)
|
67
70
|
|
71
|
+
::File.open(fname, 'rb') { |f| parse_section(f) until f.eof? }
|
68
72
|
return unless blk
|
69
73
|
|
70
74
|
count = 0
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
count += 1
|
75
|
-
yield pkt
|
76
|
-
end
|
77
|
-
end
|
75
|
+
each_packet_with_interface do |pkt, _itf|
|
76
|
+
count += 1
|
77
|
+
yield pkt
|
78
78
|
end
|
79
79
|
count
|
80
80
|
end
|
@@ -91,12 +91,10 @@ module PacketGen
|
|
91
91
|
# @return [Integer] number of packets
|
92
92
|
# @raise [ArgumentError] cannot read +fname+
|
93
93
|
def read_packet_bytes(fname, &blk)
|
94
|
-
count = 0
|
95
94
|
packets = [] unless blk
|
96
95
|
|
97
|
-
readfile(fname) do |packet|
|
96
|
+
count = readfile(fname) do |packet|
|
98
97
|
if blk
|
99
|
-
count += 1
|
100
98
|
yield packet.data.to_s, packet.interface.link_type
|
101
99
|
else
|
102
100
|
packets << packet.data.to_s
|
@@ -117,19 +115,11 @@ module PacketGen
|
|
117
115
|
# @return [Integer] number of packets
|
118
116
|
# @raise [ArgumentError] cannot read +fname+
|
119
117
|
def read_packets(fname, &blk)
|
120
|
-
count = 0
|
121
118
|
packets = [] unless blk
|
122
119
|
|
123
|
-
read_packet_bytes(fname) do |packet, link_type|
|
124
|
-
|
125
|
-
parsed_pkt = if first_header.nil?
|
126
|
-
# unknown link type, try to guess
|
127
|
-
Packet.parse(packet)
|
128
|
-
else
|
129
|
-
Packet.parse(packet, first_header: first_header)
|
130
|
-
end
|
120
|
+
count = read_packet_bytes(fname) do |packet, link_type|
|
121
|
+
parsed_pkt = parse_packet(packet, link_type)
|
131
122
|
if blk
|
132
|
-
count += 1
|
133
123
|
yield parsed_pkt
|
134
124
|
else
|
135
125
|
packets << parsed_pkt
|
@@ -151,47 +141,70 @@ module PacketGen
|
|
151
141
|
@sections.clear
|
152
142
|
end
|
153
143
|
|
144
|
+
# @deprecated
|
145
|
+
# Prefer use of {#to_a} or {#to_h}.
|
154
146
|
# Translates a {File} into an array of packets.
|
155
147
|
# @param [Hash] options
|
156
|
-
# @option options [String] :
|
148
|
+
# @option options [String] :file if given, object is cleared and filename
|
157
149
|
# is analyzed before generating array. Else, array is generated from +self+
|
158
|
-
# @option options [String] :file same as +:filename+
|
159
150
|
# @option options [Boolean] :keep_timestamps if +true+ (default value: +false+),
|
160
151
|
# generates an array of hashes, each one with timestamp as key and packet
|
161
152
|
# as value. There is one hash per packet.
|
162
|
-
# @option options [Boolean] :keep_ts same as +:keep_timestamp+
|
163
153
|
# @return [Array<Packet>,Array<Hash>]
|
164
154
|
def file_to_array(options={})
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
155
|
+
Deprecation.deprecated(self.class, __method__)
|
156
|
+
|
157
|
+
file = options[:file] || options[:filename]
|
158
|
+
reread file
|
159
|
+
|
160
|
+
ary = []
|
161
|
+
blk = if options[:keep_timestamps] || options[:keep_ts]
|
162
|
+
proc { |pkt| { pkt.timestamp => pkt.data.to_s } }
|
163
|
+
else
|
164
|
+
proc { |pkt| pkt.data.to_s }
|
165
|
+
end
|
166
|
+
each_packet_with_interface do |pkt, _itf|
|
167
|
+
ary << blk.call(pkt)
|
169
168
|
end
|
170
169
|
|
170
|
+
ary
|
171
|
+
end
|
172
|
+
|
173
|
+
# Translates a {File} into an array of packets.
|
174
|
+
# @return [Array<Packet>]
|
175
|
+
# @since 3.1.6
|
176
|
+
def to_a
|
171
177
|
ary = []
|
172
|
-
|
173
|
-
|
174
|
-
if options[:keep_timestamps] || options[:keep_ts]
|
175
|
-
ary.concat(itf.packets.map { |pkt| { pkt.timestamp => pkt.data.to_s } })
|
176
|
-
else
|
177
|
-
ary.concat(itf.packets.map { |pkt| pkt.data.to_s })
|
178
|
-
end
|
179
|
-
end
|
178
|
+
each_packet_with_interface do |pkt, itf|
|
179
|
+
ary << parse_packet(pkt.data.to_s, itf.link_type)
|
180
180
|
end
|
181
|
+
|
181
182
|
ary
|
182
183
|
end
|
183
184
|
|
185
|
+
# Translates a {File} into a hash with timestamps as keys.
|
186
|
+
# @note Only packets from {EPB} sections are extracted, as {SPB} ones do not have timestamp.
|
187
|
+
# @return [Hash{Time => Packet}]
|
188
|
+
# @since 3.1.6
|
189
|
+
def to_h
|
190
|
+
hsh = {}
|
191
|
+
each_packet_with_interface do |pkt, itf|
|
192
|
+
next if pkt.is_a?(SPB)
|
193
|
+
|
194
|
+
hsh[pkt.timestamp] = parse_packet(pkt.data.to_s, itf.link_type)
|
195
|
+
end
|
196
|
+
|
197
|
+
hsh
|
198
|
+
end
|
199
|
+
|
184
200
|
# Writes the {File} to a file.
|
185
201
|
# @param [Hash] options
|
186
202
|
# @option options [Boolean] :append (default: +false+) if set to +true+,
|
187
203
|
# the packets are appended to the file, rather than overwriting it
|
188
204
|
# @return [Array] array of 2 elements: filename and size written
|
205
|
+
# @todo for 4.0, replace +options+ by +append+ kwarg
|
189
206
|
def to_file(filename, options={})
|
190
|
-
mode =
|
191
|
-
'ab'
|
192
|
-
else
|
193
|
-
'wb'
|
194
|
-
end
|
207
|
+
mode = (options[:append] && ::File.exist?(filename)) ? 'ab' : 'wb'
|
195
208
|
::File.open(filename, mode) { |f| f.write(self.to_s) }
|
196
209
|
[filename, self.to_s.size]
|
197
210
|
end
|
@@ -211,6 +224,7 @@ module PacketGen
|
|
211
224
|
self.to_file(filename.to_s, append: true)
|
212
225
|
end
|
213
226
|
|
227
|
+
# @deprecated Prefer use of {#read_array} or {#read_hash}.
|
214
228
|
# @overload array_to_file(ary)
|
215
229
|
# Update {File} object with packets.
|
216
230
|
# @param [Array] ary as generated by {#file_to_array} or Array of Packet objects.
|
@@ -219,7 +233,7 @@ module PacketGen
|
|
219
233
|
# @overload array_to_file(options={})
|
220
234
|
# Update {File} and/or write it to a file
|
221
235
|
# @param [Hash] options
|
222
|
-
# @option options [String] :
|
236
|
+
# @option options [String] :file file written on disk only if given
|
223
237
|
# @option options [Array] :array can either be an array of packet data,
|
224
238
|
# or a hash-value pair of timestamp => data.
|
225
239
|
# @option options [Time] :timestamp set an initial timestamp
|
@@ -229,53 +243,13 @@ module PacketGen
|
|
229
243
|
# the file
|
230
244
|
# @return [Array] see return value from {#to_file}
|
231
245
|
def array_to_file(options={})
|
232
|
-
|
233
|
-
when Hash
|
234
|
-
filename = options[:filename] || options[:file]
|
235
|
-
ary = options[:array] || options[:arr]
|
236
|
-
unless ary.is_a? Array
|
237
|
-
raise ArgumentError, ':array parameter needs to be an array'
|
238
|
-
end
|
239
|
-
ts = options[:timestamp] || options[:ts] || Time.now
|
240
|
-
ts_inc = options[:ts_inc] || 1
|
241
|
-
append = !options[:append].nil?
|
242
|
-
when Array
|
243
|
-
ary = options
|
244
|
-
ts = Time.now
|
245
|
-
ts_inc = 1
|
246
|
-
filename = nil
|
247
|
-
append = false
|
248
|
-
else
|
249
|
-
raise ArgumentError, 'unknown argument. Need either a Hash or Array'
|
250
|
-
end
|
246
|
+
filename, ary, ts, ts_inc, append = array_to_file_options(options)
|
251
247
|
|
252
|
-
section =
|
253
|
-
@sections << section
|
254
|
-
itf = IDB.new(endian: section.endian)
|
255
|
-
classify_block section, itf
|
248
|
+
section = create_new_shb_section
|
256
249
|
|
257
|
-
ary.
|
258
|
-
|
259
|
-
|
260
|
-
this_ts = pkt.keys.first.to_i
|
261
|
-
this_cap_len = pkt.values.first.to_s.size
|
262
|
-
this_data = pkt.values.first.to_s
|
263
|
-
else
|
264
|
-
this_ts = (ts + ts_inc * i).to_i
|
265
|
-
this_cap_len = pkt.to_s.size
|
266
|
-
this_data = pkt.to_s
|
267
|
-
end
|
268
|
-
this_ts = (this_ts / itf.ts_resol).to_i
|
269
|
-
this_tsh = this_ts >> 32
|
270
|
-
this_tsl = this_ts & 0xffffffff
|
271
|
-
this_pkt = EPB.new(endian: section.endian,
|
272
|
-
interface_id: 0,
|
273
|
-
tsh: this_tsh,
|
274
|
-
tsl: this_tsl,
|
275
|
-
cap_len: this_cap_len,
|
276
|
-
orig_len: this_cap_len,
|
277
|
-
data: this_data)
|
278
|
-
classify_block section, this_pkt
|
250
|
+
ary.each do |pkt|
|
251
|
+
classify_block(section, epb_from_pkt(pkt, section, ts))
|
252
|
+
ts += ts_inc
|
279
253
|
end
|
280
254
|
|
281
255
|
if filename
|
@@ -285,60 +259,104 @@ module PacketGen
|
|
285
259
|
end
|
286
260
|
end
|
287
261
|
|
262
|
+
# Update current object from an array of packets
|
263
|
+
# @param [Array<Packet>] packets
|
264
|
+
# @param [Time, nil] timestamp initial timestamp, used for first packet
|
265
|
+
# @param [Numeric, nil] ts_inc timestamp increment, in seconds, to increment
|
266
|
+
# initial timestamp for each packet
|
267
|
+
# @return [void]
|
268
|
+
# @note if +timestamp+ and/or +ts_inc+ are nil, {SPB} sections are created
|
269
|
+
# for each packet, else {EPB} ones are used
|
270
|
+
# @since 3.1.6
|
271
|
+
def read_array(packets, timestamp: nil, ts_inc: nil)
|
272
|
+
ts = timestamp
|
273
|
+
section = create_new_shb_section
|
274
|
+
packets.each do |pkt|
|
275
|
+
block = create_block_from_pkt(pkt, section, ts, ts_inc)
|
276
|
+
classify_block(section, block)
|
277
|
+
ts = update_ts(ts, ts_inc)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# Update current object from a hash of packets and timestamps
|
282
|
+
# @param [Hash{Time => Packet}] hsh
|
283
|
+
# @return [void]
|
284
|
+
# @since 3.1.6
|
285
|
+
def read_hash(hsh)
|
286
|
+
section = create_new_shb_section
|
287
|
+
hsh.each do |ts, pkt|
|
288
|
+
block = create_block_from_pkt(pkt, section, ts, 0)
|
289
|
+
classify_block(section, block)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
# @return [String]
|
294
|
+
# @since 3.1.6
|
295
|
+
def inspect
|
296
|
+
str = +''
|
297
|
+
sections.each do |section|
|
298
|
+
str << section.inspect
|
299
|
+
section.interfaces.each do |itf|
|
300
|
+
str << itf.inspect
|
301
|
+
itf.packets.each { |block| str << block.inspect }
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
str
|
306
|
+
end
|
307
|
+
|
288
308
|
private
|
289
309
|
|
290
310
|
# Parse a section. A section is made of at least a SHB. It than may contain
|
291
|
-
# others blocks, such as
|
311
|
+
# others blocks, such as IDB, SPB or EPB.
|
292
312
|
# @param [IO] io
|
293
313
|
# @return [void]
|
294
314
|
def parse_section(io)
|
295
|
-
shb = SHB.new
|
296
|
-
type = Types::Int32.new(0, shb.endian).read(io.read(4))
|
297
|
-
io.seek(-4, IO::SEEK_CUR)
|
298
|
-
shb = parse(type, io, shb)
|
315
|
+
shb = parse_shb(SHB.new, io)
|
299
316
|
raise InvalidFileError, 'no Section header found' unless shb.is_a?(SHB)
|
300
317
|
|
301
|
-
if shb.section_len.to_i != 0xffffffffffffffff
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
until io.eof?
|
313
|
-
shb = @sections.last
|
314
|
-
type = Types::Int32.new(0, shb.endian).read(io.read(4))
|
315
|
-
io.seek(-4, IO::SEEK_CUR)
|
316
|
-
parse(type, io, shb)
|
317
|
-
end
|
318
|
+
to_parse = if shb.section_len.to_i != 0xffffffffffffffff
|
319
|
+
# Section length is defined
|
320
|
+
StringIO.new(io.read(shb.section_len.to_i))
|
321
|
+
else
|
322
|
+
# section length is undefined
|
323
|
+
io
|
324
|
+
end
|
325
|
+
|
326
|
+
until to_parse.eof?
|
327
|
+
shb = @sections.last
|
328
|
+
parse_shb shb, to_parse
|
318
329
|
end
|
319
330
|
end
|
320
331
|
|
332
|
+
# Parse a SHB
|
333
|
+
# @param [SHB] shb SHB to parse
|
334
|
+
# @param [IO] io stream from which parse SHB
|
335
|
+
# @return [SHB]
|
336
|
+
def parse_shb(shb, io)
|
337
|
+
type = Types::Int32.new(0, shb.endian).read(io.read(4))
|
338
|
+
io.seek(-4, IO::SEEK_CUR)
|
339
|
+
parse(type, io, shb)
|
340
|
+
end
|
341
|
+
|
321
342
|
# Parse a block from its type
|
322
343
|
# @param [Types::Int32] type
|
323
344
|
# @param [IO] io stream from which parse block
|
324
345
|
# @param [SHB] shb header of current section
|
325
|
-
# @return [
|
346
|
+
# @return [Block]
|
326
347
|
def parse(type, io, shb)
|
327
|
-
|
328
|
-
.map { |c| [PcapNG.const_get(c).to_i, c] }
|
329
|
-
types = Hash[types]
|
330
|
-
|
331
|
-
if types.key?(type.to_i)
|
332
|
-
klass = PcapNG.const_get(types[type.to_i].to_s.gsub(/_TYPE/, '').to_sym)
|
333
|
-
block = klass.new(endian: shb.endian)
|
334
|
-
else
|
335
|
-
block = UnknownBlock.new(endian: shb.endian)
|
336
|
-
end
|
337
|
-
|
348
|
+
block = guess_block_type(type).new(endian: shb.endian)
|
338
349
|
classify_block shb, block
|
339
350
|
block.read(io)
|
340
351
|
end
|
341
352
|
|
353
|
+
# Guess class to use from type
|
354
|
+
# @param [Types::Int] type
|
355
|
+
# @return [Block]
|
356
|
+
def guess_block_type(type)
|
357
|
+
BLOCK_TYPES.key?(type.to_i) ? BLOCK_TYPES[type.to_i] : UnknownBlock
|
358
|
+
end
|
359
|
+
|
342
360
|
# Classify block from its type
|
343
361
|
# @param [SHB] shb header of current section
|
344
362
|
# @param [Block] block block to classify
|
@@ -349,18 +367,122 @@ module PacketGen
|
|
349
367
|
@sections << block
|
350
368
|
when IDB
|
351
369
|
shb << block
|
352
|
-
|
353
|
-
|
354
|
-
shb.interfaces[
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
370
|
+
when SPB, EPB
|
371
|
+
ifid = block.is_a?(EPB) ? block.interface_id : 0
|
372
|
+
shb.interfaces[ifid] << block
|
373
|
+
else
|
374
|
+
shb.add_unknown_block(block)
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
def array_to_file_options(options)
|
379
|
+
case options
|
380
|
+
when Hash
|
381
|
+
array_to_file_options_from_hash(options)
|
382
|
+
when Array
|
383
|
+
[nil, options, Time.now, 1, false]
|
384
|
+
else
|
385
|
+
raise ArgumentError, 'unknown argument. Need either a Hash or Array'
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
# Extract and check options for #array_to_file
|
390
|
+
def array_to_file_options_from_hash(options)
|
391
|
+
%i[filename arr ts].each do |deprecated_opt|
|
392
|
+
Deprecation.deprecated_option(self.class, :array_to_file, deprecated_opt) if options[deprecated_opt]
|
393
|
+
end
|
394
|
+
|
395
|
+
filename = options[:filename] || options[:file]
|
396
|
+
ary = options[:array] || options[:arr]
|
397
|
+
raise ArgumentError, ':array parameter needs to be an array' unless ary.is_a? Array
|
398
|
+
|
399
|
+
ts = options[:timestamp] || options[:ts] || Time.now
|
400
|
+
ts_inc = options[:ts_inc] || 1
|
401
|
+
append = !options[:append].nil?
|
402
|
+
|
403
|
+
[filename, ary, ts, ts_inc, append]
|
404
|
+
end
|
405
|
+
|
406
|
+
def create_new_shb_section
|
407
|
+
section = SHB.new
|
408
|
+
@sections << section
|
409
|
+
itf = IDB.new(endian: section.endian)
|
410
|
+
classify_block section, itf
|
411
|
+
|
412
|
+
section
|
413
|
+
end
|
414
|
+
|
415
|
+
# Compute tsh and tsl from ts
|
416
|
+
def calc_ts(timeslot, ts_resol)
|
417
|
+
this_ts = (timeslot / ts_resol).to_i
|
418
|
+
|
419
|
+
[this_ts >> 32, this_ts & 0xffffffff]
|
420
|
+
end
|
421
|
+
|
422
|
+
def reread(filename)
|
423
|
+
return if filename.nil?
|
424
|
+
|
425
|
+
clear
|
426
|
+
readfile filename
|
427
|
+
end
|
428
|
+
|
429
|
+
def create_block_from_pkt(pkt, section, timestamp, ts_inc)
|
430
|
+
if timestamp.nil? || ts_inc.nil?
|
431
|
+
spb_from_pkt(pkt, section)
|
359
432
|
else
|
360
|
-
|
361
|
-
block.section = shb
|
433
|
+
epb_from_pkt(pkt, section, timestamp)
|
362
434
|
end
|
363
435
|
end
|
436
|
+
|
437
|
+
def spb_from_pkt(pkt, section)
|
438
|
+
pkt_s = pkt.to_s
|
439
|
+
size = pkt_s.size
|
440
|
+
SPB.new(endian: section.endian,
|
441
|
+
block_len: size,
|
442
|
+
orig_len: size,
|
443
|
+
data: pkt_s)
|
444
|
+
end
|
445
|
+
|
446
|
+
# @todo remove hash case when #array_to_file will be removed
|
447
|
+
def epb_from_pkt(pkt, section, timestamp)
|
448
|
+
this_ts, this_data = case pkt
|
449
|
+
when Hash
|
450
|
+
[pkt.keys.first.to_i, pkt.values.first.to_s]
|
451
|
+
else
|
452
|
+
[timestamp.to_r, pkt.to_s]
|
453
|
+
end
|
454
|
+
this_cap_len = this_data.size
|
455
|
+
this_tsh, this_tsl = calc_ts(this_ts, section.interfaces.last.ts_resol)
|
456
|
+
EPB.new(endian: section.endian,
|
457
|
+
interface_id: 0,
|
458
|
+
tsh: this_tsh,
|
459
|
+
tsl: this_tsl,
|
460
|
+
cap_len: this_cap_len,
|
461
|
+
orig_len: this_cap_len,
|
462
|
+
data: this_data)
|
463
|
+
end
|
464
|
+
|
465
|
+
def update_ts(timestamp, ts_inc)
|
466
|
+
return nil if timestamp.nil? || ts_inc.nil?
|
467
|
+
|
468
|
+
timestamp + ts_inc
|
469
|
+
end
|
470
|
+
|
471
|
+
# Iterate over each xPB with its associated interface
|
472
|
+
# @return [void]
|
473
|
+
# @yieldparam [String] xpb
|
474
|
+
# @yieldparam [IDB] itf
|
475
|
+
def each_packet_with_interface
|
476
|
+
sections.each do |section|
|
477
|
+
section.interfaces.each do |itf|
|
478
|
+
itf.packets.each { |xpb| yield xpb, itf }
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
def parse_packet(data, link_type)
|
484
|
+
Packet.parse(data, first_header: KNOWN_LINK_TYPES[link_type])
|
485
|
+
end
|
364
486
|
end
|
365
487
|
end
|
366
488
|
end
|