packetgen 3.3.3 → 4.1.0
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 +38 -22
- data/lib/packetgen/capture.rb +2 -2
- data/lib/packetgen/config.rb +0 -1
- data/lib/packetgen/deprecation.rb +14 -8
- data/lib/packetgen/header/arp.rb +17 -18
- data/lib/packetgen/header/asn1_base.rb +2 -1
- data/lib/packetgen/header/base.rb +42 -40
- data/lib/packetgen/header/bootp.rb +35 -37
- data/lib/packetgen/header/dhcp/option.rb +21 -21
- data/lib/packetgen/header/dhcp/options.rb +3 -3
- data/lib/packetgen/header/dhcp.rb +8 -9
- data/lib/packetgen/header/dhcpv6/duid.rb +16 -16
- data/lib/packetgen/header/dhcpv6/option.rb +83 -61
- data/lib/packetgen/header/dhcpv6/options.rb +4 -4
- data/lib/packetgen/header/dhcpv6/relay.rb +6 -5
- data/lib/packetgen/header/dhcpv6.rb +17 -18
- data/lib/packetgen/header/dns/name.rb +21 -16
- data/lib/packetgen/header/dns/opt.rb +5 -2
- data/lib/packetgen/header/dns/option.rb +14 -14
- data/lib/packetgen/header/dns/qdsection.rb +3 -3
- data/lib/packetgen/header/dns/question.rb +7 -8
- data/lib/packetgen/header/dns/rr.rb +56 -43
- data/lib/packetgen/header/dns/rrsection.rb +6 -6
- data/lib/packetgen/header/dns.rb +103 -90
- data/lib/packetgen/header/dot11/control.rb +12 -12
- data/lib/packetgen/header/dot11/data.rb +25 -24
- data/lib/packetgen/header/dot11/element.rb +4 -4
- data/lib/packetgen/header/dot11/management.rb +21 -18
- data/lib/packetgen/header/dot11/sub_mngt.rb +40 -53
- data/lib/packetgen/header/dot11.rb +117 -122
- data/lib/packetgen/header/dot1q.rb +12 -13
- data/lib/packetgen/header/dot1x.rb +13 -13
- data/lib/packetgen/header/eap/fast.rb +4 -4
- data/lib/packetgen/header/eap/md5.rb +16 -8
- data/lib/packetgen/header/eap/tls.rb +18 -19
- data/lib/packetgen/header/eap/ttls.rb +22 -21
- data/lib/packetgen/header/eap.rb +73 -48
- data/lib/packetgen/header/eth.rb +41 -27
- data/lib/packetgen/header/gre.rb +33 -11
- data/lib/packetgen/header/http/headers.rb +7 -6
- data/lib/packetgen/header/http/request.rb +38 -29
- data/lib/packetgen/header/http/response.rb +35 -27
- data/lib/packetgen/header/http/verbs.rb +1 -3
- data/lib/packetgen/header/icmp.rb +14 -14
- data/lib/packetgen/header/icmpv6.rb +10 -9
- data/lib/packetgen/header/igmp.rb +26 -15
- data/lib/packetgen/header/igmpv3/group_record.rb +18 -13
- data/lib/packetgen/header/igmpv3/mq.rb +16 -18
- data/lib/packetgen/header/igmpv3/mr.rb +5 -5
- data/lib/packetgen/header/igmpv3.rb +12 -11
- data/lib/packetgen/header/ip/addr.rb +19 -15
- data/lib/packetgen/header/ip/option.rb +47 -36
- data/lib/packetgen/header/ip/options.rb +1 -1
- data/lib/packetgen/header/ip.rb +77 -95
- data/lib/packetgen/header/ipv6/addr.rb +28 -27
- data/lib/packetgen/header/ipv6/extension.rb +13 -11
- data/lib/packetgen/header/ipv6/hop_by_hop.rb +32 -13
- data/lib/packetgen/header/ipv6.rb +42 -35
- data/lib/packetgen/header/llc.rb +28 -21
- data/lib/packetgen/header/mdns.rb +10 -3
- data/lib/packetgen/header/mld.rb +15 -13
- data/lib/packetgen/header/mldv2/mcast_address_record.rb +17 -12
- data/lib/packetgen/header/mldv2/mlq.rb +22 -24
- data/lib/packetgen/header/mldv2/mlr.rb +8 -8
- data/lib/packetgen/header/mldv2.rb +1 -1
- data/lib/packetgen/header/ospfv2/db_description.rb +17 -18
- data/lib/packetgen/header/ospfv2/hello.rb +18 -17
- data/lib/packetgen/header/ospfv2/ls_ack.rb +6 -7
- data/lib/packetgen/header/ospfv2/ls_request.rb +14 -13
- data/lib/packetgen/header/ospfv2/ls_update.rb +9 -9
- data/lib/packetgen/header/ospfv2/lsa.rb +79 -60
- data/lib/packetgen/header/ospfv2/lsa_header.rb +12 -11
- data/lib/packetgen/header/ospfv2.rb +49 -46
- data/lib/packetgen/header/ospfv3/db_description.rb +20 -22
- data/lib/packetgen/header/ospfv3/hello.rb +17 -16
- data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +22 -20
- data/lib/packetgen/header/ospfv3/ls_ack.rb +7 -8
- data/lib/packetgen/header/ospfv3/ls_request.rb +18 -18
- data/lib/packetgen/header/ospfv3/ls_update.rb +10 -10
- data/lib/packetgen/header/ospfv3/lsa.rb +62 -51
- data/lib/packetgen/header/ospfv3/lsa_header.rb +12 -11
- data/lib/packetgen/header/ospfv3.rb +54 -52
- data/lib/packetgen/header/sctp/chunk.rb +80 -56
- data/lib/packetgen/header/sctp/error.rb +174 -202
- data/lib/packetgen/header/sctp/padded32.rb +3 -3
- data/lib/packetgen/header/sctp/parameter.rb +89 -136
- data/lib/packetgen/header/sctp.rb +19 -8
- data/lib/packetgen/header/snmp.rb +108 -7
- data/lib/packetgen/header/tcp/option.rb +52 -39
- data/lib/packetgen/header/tcp/options.rb +13 -5
- data/lib/packetgen/header/tcp.rb +83 -65
- data/lib/packetgen/header/tftp.rb +31 -25
- data/lib/packetgen/header/udp.rb +21 -19
- data/lib/packetgen/header.rb +23 -18
- data/lib/packetgen/headerable.rb +21 -5
- data/lib/packetgen/inspect.rb +3 -8
- data/lib/packetgen/packet.rb +146 -71
- data/lib/packetgen/pcap.rb +15 -4
- data/lib/packetgen/pcapng/block.rb +20 -18
- data/lib/packetgen/pcapng/epb.rb +13 -15
- data/lib/packetgen/pcapng/file.rb +44 -111
- data/lib/packetgen/pcapng/idb.rb +11 -12
- data/lib/packetgen/pcapng/shb.rb +15 -16
- data/lib/packetgen/pcapng/spb.rb +9 -11
- data/lib/packetgen/pcapng/unknown_block.rb +6 -17
- data/lib/packetgen/pcapng.rb +6 -4
- data/lib/packetgen/pcaprub_wrapper.rb +17 -1
- data/lib/packetgen/proto.rb +5 -1
- data/lib/packetgen/unknown_packet.rb +5 -5
- data/lib/packetgen/utils/arp_spoofer.rb +18 -19
- data/lib/packetgen/utils.rb +4 -3
- data/lib/packetgen/version.rb +1 -1
- data/lib/packetgen.rb +12 -5
- metadata +29 -38
- data/lib/packetgen/types/abstract_tlv.rb +0 -278
- data/lib/packetgen/types/array.rb +0 -287
- data/lib/packetgen/types/cstring.rb +0 -109
- data/lib/packetgen/types/enum.rb +0 -171
- data/lib/packetgen/types/fieldable.rb +0 -66
- data/lib/packetgen/types/fields.rb +0 -622
- data/lib/packetgen/types/int.rb +0 -473
- data/lib/packetgen/types/int_string.rb +0 -102
- data/lib/packetgen/types/length_from.rb +0 -54
- data/lib/packetgen/types/oui.rb +0 -52
- data/lib/packetgen/types/string.rb +0 -97
- data/lib/packetgen/types/tlv.rb +0 -161
- data/lib/packetgen/types.rb +0 -26
data/lib/packetgen/packet.rb
CHANGED
@@ -7,50 +7,58 @@
|
|
7
7
|
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
8
8
|
# This program is published under MIT license.
|
9
9
|
|
10
|
-
# rubocop:disable Metrics/ClassLength
|
11
|
-
|
12
10
|
module PacketGen
|
13
11
|
# An object of type {Packet} handles a network packet. This packet may contain
|
14
12
|
# multiple protocol headers, starting from MAC layer or from Network (OSI) layer.
|
15
13
|
#
|
16
|
-
#
|
17
|
-
#
|
14
|
+
# A Packet is created using {.gen} method. Headers may be added to this packet using {#add}.
|
15
|
+
# It may also be created from parsing a binary string, using {.parse} method.
|
16
|
+
#
|
17
|
+
# @example Very simple packet
|
18
|
+
# # Create a packet with a single IP header. IP source and destination addresses are set.
|
19
|
+
# pkt = PacketGen::Packet.gen('IP', src: '192.168.1.1', dst: '192.168.1.2')
|
18
20
|
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# Packet.parse(binary_string)
|
21
|
+
# @example Parsing a binary string
|
22
|
+
# # a IP/ICMP packet, as binary string
|
23
|
+
# binary_string = "E\x00\x00\x1C\xAA`\x00\x00@\x01M-\xC0\xA8\x01\x01\xC0\xA8\x01\x02\x00\b;1abcd".b
|
24
|
+
# pkt = PacketGen::Packet.parse(binary_string)
|
25
|
+
# pkt.is?('IP') #=> true
|
26
|
+
# pkt.is?('ICMP') #=> true
|
23
27
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
# # read information
|
27
|
-
# pkt.udp.sport
|
28
|
-
# pkt.ip.ttl
|
29
|
-
# # set information
|
30
|
-
# pkt.udp.dport = 2323
|
31
|
-
# pkt.ip.ttl = 1
|
32
|
-
# pkt.ip(ttl: 1, id: 1234)
|
28
|
+
# Information for each level is accessible through the associated header. Header is accessible through a
|
29
|
+
# method defined with its name at packet level (i.e. +#ip+ for a IP header).
|
33
30
|
#
|
34
|
-
#
|
35
|
-
#
|
31
|
+
# @example Accessing header information
|
32
|
+
# pkt = PacketGen::Packet.gen('IP').add('UDP')
|
33
|
+
# # read information
|
34
|
+
# pkt.udp.sport #=> 0
|
35
|
+
# pkt.ip.ttl #=> 64
|
36
|
+
# # set information
|
37
|
+
# pkt.udp.dport = 2323
|
38
|
+
# pkt.ip.ttl = 1
|
39
|
+
# # Set multiple attributes at once
|
40
|
+
# pkt.ip(ttl: 1, id: 1234)
|
36
41
|
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
# do_some_stuffs
|
41
|
-
# end
|
42
|
-
# packets = Packet.capture(iface: 'eth0', max: 5) # get 5 packets from eth0
|
42
|
+
# Packets may be written to files using {#write}:
|
43
|
+
# pkt = PacketGen::Packet.gen('Eth')
|
44
|
+
# pkt.write('file.pcapng')
|
43
45
|
#
|
44
|
-
# Packets may
|
45
|
-
#
|
46
|
+
# Packets may be captured from network interfaces:
|
47
|
+
# # Capture all packets from default interface. Never end.
|
48
|
+
# PacketGen::Packet.capture do |packet|
|
49
|
+
# do_some_stuffs
|
50
|
+
# end
|
46
51
|
#
|
47
|
-
#
|
48
|
-
#
|
52
|
+
# # Get 5 packets from eth0 interface
|
53
|
+
# packets = Packet.capture(iface: 'eth0', max: 5)
|
49
54
|
#
|
55
|
+
# Finally, packets may also be read from a file:
|
56
|
+
# packets = Packet.read(file.pcapng)
|
50
57
|
# @author Sylvain Daubert
|
58
|
+
# @author LemonTree55
|
51
59
|
class Packet
|
52
60
|
# Get packet headers, ordered as they appear in the packet.
|
53
|
-
# @return [Array<
|
61
|
+
# @return [Array<Headerable>]
|
54
62
|
attr_reader :headers
|
55
63
|
# Activaye or deactivate header cache (activated by default)
|
56
64
|
# @return [Boolean]
|
@@ -61,20 +69,20 @@ module PacketGen
|
|
61
69
|
# @param [Hash] options specific options for +protocol+
|
62
70
|
# @return [Packet]
|
63
71
|
def self.gen(protocol, options={})
|
64
|
-
self.new.add
|
72
|
+
self.new.add(protocol, options)
|
65
73
|
end
|
66
74
|
|
67
75
|
# Parse a binary string and generate a Packet from it.
|
68
76
|
# # auto-detect first header
|
69
|
-
# Packet.parse
|
77
|
+
# Packet.parse(str)
|
70
78
|
# # force decoding a Ethernet header for first header
|
71
|
-
# Packet.parse
|
79
|
+
# Packet.parse(str, first_header: 'Eth')
|
72
80
|
# @param [String] binary_str
|
73
81
|
# @param [String,nil] first_header First protocol header. +nil+ means discover it!
|
74
82
|
# @return [Packet]
|
75
83
|
# @raise [ArgumentError] +first_header+ is an unknown header
|
76
84
|
def self.parse(binary_str, first_header: nil)
|
77
|
-
new.parse
|
85
|
+
new.parse(binary_str, first_header: first_header)
|
78
86
|
end
|
79
87
|
|
80
88
|
# Capture packets from wire.
|
@@ -112,16 +120,23 @@ module PacketGen
|
|
112
120
|
Pcap.read(filename)
|
113
121
|
end
|
114
122
|
|
115
|
-
# Write packets to +filename
|
123
|
+
# Write packets to +filename+, as pcap or PcapNG file
|
116
124
|
#
|
117
125
|
# For more options, see {PcapNG::File}.
|
118
126
|
# @param [String] filename
|
119
127
|
# @param [Array<Packet>] packets packets to write
|
120
128
|
# @return [void]
|
129
|
+
# @since 4.0.0 write a pcap file if filename extension is +.pcap+
|
130
|
+
# @author Sylvain Daubert
|
131
|
+
# @author LemonTree55 (pcap)
|
121
132
|
def self.write(filename, packets)
|
122
|
-
|
123
|
-
|
124
|
-
|
133
|
+
if filename.end_with?('.pcap')
|
134
|
+
Pcap.write(filename, packets)
|
135
|
+
else
|
136
|
+
pf = PcapNG::File.new
|
137
|
+
pf.read_array(packets)
|
138
|
+
pf.to_f(filename)
|
139
|
+
end
|
125
140
|
end
|
126
141
|
|
127
142
|
# @private
|
@@ -135,14 +150,22 @@ module PacketGen
|
|
135
150
|
# @param [String] protocol
|
136
151
|
# @param [Hash] options protocol specific options
|
137
152
|
# @return [self]
|
138
|
-
# @raise [
|
153
|
+
# @raise [ArgumentError] unknown protocol
|
154
|
+
# @raise [BindingError] unknown binding
|
155
|
+
# @see #<<
|
156
|
+
# @example
|
157
|
+
# pkt = PacketGen::Packet.gen('Eth')
|
158
|
+
# # Add a IP header
|
159
|
+
# pkt.add('IP')
|
160
|
+
# # Add a TCP header, with some attributes and body set
|
161
|
+
# pkt.add('TCP', dport: 80, seqnum: 123456, body: "abcd".b)
|
139
162
|
def add(protocol, options={})
|
140
163
|
klass = check_protocol(protocol)
|
141
164
|
|
142
165
|
# options[:packet]= self is speedier than options.merge(packet: self)
|
143
166
|
options[:packet] = self
|
144
167
|
header = klass.new(options)
|
145
|
-
add_header
|
168
|
+
add_header(header)
|
146
169
|
self
|
147
170
|
end
|
148
171
|
|
@@ -159,7 +182,7 @@ module PacketGen
|
|
159
182
|
# options[:packet]= self is speedier than options.merge(packet: self)
|
160
183
|
options[:packet] = self
|
161
184
|
header = klass.new(options)
|
162
|
-
add_header
|
185
|
+
add_header(header, previous_header: prev)
|
163
186
|
idx = headers.index(prev) + 1
|
164
187
|
headers[idx, 0] = header
|
165
188
|
header[:body] = nxt
|
@@ -167,13 +190,14 @@ module PacketGen
|
|
167
190
|
end
|
168
191
|
|
169
192
|
# Check if a protocol header is embedded in packet.
|
193
|
+
# @example
|
170
194
|
# pkt = PacketGen.gen('IP').add('UDP')
|
171
195
|
# pkt.is?('IP') #=> true
|
172
196
|
# pkt.is?('TCP') #=> false
|
173
197
|
# @return [Boolean]
|
174
198
|
# @raise [ArgumentError] unknown protocol
|
175
199
|
def is?(protocol)
|
176
|
-
klass = check_protocol
|
200
|
+
klass = check_protocol(protocol)
|
177
201
|
headers.any?(klass)
|
178
202
|
end
|
179
203
|
|
@@ -181,7 +205,7 @@ module PacketGen
|
|
181
205
|
# @return [void]
|
182
206
|
def calc_checksum
|
183
207
|
headers.reverse_each do |header|
|
184
|
-
header.calc_checksum if header.respond_to?
|
208
|
+
header.calc_checksum if header.respond_to?(:calc_checksum)
|
185
209
|
end
|
186
210
|
end
|
187
211
|
|
@@ -189,27 +213,36 @@ module PacketGen
|
|
189
213
|
# @return [void]
|
190
214
|
def calc_length
|
191
215
|
headers.reverse_each do |header|
|
192
|
-
header.calc_length if header.respond_to?
|
216
|
+
header.calc_length if header.respond_to?(:calc_length)
|
193
217
|
end
|
194
218
|
end
|
195
219
|
|
196
220
|
# Recalculate all calculatable fields (for now: length and checksum)
|
197
221
|
# @return [void]
|
222
|
+
# @author LemonTree55
|
198
223
|
def calc
|
199
|
-
|
200
|
-
|
224
|
+
headers.reverse_each do |header|
|
225
|
+
header.calc_length if header.respond_to?(:calc_length)
|
226
|
+
header.calc_checksum if header.respond_to?(:calc_checksum)
|
227
|
+
end
|
201
228
|
end
|
202
229
|
|
203
|
-
# Get packet body
|
204
|
-
# @return [
|
230
|
+
# Get packet body. If packet (i.e. last header) has no +:body+ field, return +nil+.
|
231
|
+
# @return [Headerable,BinStruct::String,nil]
|
205
232
|
def body
|
206
|
-
last_header[:body] if last_header.respond_to?
|
233
|
+
last_header[:body] if last_header.respond_to?(:body)
|
207
234
|
end
|
208
235
|
|
209
236
|
# Set packet body
|
210
|
-
# @param [String] str
|
237
|
+
# @param [String] str Binary string
|
211
238
|
# @return [void]
|
239
|
+
# @raise [Error] Packet (i.e. last header) has no +:body+ field.
|
240
|
+
# @note To set a {Headerable} object, prefer #{<<}
|
241
|
+
# @see #<<
|
242
|
+
# @since 4.1.0 raise {Error} if no body on packet
|
212
243
|
def body=(str)
|
244
|
+
raise Error, 'no body in last headeré' unless last_header.respond_to?(:body)
|
245
|
+
|
213
246
|
last_header.body = str
|
214
247
|
end
|
215
248
|
|
@@ -229,7 +262,7 @@ module PacketGen
|
|
229
262
|
alias write to_f
|
230
263
|
|
231
264
|
# Send packet on wire. Use first header +#to_w+ method.
|
232
|
-
# @param [String] iface interface name. Default to first non-loopback interface
|
265
|
+
# @param [String,nil] iface interface name. Default to first non-loopback interface
|
233
266
|
# @param [Boolean] calc if +true+, call {#calc} on packet before sending it.
|
234
267
|
# @param [Integer] number number of times to send the packets
|
235
268
|
# @param [Integer,Float] interval time, in seconds, between sending 2 packets
|
@@ -239,12 +272,12 @@ module PacketGen
|
|
239
272
|
def to_w(iface=nil, calc: true, number: 1, interval: 1)
|
240
273
|
iface ||= PacketGen.default_iface
|
241
274
|
|
242
|
-
if first_header.respond_to?
|
275
|
+
if first_header.respond_to?(:to_w)
|
243
276
|
self.calc if calc
|
244
277
|
|
245
278
|
number.times do
|
246
279
|
first_header.to_w(iface)
|
247
|
-
sleep
|
280
|
+
sleep(interval) if number > 1
|
248
281
|
end
|
249
282
|
else
|
250
283
|
type = first_header.protocol_name
|
@@ -257,12 +290,19 @@ module PacketGen
|
|
257
290
|
# @param [Boolean] parsing set to +true+ to not update last current header field
|
258
291
|
# from binding with first other's one. Use only when current header field
|
259
292
|
# has its value set accordingly.
|
260
|
-
# @return [self] +self+ with new headers from +other+
|
293
|
+
# @return [self] +self+ updated with new headers from +other+
|
261
294
|
# @raise [BindingError] do not known how to encapsulate
|
262
295
|
# @since 1.1.0
|
296
|
+
# @example
|
297
|
+
# # Create a first IP packet
|
298
|
+
# ip1 = PacketGen::Packet.gen('IP', id: 1)
|
299
|
+
# # Create second IP packet, to encapsulate in first
|
300
|
+
# ip2 = PacketGen.gen('IP', id: 2)
|
301
|
+
# ip1.encapsulate(ip2)
|
302
|
+
# ip1.ip(2) == ip2.ip
|
263
303
|
def encapsulate(other, parsing: false)
|
264
304
|
other.headers.each_with_index do |h, i|
|
265
|
-
add_header
|
305
|
+
add_header(h, parsing: i.positive? || parsing)
|
266
306
|
end
|
267
307
|
end
|
268
308
|
|
@@ -272,6 +312,12 @@ module PacketGen
|
|
272
312
|
# @raise [FormatError] any headers not in +self+
|
273
313
|
# @raise [BindingError] removed headers result in an unknown binding
|
274
314
|
# @since 1.1.0
|
315
|
+
# @example
|
316
|
+
# # IP/IP encapsulation
|
317
|
+
# pkt = PacketGen::Packet.gen('IP', id: 1).add('IP', id:2)
|
318
|
+
# # Remove outer IP header
|
319
|
+
# pkt.decapsulate(pkt.ip(1))
|
320
|
+
# pkt.ip.id #=> 2
|
275
321
|
def decapsulate(*hdrs)
|
276
322
|
hdrs.each do |hdr|
|
277
323
|
prev_hdr = previous_header(hdr)
|
@@ -280,14 +326,15 @@ module PacketGen
|
|
280
326
|
add_header(next_hdr, previous_header: prev_hdr) if prev_hdr && next_hdr
|
281
327
|
end
|
282
328
|
invalidate_header_cache
|
329
|
+
self
|
283
330
|
rescue ArgumentError => e
|
284
331
|
raise FormatError, e.message
|
285
332
|
end
|
286
333
|
|
287
334
|
# Parse a binary string and populate Packet from it.
|
288
335
|
# @param [String] binary_str
|
289
|
-
# @param [String,nil] first_header First protocol header. +nil+ means discover it!
|
290
|
-
# @return [
|
336
|
+
# @param [String,nil] first_header First protocol header name. +nil+ means discover it!
|
337
|
+
# @return [self]
|
291
338
|
# @raise [ParseError] +first_header+ is an unknown header
|
292
339
|
# @raise [BindingError] unknwon binding between some headers
|
293
340
|
def parse(binary_str, first_header: nil)
|
@@ -299,7 +346,7 @@ module PacketGen
|
|
299
346
|
raise ParseError, "cannot identify first header in string: #{binary_str.inspect}" if first_header.nil?
|
300
347
|
end
|
301
348
|
|
302
|
-
add
|
349
|
+
add(first_header)
|
303
350
|
headers[-1, 1] = last_header.read(binary_str)
|
304
351
|
|
305
352
|
# Decode upper headers recursively
|
@@ -317,12 +364,14 @@ module PacketGen
|
|
317
364
|
str << Inspect.inspect_body(body)
|
318
365
|
end
|
319
366
|
|
367
|
+
# Check equality at binary level
|
320
368
|
# @param [Packet] other
|
321
369
|
# @return [Boolean]
|
322
370
|
def ==(other)
|
323
371
|
to_s == other.to_s
|
324
372
|
end
|
325
373
|
|
374
|
+
# +true+ if {#==} is +true+ with another packet, or if +other+ is a protocol name String, whose protocol is in Packet.
|
326
375
|
# @param [Packet] other
|
327
376
|
# @return [Boolean]
|
328
377
|
# @since 3.1.2
|
@@ -331,14 +380,15 @@ module PacketGen
|
|
331
380
|
when PacketGen::Packet
|
332
381
|
self == other
|
333
382
|
when String
|
334
|
-
is?
|
383
|
+
is?(other)
|
335
384
|
else
|
336
385
|
false
|
337
386
|
end
|
338
387
|
end
|
339
388
|
|
340
|
-
# Invert all possible
|
389
|
+
# Invert all possible attributes in packet to create a reply.
|
341
390
|
# @return [self]
|
391
|
+
# @note Only modify headers responding to +#reply!+.
|
342
392
|
# @since 2.7.0
|
343
393
|
def reply!
|
344
394
|
headers.each do |header|
|
@@ -350,12 +400,32 @@ module PacketGen
|
|
350
400
|
# Forge a new packet from current one with all possible fields
|
351
401
|
# inverted. The new packet may be a reply to current one.
|
352
402
|
# @return [Packet]
|
403
|
+
# @note Only modify headers responding to +#reply!+.
|
353
404
|
# @since 2.7.0
|
354
405
|
def reply
|
355
406
|
pkt = dup
|
356
407
|
pkt.reply!
|
357
408
|
end
|
358
409
|
|
410
|
+
# Append an already defined header to packet
|
411
|
+
# @param [Headerable] header
|
412
|
+
# @return [self]
|
413
|
+
# @raise [ArgumentError] unknown protocol
|
414
|
+
# @raise [BindingError] unknown binding
|
415
|
+
# @example
|
416
|
+
# pkt = PacketGen.gen('Eth')
|
417
|
+
# # Add a new header from its type
|
418
|
+
# pkt.add('IP')
|
419
|
+
# # Add a new pre-generated header
|
420
|
+
# pkt << PacketGen::Header::TCP.new
|
421
|
+
# @see #add
|
422
|
+
# @since 4.1.0
|
423
|
+
# @author LemonTree55
|
424
|
+
def <<(header)
|
425
|
+
add_header(header)
|
426
|
+
self
|
427
|
+
end
|
428
|
+
|
359
429
|
private
|
360
430
|
|
361
431
|
# Dup +@headers+ instance variable. Internally used by +#dup+ and +#clone+
|
@@ -363,7 +433,7 @@ module PacketGen
|
|
363
433
|
def initialize_copy(_other)
|
364
434
|
@headers = headers.map(&:dup)
|
365
435
|
headers.each do |header|
|
366
|
-
add_magic_header_method
|
436
|
+
add_magic_header_method(header)
|
367
437
|
end
|
368
438
|
invalidate_header_cache
|
369
439
|
end
|
@@ -410,17 +480,20 @@ module PacketGen
|
|
410
480
|
end
|
411
481
|
|
412
482
|
# @overload header(klass, layer=1)
|
483
|
+
# Get a header given its class and its layer (example: IP-in-IP encapsulation)
|
413
484
|
# @param [Class] klass
|
414
485
|
# @param [Integer] layer
|
415
486
|
# @overload header(klass, options={})
|
487
|
+
# Get a header given its class, and set some attributes
|
416
488
|
# @param [String] klass
|
417
|
-
# @param [Hash] options
|
418
|
-
# @raise [ArgumentError] unknown
|
419
|
-
# @return [Header::Base]
|
489
|
+
# @param [Hash] options attributes to set
|
490
|
+
# @raise [ArgumentError] unknown attribute
|
491
|
+
# @return [Header::Base,nil]
|
420
492
|
def header(klass, arg)
|
421
493
|
layer = arg.is_a?(Integer) ? arg : 1
|
422
494
|
header = find_header(klass, layer)
|
423
|
-
return
|
495
|
+
return nil if header.nil?
|
496
|
+
return header unless arg.is_a?(Hash)
|
424
497
|
|
425
498
|
arg.each do |key, value|
|
426
499
|
raise ArgumentError, "unknown #{key} attribute for #{klass}" unless header.respond_to?(:"#{key}=")
|
@@ -434,12 +507,14 @@ module PacketGen
|
|
434
507
|
# Get header from cache, or find it in packet
|
435
508
|
# @param [Class] klass
|
436
509
|
# @param [Integer] layer
|
437
|
-
# @return [Header::Base]
|
510
|
+
# @return [Header::Base,nil]
|
438
511
|
def find_header(klass, layer)
|
439
512
|
header = fetch_header_from_cache(klass, layer)
|
440
513
|
return header if header
|
441
514
|
|
442
|
-
header = headers.select { |h| h.is_a?
|
515
|
+
header = headers.select { |h| h.is_a?(klass) }[layer - 1]
|
516
|
+
return nil if header.nil?
|
517
|
+
|
443
518
|
add_header_to_cache(header, klass, layer)
|
444
519
|
header
|
445
520
|
end
|
@@ -468,9 +543,9 @@ module PacketGen
|
|
468
543
|
header.packet = self
|
469
544
|
headers << header unless previous_header
|
470
545
|
|
471
|
-
return if respond_to?
|
546
|
+
return if respond_to?(header.method_name)
|
472
547
|
|
473
|
-
add_magic_header_method
|
548
|
+
add_magic_header_method(header)
|
474
549
|
end
|
475
550
|
|
476
551
|
# Bind +header+ to +prev_header+.
|
@@ -497,7 +572,7 @@ module PacketGen
|
|
497
572
|
|
498
573
|
# Try to guess header from +binary_str+
|
499
574
|
# @param [String] binary_str
|
500
|
-
# @return [String] header/protocol name
|
575
|
+
# @return [String,nil] header/protocol name, or nil if cannot be guessed
|
501
576
|
def guess_first_header(binary_str)
|
502
577
|
first_header = nil
|
503
578
|
Header.all.each do |hklass|
|
@@ -529,7 +604,7 @@ module PacketGen
|
|
529
604
|
nheader = nheader.read(last_known_hdr.body)
|
530
605
|
next unless nheader.parse?
|
531
606
|
|
532
|
-
add_header
|
607
|
+
add_header(nheader, parsing: true)
|
533
608
|
break if last_header == last_known_hdr
|
534
609
|
end
|
535
610
|
end
|
data/lib/packetgen/pcap.rb
CHANGED
@@ -8,8 +8,7 @@
|
|
8
8
|
require_relative 'pcaprub_wrapper'
|
9
9
|
|
10
10
|
module PacketGen
|
11
|
-
# Module to read PCAP files
|
12
|
-
# @author Sylvain Daubert
|
11
|
+
# Module to read/write PCAP files
|
13
12
|
# @api private
|
14
13
|
# @since 3.1.4
|
15
14
|
module Pcap
|
@@ -17,14 +16,26 @@ module PacketGen
|
|
17
16
|
# @param [String] filename
|
18
17
|
# @return [Array<Packet>]
|
19
18
|
# @author Kent Gruber
|
19
|
+
# @author LemonTree55
|
20
20
|
def self.read(filename)
|
21
21
|
packets = []
|
22
|
-
PCAPRUBWrapper.read_pcap(filename: filename) do |
|
23
|
-
|
22
|
+
PCAPRUBWrapper.read_pcap(filename: filename) do |raw_packet|
|
23
|
+
packet = PacketGen.parse(raw_packet.to_s)
|
24
|
+
next if packet.nil?
|
24
25
|
|
25
26
|
packets << packet
|
26
27
|
end
|
27
28
|
packets
|
28
29
|
end
|
30
|
+
|
31
|
+
# Write binary packets to a PCAP file
|
32
|
+
# @param [String] filename
|
33
|
+
# @param [Array<String>] packets
|
34
|
+
# @return [void]
|
35
|
+
# @since 4.0.0
|
36
|
+
# @author LemonTree55
|
37
|
+
def self.write(filename, packets)
|
38
|
+
PCAPRUBWrapper.pcap_write(filename, packets.map(&:to_s))
|
39
|
+
end
|
29
40
|
end
|
30
41
|
end
|
@@ -10,63 +10,65 @@ module PacketGen
|
|
10
10
|
module PcapNG
|
11
11
|
# @abstract Base class for all block types
|
12
12
|
# @author Sylvain Daubert
|
13
|
-
|
13
|
+
# @author LemonTree55
|
14
|
+
class Block < BinStruct::Struct
|
14
15
|
# @return [:little, :big]
|
15
16
|
attr_accessor :endian
|
16
17
|
|
17
18
|
# @!attribute type
|
18
19
|
# 32-bit block type
|
19
20
|
# @return [Integer]
|
20
|
-
|
21
|
+
define_attr :type, BinStruct::Int32
|
21
22
|
# @!attribute block_len
|
22
23
|
# 32-bit block length
|
23
24
|
# @return [Integer]
|
24
|
-
|
25
|
-
# @!attribute
|
25
|
+
define_attr :block_len, BinStruct::Int32
|
26
|
+
# @!attribute block_len2
|
26
27
|
# 32-bit block length
|
27
28
|
# @return [Integer]
|
28
|
-
|
29
|
+
define_attr :block_len2, BinStruct::Int32
|
29
30
|
|
30
31
|
def initialize(options={})
|
31
32
|
super
|
33
|
+
endianness(options[:endian] || :little)
|
34
|
+
recalc_block_len
|
32
35
|
end
|
33
36
|
|
34
37
|
# Has this block option?
|
35
38
|
# @return [Boolean]
|
36
39
|
# @since 2.7.0
|
37
40
|
def options?
|
38
|
-
@
|
41
|
+
@attributes.key?(:options) && @attributes[:options].sz.positive?
|
39
42
|
end
|
40
43
|
|
41
|
-
# Calculate block length and update
|
44
|
+
# Calculate block length and update +block_len+ and +block_len2+ fields
|
42
45
|
# @return [void]
|
43
46
|
def recalc_block_len
|
44
|
-
len =
|
47
|
+
len = attributes.map { |f| @attributes[f].to_s }.join.size
|
45
48
|
self.block_len = self.block_len2 = len
|
46
49
|
end
|
47
50
|
|
48
51
|
# Pad given field to 32 bit boundary, if needed
|
49
|
-
# @param [Array<Symbol>] fields
|
52
|
+
# @param [Array<Symbol>] fields fields to pad
|
50
53
|
# @return [void]
|
54
|
+
# @author LemonTree55
|
51
55
|
def pad_field(*fields)
|
52
56
|
fields.each do |field|
|
53
|
-
obj = @
|
54
|
-
|
55
|
-
obj << "\x00" * pad_size
|
57
|
+
obj = @attributes[field]
|
58
|
+
obj << "\x00" * -(obj.sz % -4)
|
56
59
|
end
|
57
60
|
end
|
58
61
|
|
59
62
|
private
|
60
63
|
|
61
64
|
# Set the endianness for the various Int classes handled by self.
|
62
|
-
# Must be called by all subclass #initialize method.
|
63
65
|
# @param [:little, :big] endian
|
64
66
|
# @return [:little, :big] returns endian
|
65
67
|
def endianness(endian)
|
66
68
|
raise ArgumentError, "unknown endianness for #{self.class}" unless %i[little big].include?(endian)
|
67
69
|
|
68
70
|
@endian = endian
|
69
|
-
@
|
71
|
+
@attributes.each_value { |v| v.endian = endian if v.is_a?(BinStruct::Int) }
|
70
72
|
endian
|
71
73
|
end
|
72
74
|
|
@@ -75,19 +77,19 @@ module PacketGen
|
|
75
77
|
end
|
76
78
|
|
77
79
|
def to_io(str_or_io)
|
78
|
-
return str_or_io if str_or_io.respond_to?
|
80
|
+
return str_or_io if str_or_io.respond_to?(:read)
|
79
81
|
|
80
|
-
StringIO.new(
|
82
|
+
StringIO.new(str_or_io.to_s.b)
|
81
83
|
end
|
82
84
|
|
83
85
|
def remove_padding(io, data_len)
|
84
86
|
data_pad_len = (4 - (data_len % 4)) % 4
|
85
|
-
io.read
|
87
|
+
io.read(data_pad_len)
|
86
88
|
data_pad_len
|
87
89
|
end
|
88
90
|
|
89
91
|
def read_blocklen2_and_check(io)
|
90
|
-
self[:block_len2].read
|
92
|
+
self[:block_len2].read(io.read(4))
|
91
93
|
check_len_coherency
|
92
94
|
end
|
93
95
|
end
|