DIY-pcap 0.4.1 → 0.4.3
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.
- data/.gitignore +4 -4
- data/DIY-pcap.gemspec +17 -17
- data/Gemfile +3 -3
- data/Rakefile +1 -1
- data/lib/DIY-pcap.rb +2 -2
- data/lib/diy/command.rb +7 -1
- data/lib/diy/controller.rb +10 -15
- data/lib/diy/live.rb +9 -2
- data/lib/diy/parser/mu/pcap/ethernet.rb +148 -148
- data/lib/diy/parser/mu/pcap/header.rb +75 -75
- data/lib/diy/parser/mu/pcap/io_pair.rb +67 -67
- data/lib/diy/parser/mu/pcap/io_wrapper.rb +76 -76
- data/lib/diy/parser/mu/pcap/ip.rb +61 -61
- data/lib/diy/parser/mu/pcap/ipv4.rb +257 -257
- data/lib/diy/parser/mu/pcap/ipv6.rb +148 -148
- data/lib/diy/parser/mu/pcap/packet.rb +104 -104
- data/lib/diy/parser/mu/pcap/pkthdr.rb +155 -155
- data/lib/diy/parser/mu/pcap/reader.rb +61 -61
- data/lib/diy/parser/mu/pcap/reader/http_family.rb +170 -170
- data/lib/diy/parser/mu/pcap/sctp.rb +367 -367
- data/lib/diy/parser/mu/pcap/sctp/chunk.rb +123 -123
- data/lib/diy/parser/mu/pcap/sctp/chunk/data.rb +134 -134
- data/lib/diy/parser/mu/pcap/sctp/chunk/init.rb +100 -100
- data/lib/diy/parser/mu/pcap/sctp/chunk/init_ack.rb +68 -68
- data/lib/diy/parser/mu/pcap/sctp/parameter.rb +110 -110
- data/lib/diy/parser/mu/pcap/sctp/parameter/ip_address.rb +48 -48
- data/lib/diy/parser/mu/pcap/stream_packetizer.rb +72 -72
- data/lib/diy/parser/mu/pcap/tcp.rb +505 -505
- data/lib/diy/parser/mu/pcap/udp.rb +69 -69
- data/lib/diy/parser/mu/scenario/pcap.rb +172 -172
- data/lib/diy/parser/mu/scenario/pcap/fields.rb +50 -50
- data/lib/diy/parser/mu/scenario/pcap/rtp.rb +71 -71
- data/lib/diy/parser/pcap.rb +109 -109
- data/lib/diy/version.rb +1 -1
- metadata +7 -9
@@ -1,258 +1,258 @@
|
|
1
|
-
# http://www.mudynamics.com
|
2
|
-
# http://labs.mudynamics.com
|
3
|
-
# http://www.pcapr.net
|
4
|
-
|
5
|
-
require 'ipaddr'
|
6
|
-
|
7
|
-
module Mu
|
8
|
-
class Pcap
|
9
|
-
|
10
|
-
class IPv4 < IP
|
11
|
-
IP_RF = 0x8000 # Reserved
|
12
|
-
IP_DF = 0x4000 # Don't fragment
|
13
|
-
IP_MF = 0x2000 # More fragments
|
14
|
-
IP_OFFMASK = 0x1fff
|
15
|
-
|
16
|
-
FMT_HEADER = 'CCnnnCCna4a4'
|
17
|
-
|
18
|
-
attr_accessor :ip_id, :offset, :ttl, :proto, :src, :dst, :dscp
|
19
|
-
|
20
|
-
def initialize src=nil, dst=nil, ip_id=0, offset=0, ttl=64, proto=0, dscp=0
|
21
|
-
super()
|
22
|
-
@ip_id = ip_id
|
23
|
-
@offset = offset
|
24
|
-
@ttl = ttl
|
25
|
-
@proto = proto
|
26
|
-
@src = src
|
27
|
-
@dst = dst
|
28
|
-
@dscp = dscp
|
29
|
-
end
|
30
|
-
|
31
|
-
def v4?
|
32
|
-
return true
|
33
|
-
end
|
34
|
-
|
35
|
-
def flow_id
|
36
|
-
if not @payload or @payload.is_a? String
|
37
|
-
return [:ipv4, @proto, @src, @dst]
|
38
|
-
else
|
39
|
-
return [:ipv4, @src, @dst, @payload.flow_id]
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
NTOP = {} # Network to human cache
|
44
|
-
HTON = {} # Human to network cache
|
45
|
-
|
46
|
-
def self.from_bytes bytes
|
47
|
-
bytes.length >= 20 or
|
48
|
-
raise ParseError, "Truncated IPv4 header: expected at least 20 bytes, got #{bytes.length} bytes"
|
49
|
-
|
50
|
-
vhl, tos, length, id, offset, ttl, proto, checksum, src, dst = bytes[0,20].unpack FMT_HEADER
|
51
|
-
version = vhl >> 4
|
52
|
-
hl = (vhl & 0b1111) * 4
|
53
|
-
|
54
|
-
version == 4 or
|
55
|
-
raise ParseError, "Wrong IPv4 version: got (#{version})"
|
56
|
-
hl >= 20 or
|
57
|
-
raise ParseError, "Bad IPv4 header length: expected at least 20 bytes raise ParseError, got #{hl} bytes"
|
58
|
-
bytes.length >= hl or
|
59
|
-
raise ParseError, "Truncated IPv4 header: expected #{hl} bytes raise ParseError, got #{bytes.length} bytes"
|
60
|
-
length >= 20 or
|
61
|
-
raise ParseError, "Bad IPv4 packet length: expected at least 20 bytes raise ParseError, got #{length} bytes"
|
62
|
-
bytes.length >= length or
|
63
|
-
raise ParseError, "Truncated IPv4 packet: expected #{length} bytes raise ParseError, got #{bytes.length} bytes"
|
64
|
-
|
65
|
-
if hl != 20
|
66
|
-
IPv4.check_options bytes[20, hl-20]
|
67
|
-
end
|
68
|
-
|
69
|
-
src = NTOP[src] ||= IPAddr.ntop(src)
|
70
|
-
dst = NTOP[dst] ||= IPAddr.ntop(dst)
|
71
|
-
dscp = tos >> 2
|
72
|
-
ipv4 = IPv4.new(src, dst, id, offset, ttl, proto, dscp)
|
73
|
-
ipv4.payload_raw = bytes[hl..-1]
|
74
|
-
|
75
|
-
payload = bytes[hl...length]
|
76
|
-
if offset & (IP_OFFMASK | IP_MF) == 0
|
77
|
-
begin
|
78
|
-
case proto
|
79
|
-
when IPPROTO_TCP
|
80
|
-
ipv4.payload = TCP.from_bytes payload
|
81
|
-
when IPPROTO_UDP
|
82
|
-
ipv4.payload = UDP.from_bytes payload
|
83
|
-
when IPPROTO_SCTP
|
1
|
+
# http://www.mudynamics.com
|
2
|
+
# http://labs.mudynamics.com
|
3
|
+
# http://www.pcapr.net
|
4
|
+
|
5
|
+
require 'ipaddr'
|
6
|
+
|
7
|
+
module Mu
|
8
|
+
class Pcap
|
9
|
+
|
10
|
+
class IPv4 < IP
|
11
|
+
IP_RF = 0x8000 # Reserved
|
12
|
+
IP_DF = 0x4000 # Don't fragment
|
13
|
+
IP_MF = 0x2000 # More fragments
|
14
|
+
IP_OFFMASK = 0x1fff
|
15
|
+
|
16
|
+
FMT_HEADER = 'CCnnnCCna4a4'
|
17
|
+
|
18
|
+
attr_accessor :ip_id, :offset, :ttl, :proto, :src, :dst, :dscp
|
19
|
+
|
20
|
+
def initialize src=nil, dst=nil, ip_id=0, offset=0, ttl=64, proto=0, dscp=0
|
21
|
+
super()
|
22
|
+
@ip_id = ip_id
|
23
|
+
@offset = offset
|
24
|
+
@ttl = ttl
|
25
|
+
@proto = proto
|
26
|
+
@src = src
|
27
|
+
@dst = dst
|
28
|
+
@dscp = dscp
|
29
|
+
end
|
30
|
+
|
31
|
+
def v4?
|
32
|
+
return true
|
33
|
+
end
|
34
|
+
|
35
|
+
def flow_id
|
36
|
+
if not @payload or @payload.is_a? String
|
37
|
+
return [:ipv4, @proto, @src, @dst]
|
38
|
+
else
|
39
|
+
return [:ipv4, @src, @dst, @payload.flow_id]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
NTOP = {} # Network to human cache
|
44
|
+
HTON = {} # Human to network cache
|
45
|
+
|
46
|
+
def self.from_bytes bytes
|
47
|
+
bytes.length >= 20 or
|
48
|
+
raise ParseError, "Truncated IPv4 header: expected at least 20 bytes, got #{bytes.length} bytes"
|
49
|
+
|
50
|
+
vhl, tos, length, id, offset, ttl, proto, checksum, src, dst = bytes[0,20].unpack FMT_HEADER
|
51
|
+
version = vhl >> 4
|
52
|
+
hl = (vhl & 0b1111) * 4
|
53
|
+
|
54
|
+
version == 4 or
|
55
|
+
raise ParseError, "Wrong IPv4 version: got (#{version})"
|
56
|
+
hl >= 20 or
|
57
|
+
raise ParseError, "Bad IPv4 header length: expected at least 20 bytes raise ParseError, got #{hl} bytes"
|
58
|
+
bytes.length >= hl or
|
59
|
+
raise ParseError, "Truncated IPv4 header: expected #{hl} bytes raise ParseError, got #{bytes.length} bytes"
|
60
|
+
length >= 20 or
|
61
|
+
raise ParseError, "Bad IPv4 packet length: expected at least 20 bytes raise ParseError, got #{length} bytes"
|
62
|
+
bytes.length >= length or
|
63
|
+
raise ParseError, "Truncated IPv4 packet: expected #{length} bytes raise ParseError, got #{bytes.length} bytes"
|
64
|
+
|
65
|
+
if hl != 20
|
66
|
+
IPv4.check_options bytes[20, hl-20]
|
67
|
+
end
|
68
|
+
|
69
|
+
src = NTOP[src] ||= IPAddr.ntop(src)
|
70
|
+
dst = NTOP[dst] ||= IPAddr.ntop(dst)
|
71
|
+
dscp = tos >> 2
|
72
|
+
ipv4 = IPv4.new(src, dst, id, offset, ttl, proto, dscp)
|
73
|
+
ipv4.payload_raw = bytes[hl..-1]
|
74
|
+
|
75
|
+
payload = bytes[hl...length]
|
76
|
+
if offset & (IP_OFFMASK | IP_MF) == 0
|
77
|
+
begin
|
78
|
+
case proto
|
79
|
+
when IPPROTO_TCP
|
80
|
+
ipv4.payload = TCP.from_bytes payload
|
81
|
+
when IPPROTO_UDP
|
82
|
+
ipv4.payload = UDP.from_bytes payload
|
83
|
+
when IPPROTO_SCTP
|
84
84
|
#ipv4.payload = SCTP.from_bytes payload
|
85
|
-
ipv4.payload = payload
|
86
|
-
else
|
87
|
-
ipv4.payload = payload
|
88
|
-
end
|
89
|
-
rescue ParseError => e
|
90
|
-
Pcap.warning e
|
91
|
-
end
|
92
|
-
else
|
93
|
-
ipv4.payload = payload
|
94
|
-
end
|
95
|
-
return ipv4
|
96
|
-
end
|
97
|
-
|
98
|
-
def write io
|
99
|
-
if @payload.is_a? String
|
100
|
-
payload = @payload
|
101
|
-
else
|
102
|
-
string_io = StringIO.new
|
103
|
-
@payload.write string_io, self
|
104
|
-
payload = string_io.string
|
105
|
-
end
|
106
|
-
length = 20 + payload.length
|
107
|
-
if length > 65535
|
108
|
-
Pcap.warning "IPv4 payload is too large"
|
109
|
-
end
|
110
|
-
|
111
|
-
src = HTON[@src] ||= IPAddr.new(@src).hton
|
112
|
-
dst = HTON[@dst] ||= IPAddr.new(@dst).hton
|
113
|
-
fields = [0x45, @dscp << 2, length, @ip_id, @offset, @ttl, @proto, 0, src, dst]
|
114
|
-
header = fields.pack(FMT_HEADER)
|
115
|
-
fields[7] = IP.checksum(header)
|
116
|
-
header = fields.pack(FMT_HEADER)
|
117
|
-
io.write header
|
118
|
-
io.write payload
|
119
|
-
end
|
120
|
-
|
121
|
-
FMT_PSEUDO_HEADER = 'a4a4CCn'
|
122
|
-
def pseudo_header payload_length
|
123
|
-
src = HTON[@src] ||= IPAddr.new(@src).hton
|
124
|
-
dst = HTON[@dst] ||= IPAddr.new(@dst).hton
|
125
|
-
return [src, dst, 0, @proto, payload_length].pack(FMT_PSEUDO_HEADER)
|
126
|
-
end
|
127
|
-
|
128
|
-
def fragment?
|
129
|
-
return (@offset & (IP_OFFMASK | IP_MF) != 0)
|
130
|
-
end
|
131
|
-
|
132
|
-
# Check that IP or TCP options are valid. Do nothing if they are valid.
|
133
|
-
# Both IP and TCP options are 8-bit TLVs with an inclusive length. Both
|
134
|
-
# have one byte options 0 and 1.
|
135
|
-
def self.check_options options, label='IPv4'
|
136
|
-
while not options.empty?
|
137
|
-
type = options.slice!(0, 1)[0].ord
|
138
|
-
if type == 0 or type == 1
|
139
|
-
next
|
140
|
-
end
|
141
|
-
Pcap.assert !options.empty?,
|
142
|
-
"#{label} option #{type} is missing the length field"
|
143
|
-
length = options.slice!(0, 1)[0].ord
|
144
|
-
Pcap.assert length >= 2,
|
145
|
-
"#{label} option #{type} has invalid length: #{length}"
|
146
|
-
Pcap.assert length - 2 <= options.length,
|
147
|
-
"#{label} option #{type} has truncated data"
|
148
|
-
options.slice! 0, length - 2
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
ReassembleState = ::Struct.new :packets, :bytes, :mf, :overlap
|
153
|
-
|
154
|
-
# Reassemble fragmented IPv4 packets
|
155
|
-
def self.reassemble packets
|
156
|
-
reassembled_packets = []
|
157
|
-
flow_id_to_state = {}
|
158
|
-
packets.each do |packet|
|
159
|
-
if not packet.is_a?(Ethernet) or not packet.payload.is_a?(IPv4)
|
160
|
-
# Ignore non-IPv4 packet
|
161
|
-
elsif not packet.payload.fragment?
|
162
|
-
# Ignore non-fragments
|
163
|
-
else
|
164
|
-
# Get reassembly state
|
165
|
-
ip = packet.payload
|
166
|
-
flow_id = [ip.ip_id, ip.proto, ip.src, ip.dst]
|
167
|
-
state = flow_id_to_state[flow_id]
|
168
|
-
if not state
|
169
|
-
state = ReassembleState.new [], [], true, false
|
170
|
-
flow_id_to_state[flow_id] = state
|
171
|
-
end
|
172
|
-
state.packets << packet
|
173
|
-
|
174
|
-
# Clear the more-fragments flag if no more fragments
|
175
|
-
if ip.offset & IP_MF == 0
|
176
|
-
state.mf = false
|
177
|
-
end
|
178
|
-
|
179
|
-
# Add the bytes
|
180
|
-
start = (ip.offset & IP_OFFMASK) * 8
|
181
|
-
finish = start + ip.payload.length
|
182
|
-
state.bytes.fill nil, start, finish - start
|
183
|
-
start.upto(finish-1) do |i|
|
184
|
-
if not state.bytes[i]
|
185
|
-
byte = ip.payload[i - start].chr
|
186
|
-
state.bytes[i] = byte
|
187
|
-
elsif not state.overlap
|
188
|
-
name = "%s:%s:%d" % [ip.src, ip.dst, ip.proto]
|
189
|
-
Pcap.warning \
|
190
|
-
"IPv4 flow #{name} contains overlapping fragements"
|
191
|
-
state.overlap = true
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
# We're done if we've received a fragment without the
|
196
|
-
# more-fragments flag and all the bytes in the buffer have been
|
197
|
-
# set.
|
198
|
-
if not state.mf and state.bytes.all?
|
199
|
-
# Remove fragments from reassembled_packets
|
200
|
-
state.packets.each do |packet|
|
201
|
-
reassembled_packets.delete_if do |reassembled_packet|
|
202
|
-
packet.object_id == reassembled_packet.object_id
|
203
|
-
end
|
204
|
-
end
|
205
|
-
# Remove state
|
206
|
-
flow_id_to_state.delete flow_id
|
207
|
-
# Create new packet
|
208
|
-
packet = state.packets[0].deepdup
|
209
|
-
ipv4 = packet.payload
|
210
|
-
ipv4.offset = 0
|
211
|
-
ipv4.payload = state.bytes.join
|
212
|
-
# Decode
|
213
|
-
begin
|
214
|
-
case ipv4.proto
|
215
|
-
when IPPROTO_TCP
|
216
|
-
ipv4.payload = TCP.from_bytes ipv4.payload
|
217
|
-
when IPPROTO_UDP
|
218
|
-
ipv4.payload = UDP.from_bytes ipv4.payload
|
219
|
-
when IPPROTO_SCTP
|
220
|
-
ipv4.payload = SCTP.from_bytes ipv4.payload
|
221
|
-
end
|
222
|
-
rescue ParseError => e
|
223
|
-
Pcap.warning e
|
224
|
-
end
|
225
|
-
end
|
226
|
-
end
|
227
|
-
reassembled_packets << packet
|
228
|
-
end
|
229
|
-
if !flow_id_to_state.empty?
|
230
|
-
Pcap.warning \
|
231
|
-
"#{flow_id_to_state.length} flow(s) have IPv4 fragments " \
|
232
|
-
"that can't be reassembled"
|
233
|
-
end
|
234
|
-
|
235
|
-
return reassembled_packets
|
236
|
-
end
|
237
|
-
|
238
|
-
def to_s
|
239
|
-
if @payload.is_a? String
|
240
|
-
payload = @payload.inspect
|
241
|
-
else
|
242
|
-
payload = @payload.to_s
|
243
|
-
end
|
244
|
-
return "ipv4(%d, %s, %s, %s)" % [@proto, @src, @dst, payload]
|
245
|
-
end
|
246
|
-
|
247
|
-
def == other
|
248
|
-
return super &&
|
249
|
-
self.proto == other.proto &&
|
250
|
-
self.ip_id == other.ip_id &&
|
251
|
-
self.offset == other.offset &&
|
252
|
-
self.ttl == other.ttl &&
|
253
|
-
self.dscp == other.dscp
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
end
|
258
|
-
end
|
85
|
+
ipv4.payload = payload
|
86
|
+
else
|
87
|
+
ipv4.payload = payload
|
88
|
+
end
|
89
|
+
rescue ParseError => e
|
90
|
+
Pcap.warning e
|
91
|
+
end
|
92
|
+
else
|
93
|
+
ipv4.payload = payload
|
94
|
+
end
|
95
|
+
return ipv4
|
96
|
+
end
|
97
|
+
|
98
|
+
def write io
|
99
|
+
if @payload.is_a? String
|
100
|
+
payload = @payload
|
101
|
+
else
|
102
|
+
string_io = StringIO.new
|
103
|
+
@payload.write string_io, self
|
104
|
+
payload = string_io.string
|
105
|
+
end
|
106
|
+
length = 20 + payload.length
|
107
|
+
if length > 65535
|
108
|
+
Pcap.warning "IPv4 payload is too large"
|
109
|
+
end
|
110
|
+
|
111
|
+
src = HTON[@src] ||= IPAddr.new(@src).hton
|
112
|
+
dst = HTON[@dst] ||= IPAddr.new(@dst).hton
|
113
|
+
fields = [0x45, @dscp << 2, length, @ip_id, @offset, @ttl, @proto, 0, src, dst]
|
114
|
+
header = fields.pack(FMT_HEADER)
|
115
|
+
fields[7] = IP.checksum(header)
|
116
|
+
header = fields.pack(FMT_HEADER)
|
117
|
+
io.write header
|
118
|
+
io.write payload
|
119
|
+
end
|
120
|
+
|
121
|
+
FMT_PSEUDO_HEADER = 'a4a4CCn'
|
122
|
+
def pseudo_header payload_length
|
123
|
+
src = HTON[@src] ||= IPAddr.new(@src).hton
|
124
|
+
dst = HTON[@dst] ||= IPAddr.new(@dst).hton
|
125
|
+
return [src, dst, 0, @proto, payload_length].pack(FMT_PSEUDO_HEADER)
|
126
|
+
end
|
127
|
+
|
128
|
+
def fragment?
|
129
|
+
return (@offset & (IP_OFFMASK | IP_MF) != 0)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Check that IP or TCP options are valid. Do nothing if they are valid.
|
133
|
+
# Both IP and TCP options are 8-bit TLVs with an inclusive length. Both
|
134
|
+
# have one byte options 0 and 1.
|
135
|
+
def self.check_options options, label='IPv4'
|
136
|
+
while not options.empty?
|
137
|
+
type = options.slice!(0, 1)[0].ord
|
138
|
+
if type == 0 or type == 1
|
139
|
+
next
|
140
|
+
end
|
141
|
+
Pcap.assert !options.empty?,
|
142
|
+
"#{label} option #{type} is missing the length field"
|
143
|
+
length = options.slice!(0, 1)[0].ord
|
144
|
+
Pcap.assert length >= 2,
|
145
|
+
"#{label} option #{type} has invalid length: #{length}"
|
146
|
+
Pcap.assert length - 2 <= options.length,
|
147
|
+
"#{label} option #{type} has truncated data"
|
148
|
+
options.slice! 0, length - 2
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
ReassembleState = ::Struct.new :packets, :bytes, :mf, :overlap
|
153
|
+
|
154
|
+
# Reassemble fragmented IPv4 packets
|
155
|
+
def self.reassemble packets
|
156
|
+
reassembled_packets = []
|
157
|
+
flow_id_to_state = {}
|
158
|
+
packets.each do |packet|
|
159
|
+
if not packet.is_a?(Ethernet) or not packet.payload.is_a?(IPv4)
|
160
|
+
# Ignore non-IPv4 packet
|
161
|
+
elsif not packet.payload.fragment?
|
162
|
+
# Ignore non-fragments
|
163
|
+
else
|
164
|
+
# Get reassembly state
|
165
|
+
ip = packet.payload
|
166
|
+
flow_id = [ip.ip_id, ip.proto, ip.src, ip.dst]
|
167
|
+
state = flow_id_to_state[flow_id]
|
168
|
+
if not state
|
169
|
+
state = ReassembleState.new [], [], true, false
|
170
|
+
flow_id_to_state[flow_id] = state
|
171
|
+
end
|
172
|
+
state.packets << packet
|
173
|
+
|
174
|
+
# Clear the more-fragments flag if no more fragments
|
175
|
+
if ip.offset & IP_MF == 0
|
176
|
+
state.mf = false
|
177
|
+
end
|
178
|
+
|
179
|
+
# Add the bytes
|
180
|
+
start = (ip.offset & IP_OFFMASK) * 8
|
181
|
+
finish = start + ip.payload.length
|
182
|
+
state.bytes.fill nil, start, finish - start
|
183
|
+
start.upto(finish-1) do |i|
|
184
|
+
if not state.bytes[i]
|
185
|
+
byte = ip.payload[i - start].chr
|
186
|
+
state.bytes[i] = byte
|
187
|
+
elsif not state.overlap
|
188
|
+
name = "%s:%s:%d" % [ip.src, ip.dst, ip.proto]
|
189
|
+
Pcap.warning \
|
190
|
+
"IPv4 flow #{name} contains overlapping fragements"
|
191
|
+
state.overlap = true
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# We're done if we've received a fragment without the
|
196
|
+
# more-fragments flag and all the bytes in the buffer have been
|
197
|
+
# set.
|
198
|
+
if not state.mf and state.bytes.all?
|
199
|
+
# Remove fragments from reassembled_packets
|
200
|
+
state.packets.each do |packet|
|
201
|
+
reassembled_packets.delete_if do |reassembled_packet|
|
202
|
+
packet.object_id == reassembled_packet.object_id
|
203
|
+
end
|
204
|
+
end
|
205
|
+
# Remove state
|
206
|
+
flow_id_to_state.delete flow_id
|
207
|
+
# Create new packet
|
208
|
+
packet = state.packets[0].deepdup
|
209
|
+
ipv4 = packet.payload
|
210
|
+
ipv4.offset = 0
|
211
|
+
ipv4.payload = state.bytes.join
|
212
|
+
# Decode
|
213
|
+
begin
|
214
|
+
case ipv4.proto
|
215
|
+
when IPPROTO_TCP
|
216
|
+
ipv4.payload = TCP.from_bytes ipv4.payload
|
217
|
+
when IPPROTO_UDP
|
218
|
+
ipv4.payload = UDP.from_bytes ipv4.payload
|
219
|
+
when IPPROTO_SCTP
|
220
|
+
ipv4.payload = SCTP.from_bytes ipv4.payload
|
221
|
+
end
|
222
|
+
rescue ParseError => e
|
223
|
+
Pcap.warning e
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
reassembled_packets << packet
|
228
|
+
end
|
229
|
+
if !flow_id_to_state.empty?
|
230
|
+
Pcap.warning \
|
231
|
+
"#{flow_id_to_state.length} flow(s) have IPv4 fragments " \
|
232
|
+
"that can't be reassembled"
|
233
|
+
end
|
234
|
+
|
235
|
+
return reassembled_packets
|
236
|
+
end
|
237
|
+
|
238
|
+
def to_s
|
239
|
+
if @payload.is_a? String
|
240
|
+
payload = @payload.inspect
|
241
|
+
else
|
242
|
+
payload = @payload.to_s
|
243
|
+
end
|
244
|
+
return "ipv4(%d, %s, %s, %s)" % [@proto, @src, @dst, payload]
|
245
|
+
end
|
246
|
+
|
247
|
+
def == other
|
248
|
+
return super &&
|
249
|
+
self.proto == other.proto &&
|
250
|
+
self.ip_id == other.ip_id &&
|
251
|
+
self.offset == other.offset &&
|
252
|
+
self.ttl == other.ttl &&
|
253
|
+
self.dscp == other.dscp
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
end
|
258
|
+
end
|