packetgen 3.2.0 → 3.2.2
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/README.md +1 -1
- data/bin/pgconsole +3 -3
- data/lib/packetgen/header/arp.rb +24 -13
- data/lib/packetgen/header/asn1_base.rb +2 -2
- data/lib/packetgen/header/base.rb +1 -1
- data/lib/packetgen/header/dhcpv6/duid.rb +6 -0
- data/lib/packetgen/header/dhcpv6/option.rb +11 -13
- data/lib/packetgen/header/dns/opt.rb +2 -2
- data/lib/packetgen/header/dns/rr.rb +61 -28
- data/lib/packetgen/header/dns.rb +13 -6
- data/lib/packetgen/header/dot11/management.rb +2 -5
- data/lib/packetgen/header/dot11.rb +1 -1
- data/lib/packetgen/header/eap.rb +1 -1
- data/lib/packetgen/header/eth.rb +1 -1
- data/lib/packetgen/header/http/headers.rb +16 -1
- data/lib/packetgen/header/http/response.rb +43 -23
- data/lib/packetgen/header/igmp.rb +1 -1
- data/lib/packetgen/header/ip/option.rb +36 -10
- data/lib/packetgen/header/ipv6/addr.rb +3 -0
- data/lib/packetgen/header/mdns.rb +19 -25
- data/lib/packetgen/header/mld.rb +1 -1
- data/lib/packetgen/header/ospfv2/lsa_header.rb +2 -4
- data/lib/packetgen/header/ospfv2.rb +1 -1
- data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +14 -5
- data/lib/packetgen/header/ospfv3/lsa.rb +1 -1
- data/lib/packetgen/header/ospfv3/lsa_header.rb +2 -4
- data/lib/packetgen/header/ospfv3.rb +1 -1
- data/lib/packetgen/header/snmp.rb +39 -16
- data/lib/packetgen/header/tcp/option.rb +1 -1
- data/lib/packetgen/header/tcp.rb +12 -5
- data/lib/packetgen/header/tftp.rb +15 -9
- data/lib/packetgen/inspect.rb +15 -9
- data/lib/packetgen/packet.rb +48 -2
- data/lib/packetgen/pcapng/file.rb +13 -13
- data/lib/packetgen/pcapng.rb +1 -0
- data/lib/packetgen/pcaprub_wrapper.rb +0 -4
- data/lib/packetgen/types/abstract_tlv.rb +1 -1
- data/lib/packetgen/types/array.rb +35 -13
- data/lib/packetgen/types/fields.rb +19 -19
- data/lib/packetgen/types/int.rb +7 -0
- data/lib/packetgen/types/oui.rb +1 -1
- data/lib/packetgen/types/tlv.rb +17 -9
- data/lib/packetgen/unknown_packet.rb +3 -2
- data/lib/packetgen/utils.rb +67 -24
- data/lib/packetgen/version.rb +1 -1
- data/lib/packetgen.rb +3 -3
- metadata +3 -3
@@ -62,7 +62,7 @@ module PacketGen
|
|
62
62
|
HUMAN_SEPARATOR = ','
|
63
63
|
|
64
64
|
# rubocop:disable Naming/AccessorMethodName
|
65
|
-
class <<self
|
65
|
+
class << self
|
66
66
|
# Get class set with {.set_of}.
|
67
67
|
# @return [Class]
|
68
68
|
# @since 3.0.0
|
@@ -154,20 +154,16 @@ module PacketGen
|
|
154
154
|
self
|
155
155
|
end
|
156
156
|
|
157
|
-
# Populate object from a string
|
158
|
-
# @param [String]
|
157
|
+
# Populate object from a string or from an array of hashes
|
158
|
+
# @param [String, Array<Hash>] data
|
159
159
|
# @return [self]
|
160
|
-
def read(
|
160
|
+
def read(data)
|
161
161
|
clear
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
obj = create_object_from_str(str)
|
168
|
-
@array << obj
|
169
|
-
str.slice!(0, obj.sz)
|
170
|
-
break if @counter && self.size == @counter.to_i
|
162
|
+
case data
|
163
|
+
when ::Array
|
164
|
+
read_from_array(data)
|
165
|
+
else
|
166
|
+
read_from_string(data)
|
171
167
|
end
|
172
168
|
self
|
173
169
|
end
|
@@ -198,6 +194,28 @@ module PacketGen
|
|
198
194
|
|
199
195
|
private
|
200
196
|
|
197
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
198
|
+
|
199
|
+
def read_from_string(str)
|
200
|
+
return self if str.nil? || @counter&.to_i&.zero?
|
201
|
+
|
202
|
+
str = read_with_length_from(str)
|
203
|
+
until str.empty? || (@counter && self.size == @counter.to_i)
|
204
|
+
obj = create_object_from_str(str)
|
205
|
+
@array << obj
|
206
|
+
str.slice!(0, obj.sz)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
210
|
+
|
211
|
+
def read_from_array(ary)
|
212
|
+
return self if ary.empty?
|
213
|
+
|
214
|
+
ary.each do |hsh|
|
215
|
+
self << hsh
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
201
219
|
def record_from_hash(hsh)
|
202
220
|
obj_klass = self.class.set_of_klass
|
203
221
|
raise NotImplementedError, 'class should define #record_from_hash or declare type of elements in set with .set_of' unless obj_klass
|
@@ -228,18 +246,22 @@ module PacketGen
|
|
228
246
|
class ArrayOfInt8 < Array
|
229
247
|
set_of Int8
|
230
248
|
end
|
249
|
+
|
231
250
|
# Specialized array to handle serie of {Int16}.
|
232
251
|
class ArrayOfInt16 < Array
|
233
252
|
set_of Int16
|
234
253
|
end
|
254
|
+
|
235
255
|
# Specialized array to handle serie of {Int16le}.
|
236
256
|
class ArrayOfInt16le < Array
|
237
257
|
set_of Int16le
|
238
258
|
end
|
259
|
+
|
239
260
|
# Specialized array to handle serie of {Int32}.
|
240
261
|
class ArrayOfInt32 < Types::Array
|
241
262
|
set_of Types::Int32
|
242
263
|
end
|
264
|
+
|
243
265
|
# Specialized array to handle serie of {Int32le}.
|
244
266
|
class ArrayOfInt32le < Types::Array
|
245
267
|
set_of Types::Int32le
|
@@ -115,7 +115,7 @@ module PacketGen
|
|
115
115
|
# @private bit field definitions
|
116
116
|
@bit_fields = {}
|
117
117
|
|
118
|
-
class <<self
|
118
|
+
class << self
|
119
119
|
# Get field definitions for this class.
|
120
120
|
# @return [Hash]
|
121
121
|
# @since 3.1.0
|
@@ -282,7 +282,7 @@ module PacketGen
|
|
282
282
|
type = field_defs[attr].type
|
283
283
|
raise TypeError, "#{attr} is not a PacketGen::Types::Int" unless type < Types::Int
|
284
284
|
|
285
|
-
total_size = type.new.
|
285
|
+
total_size = type.new.nbits
|
286
286
|
idx = total_size - 1
|
287
287
|
|
288
288
|
until args.empty?
|
@@ -358,15 +358,15 @@ module PacketGen
|
|
358
358
|
clear_mask = compute_clear_mask(total_size, field_mask)
|
359
359
|
|
360
360
|
class_eval <<-METHODS
|
361
|
-
def #{name}?
|
362
|
-
val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
|
363
|
-
val != 0
|
364
|
-
end
|
365
|
-
def #{name}=(v)
|
366
|
-
val = v ? 1 : 0
|
367
|
-
self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
|
368
|
-
self[:#{attr}].value |= val << #{shift}
|
369
|
-
end
|
361
|
+
def #{name}? # def bit?
|
362
|
+
val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift} # val = (self[:attr}].to_i & 1}) >> 1
|
363
|
+
val != 0 # val != 0
|
364
|
+
end # end
|
365
|
+
def #{name}=(v) # def bit=(v)
|
366
|
+
val = v ? 1 : 0 # val = v ? 1 : 0
|
367
|
+
self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask} # self[:attr].value = self[:attr].to_i & 0xfffd
|
368
|
+
self[:#{attr}].value |= val << #{shift} # self[:attr].value |= val << 1
|
369
|
+
end # end
|
370
370
|
METHODS
|
371
371
|
end
|
372
372
|
|
@@ -375,13 +375,13 @@ module PacketGen
|
|
375
375
|
clear_mask = compute_clear_mask(total_size, field_mask)
|
376
376
|
|
377
377
|
class_eval <<-METHODS
|
378
|
-
def #{name}
|
379
|
-
(self[:#{attr}].to_i & #{field_mask}) >> #{shift}
|
380
|
-
end
|
381
|
-
def #{name}=(v)
|
382
|
-
self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
|
383
|
-
self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift}
|
384
|
-
end
|
378
|
+
def #{name} # def multibit
|
379
|
+
(self[:#{attr}].to_i & #{field_mask}) >> #{shift} # (self[:attr].to_i & 6) >> 1
|
380
|
+
end # end
|
381
|
+
def #{name}=(v) # def multibit=(v)
|
382
|
+
self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask} # self[:attr].value = self[:attr].to_i & 0xfff9
|
383
|
+
self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift} # self[:attr].value |= (v & 3) << 1
|
384
|
+
end # end
|
385
385
|
METHODS
|
386
386
|
end
|
387
387
|
|
@@ -523,7 +523,7 @@ module PacketGen
|
|
523
523
|
# Return object as a hash
|
524
524
|
# @return [Hash] keys: attributes, values: attribute values
|
525
525
|
def to_h
|
526
|
-
|
526
|
+
fields.map { |f| [f, @fields[f].to_human] }.to_h
|
527
527
|
end
|
528
528
|
|
529
529
|
# Get offset of given field in {Fields} structure.
|
data/lib/packetgen/types/int.rb
CHANGED
data/lib/packetgen/types/oui.rb
CHANGED
data/lib/packetgen/types/tlv.rb
CHANGED
@@ -56,12 +56,8 @@ module PacketGen
|
|
56
56
|
def initialize(options={})
|
57
57
|
Deprecation.deprecated_class(self.class, AbstractTLV)
|
58
58
|
super
|
59
|
-
|
60
|
-
|
61
|
-
self[:value] = options[:v].new if options[:v]
|
62
|
-
self.type = options[:type] if options[:type]
|
63
|
-
self.value = options[:value] if options[:value]
|
64
|
-
self.length = options[:length] if options[:length]
|
59
|
+
initialize_types(options)
|
60
|
+
initialize_values(options)
|
65
61
|
end
|
66
62
|
|
67
63
|
# Populate object from a binary string
|
@@ -137,9 +133,9 @@ module PacketGen
|
|
137
133
|
else
|
138
134
|
'%s'
|
139
135
|
end
|
140
|
-
|
141
|
-
"#{name} type:#{@typestr} length:#{
|
142
|
-
|
136
|
+
lenstr = "%-#{(2**self[:length].nbits - 1).to_s.size}u"
|
137
|
+
"#{name} type:#{@typestr} length:#{lenstr} value:#{value.inspect}" % [human_type,
|
138
|
+
length]
|
143
139
|
end
|
144
140
|
|
145
141
|
private
|
@@ -147,6 +143,18 @@ module PacketGen
|
|
147
143
|
def human_types?
|
148
144
|
self.class.const_defined? :TYPES
|
149
145
|
end
|
146
|
+
|
147
|
+
def initialize_types(options)
|
148
|
+
self[:type] = options[:t].new(self.type) if options[:t]
|
149
|
+
self[:length] = options[:l].new(self.length) if options[:l]
|
150
|
+
self[:value] = options[:v].new if options[:v]
|
151
|
+
end
|
152
|
+
|
153
|
+
def initialize_values(options)
|
154
|
+
self.type = options[:type] if options[:type]
|
155
|
+
self.value = options[:value] if options[:value]
|
156
|
+
self.length = options[:length] if options[:length]
|
157
|
+
end
|
150
158
|
end
|
151
159
|
end
|
152
160
|
end
|
@@ -61,7 +61,8 @@ module PacketGen
|
|
61
61
|
|
62
62
|
# @return [String]
|
63
63
|
def inspect
|
64
|
-
|
64
|
+
str = Inspect.dashed_line(self.class)
|
65
|
+
str << Inspect.inspect_body(body)
|
65
66
|
end
|
66
67
|
|
67
68
|
# equality if {#to_s} is equal
|
@@ -74,7 +75,7 @@ module PacketGen
|
|
74
75
|
# @return [Boolean]
|
75
76
|
def ===(other)
|
76
77
|
case other
|
77
|
-
when
|
78
|
+
when UnknownPacket
|
78
79
|
self == other
|
79
80
|
else
|
80
81
|
false
|
data/lib/packetgen/utils.rb
CHANGED
@@ -16,21 +16,62 @@ module PacketGen
|
|
16
16
|
# @author Sylvain Daubert
|
17
17
|
# @since 2.1.3
|
18
18
|
module Utils
|
19
|
+
# @private
|
20
|
+
ARP_FILTER = 'arp src %<ipaddr>s and ether dst %<hwaddr>s'
|
21
|
+
# @private
|
22
|
+
MITM_FILTER = '((ip src %<target1>s and not ip dst %<local_ip>s) or' \
|
23
|
+
' (ip src %<target2>s and not ip dst %<local_ip>s) or' \
|
24
|
+
' (ip dst %<target1>s and not ip src %<local_ip>s) or' \
|
25
|
+
' (ip dst %<target2>s and not ip src %<local_ip>s))' \
|
26
|
+
' and ether dst %<local_mac>s'
|
27
|
+
|
28
|
+
# @private
|
29
|
+
ARP_PATH = '/usr/sbin/arp'
|
30
|
+
# @private
|
31
|
+
IP_PATH = '/usr/bin/ip'
|
32
|
+
# @private
|
33
|
+
ARP_LINE_RE = /\((\d+\.\d+\.\d+\.\d+)\) at (([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2})(?: \[ether\])? on (\w+)/.freeze
|
34
|
+
# @private
|
35
|
+
IP_LINE_RE = /^(\d+\.\d+\.\d+\.\d+) dev (\w+) lladdr (([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2})/.freeze
|
36
|
+
|
19
37
|
# Get local ARP cache
|
20
38
|
# @return [Hash] key: IP address, value: array containing MAC address and
|
21
39
|
# interface name
|
22
40
|
def self.arp_cache
|
23
|
-
|
41
|
+
return self.cache_from_arp_command if File.exist?(ARP_PATH)
|
42
|
+
return self.cache_from_ip_command if File.exist?(IP_PATH)
|
43
|
+
|
44
|
+
{}
|
45
|
+
end
|
46
|
+
|
47
|
+
# @private
|
48
|
+
def self.cache_from_arp_command(raw_cache=nil)
|
49
|
+
raw_cache ||= `#{ARP_PATH} -an`
|
24
50
|
|
25
51
|
cache = {}
|
26
52
|
raw_cache.split("\n").each do |line|
|
27
|
-
match = line.match(
|
53
|
+
match = line.match(ARP_LINE_RE)
|
28
54
|
cache[match[1]] = [match[2], match[4]] if match
|
29
55
|
end
|
30
56
|
|
31
57
|
cache
|
32
58
|
end
|
33
59
|
|
60
|
+
# @private
|
61
|
+
def self.cache_from_ip_command(raw_cache=nil)
|
62
|
+
raw_cache ||= `#{IP_PATH} neigh`
|
63
|
+
|
64
|
+
cache = {}
|
65
|
+
raw_cache.split("\n").each do |line|
|
66
|
+
match = line.match(IP_LINE_RE)
|
67
|
+
cache[match[1]] = [match[3], match[2]] if match
|
68
|
+
end
|
69
|
+
|
70
|
+
cache
|
71
|
+
end
|
72
|
+
|
73
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize
|
74
|
+
|
34
75
|
# Get MAC address from an IP address, or nil if this IP address is unknown
|
35
76
|
# on local network.
|
36
77
|
# @param [String] ipaddr dotted-octet IP address
|
@@ -40,7 +81,7 @@ module PacketGen
|
|
40
81
|
# @option options [Boolean] :no_cache if +true+, do not query local ARP
|
41
82
|
# cache and always send an ARP request on wire. Default to +false+
|
42
83
|
# @option options [Integer] :timeout timeout in seconds before stopping
|
43
|
-
# request. Default to
|
84
|
+
# request. Default to 1.
|
44
85
|
# @return [String,nil]
|
45
86
|
# @raise [RuntimeError] user don't have permission to capture packets on network device.
|
46
87
|
def self.arp(ipaddr, options={})
|
@@ -57,10 +98,10 @@ module PacketGen
|
|
57
98
|
spa: Config.instance.ipaddr(iface),
|
58
99
|
tpa: ipaddr)
|
59
100
|
|
60
|
-
capture = Capture.new(iface: iface, timeout: timeout, max: 1,
|
61
|
-
filter: "arp src #{ipaddr} and ether dst #{my_hwaddr}")
|
101
|
+
capture = Capture.new(iface: iface, timeout: timeout, max: 1, filter: ARP_FILTER % { ipaddr: ipaddr, hwaddr: my_hwaddr })
|
62
102
|
cap_thread = Thread.new { capture.start }
|
63
103
|
|
104
|
+
sleep 0.1
|
64
105
|
arp_pkt.to_w(iface)
|
65
106
|
cap_thread.join
|
66
107
|
|
@@ -70,6 +111,7 @@ module PacketGen
|
|
70
111
|
break pkt.arp.sha.to_s if pkt.arp.spa.to_s == ipaddr
|
71
112
|
end
|
72
113
|
end
|
114
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize
|
73
115
|
|
74
116
|
# Do ARP spoofing on given IP address. Call to this method blocks.
|
75
117
|
# @note This method is provided for test purpose.
|
@@ -122,39 +164,40 @@ module PacketGen
|
|
122
164
|
# end
|
123
165
|
# @since 2.2.0
|
124
166
|
# @raise [RuntimeError] user don't have permission to capture packets on network device.
|
125
|
-
def self.mitm(target1, target2, options={})
|
167
|
+
def self.mitm(target1, target2, options={}, &block)
|
126
168
|
options = { iface: PacketGen.default_iface }.merge(options)
|
127
169
|
|
128
|
-
mac1 = arp(target1)
|
129
|
-
mac2 = arp(target2)
|
130
|
-
|
131
170
|
spoofer = Utils::ARPSpoofer.new(options)
|
132
171
|
spoofer.add target1, target2, options
|
133
172
|
spoofer.add target2, target1, options
|
134
173
|
|
135
|
-
|
136
|
-
|
174
|
+
cfg = Config.instance
|
175
|
+
my_mac = cfg.hwaddr(options[:iface])
|
137
176
|
capture = Capture.new(iface: options[:iface],
|
138
|
-
filter:
|
139
|
-
" (ip src #{target2} and not ip dst #{my_ip}) or" \
|
140
|
-
" (ip dst #{target1} and not ip src #{my_ip}) or" \
|
141
|
-
" (ip dst #{target2} and not ip src #{my_ip}))" \
|
142
|
-
" and ether dst #{my_mac}")
|
177
|
+
filter: MITM_FILTER % { target1: target1, target2: target2, local_ip: cfg.ipaddr(options[:iface]), local_mac: my_mac })
|
143
178
|
|
144
179
|
spoofer.start_all
|
180
|
+
mitm_core(capture, target1, target2, my_mac, &block)
|
181
|
+
spoofer.stop_all
|
182
|
+
end
|
183
|
+
|
184
|
+
# @private
|
185
|
+
def self.mitm_core(capture, target1, target2, my_mac)
|
186
|
+
mac1 = arp(target1)
|
187
|
+
mac2 = arp(target2)
|
188
|
+
|
145
189
|
capture.start do |pkt|
|
146
190
|
modified_pkt = yield pkt
|
147
191
|
iph = modified_pkt.ip
|
148
192
|
l2 = modified_pkt.is?('Dot11') ? modified_pkt.dot11 : modified_pkt.eth
|
149
193
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
modified_pkt.to_w(options[:iface])
|
194
|
+
l2.src = my_mac
|
195
|
+
l2.dst = if (iph.src == target1) || (iph.dst == target2)
|
196
|
+
mac2
|
197
|
+
else # (iph.src == target2) || (iph.dst == target1)
|
198
|
+
mac1
|
199
|
+
end
|
200
|
+
modified_pkt.to_w(capture.iface)
|
158
201
|
end
|
159
202
|
end
|
160
203
|
end
|
data/lib/packetgen/version.rb
CHANGED
data/lib/packetgen.rb
CHANGED
@@ -39,9 +39,9 @@ module PacketGen
|
|
39
39
|
|
40
40
|
def message
|
41
41
|
"#{prev_hdr.class} knowns no layer association with #{hdr.protocol_name}. " \
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
"Try #{prev_hdr.class}.bind_layer(#{hdr.class}, " \
|
43
|
+
"#{prev_hdr.method_name}_proto_field: " \
|
44
|
+
"<value_for_#{hdr.method_name}>)"
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: packetgen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.2.
|
4
|
+
version: 3.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sylvain Daubert
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-12-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: interfacez
|
@@ -260,7 +260,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
260
260
|
requirements:
|
261
261
|
- - ">="
|
262
262
|
- !ruby/object:Gem::Version
|
263
|
-
version: 2.
|
263
|
+
version: 2.5.0
|
264
264
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
265
265
|
requirements:
|
266
266
|
- - ">="
|