DIY-pcap 0.4.1 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- 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,367 +1,367 @@
|
|
1
|
-
# http://www.mudynamics.com
|
2
|
-
# http://labs.mudynamics.com
|
3
|
-
# http://www.pcapr.net
|
4
|
-
|
5
|
-
module Mu
|
6
|
-
class Pcap
|
7
|
-
|
8
|
-
class SCTP < Packet
|
9
|
-
attr_accessor :src_port, :dst_port, :verify_tag, :checksum
|
10
|
-
|
11
|
-
# SCTP chunk types
|
12
|
-
CHUNK_DATA = 0x00
|
13
|
-
CHUNK_INIT = 0x01
|
14
|
-
CHUNK_INIT_ACK = 0x02
|
15
|
-
CHUNK_SACK = 0x03
|
16
|
-
CHUNK_HEARTBEAT = 0x04
|
17
|
-
CHUNK_HEARTBEAT_ACK = 0x05
|
18
|
-
CHUNK_ABORT = 0x06
|
19
|
-
CHUNK_SHUTDOWN = 0x07
|
20
|
-
CHUNK_SHUTDOWN_ACK = 0x08
|
21
|
-
CHUNK_ERROR = 0x09
|
22
|
-
CHUNK_COOKIE_ECHO = 0x0A
|
23
|
-
CHUNK_COOKIE_ACK = 0x0B
|
24
|
-
CHUNK_ECNE = 0x0C
|
25
|
-
CHUNK_CWR = 0x0D
|
26
|
-
CHUNK_SHUTDOWN_COMPLETE = 0x0E
|
27
|
-
CHUNK_AUTH = 0x0F
|
28
|
-
CHUNK_ASCONF_ACK = 0x80
|
29
|
-
CHUNK_PADDING = 0x84
|
30
|
-
CHUNK_FORWARD_TSN = 0xC0
|
31
|
-
CHUNK_ASCONF = 0xC1
|
32
|
-
|
33
|
-
# SCTP parameter types
|
34
|
-
PARAM_IPV4 = 0x0005
|
35
|
-
PARAM_IPV6 = 0x0006
|
36
|
-
PARAM_STATE_COOKIE = 0x0007
|
37
|
-
PARAM_COOKIE_PRESERVATIVE = 0x0009
|
38
|
-
PARAM_HOST_NAME_ADDR = 0x000B
|
39
|
-
PARAM_SUPPORTED_ADDR_TYPES = 0x000C
|
40
|
-
PARAM_ECN = 0x8000
|
41
|
-
PARAM_RANDOM = 0x8002
|
42
|
-
PARAM_CHUNK_LIST = 0x8003
|
43
|
-
PARAM_HMAC_ALGORITHM = 0x8004
|
44
|
-
PARAM_PADDING = 0x8005
|
45
|
-
PARAM_SUPPORTED_EXTENSIONS = 0x8006
|
46
|
-
PARAM_FORWARD_TSN = 0xC000
|
47
|
-
PARAM_SET_PRIMARY_ADDR = 0xC004
|
48
|
-
PARAM_ADAPTATION_LAYER_INDICATION = 0xC006
|
49
|
-
|
50
|
-
def initialize
|
51
|
-
super
|
52
|
-
|
53
|
-
@src_port = 0
|
54
|
-
@dst_port = 0
|
55
|
-
@verify_tag = 0
|
56
|
-
@checksum = 0
|
57
|
-
@payload = []
|
58
|
-
end
|
59
|
-
|
60
|
-
def flow_id
|
61
|
-
return [:sctp, @src_port, @dst_port, @verify_tag]
|
62
|
-
end
|
63
|
-
|
64
|
-
def reverse_flow_id
|
65
|
-
return [:sctp, @dst_port, @src_port, @checksum]
|
66
|
-
end
|
67
|
-
|
68
|
-
# Creates SCTP packet from the payload
|
69
|
-
def self.from_bytes bytes
|
70
|
-
# Basic packet validation
|
71
|
-
Pcap.assert(bytes.length >= 12,
|
72
|
-
"Truncated SCTP header: 12 > #{bytes.length}")
|
73
|
-
Pcap.assert(bytes.length >= 16,
|
74
|
-
"Truncated SCTP packet: got only #{bytes.length} bytes")
|
75
|
-
|
76
|
-
# Read SCTP header
|
77
|
-
sport, dport, vtag, cksum = bytes.unpack('nnNN')
|
78
|
-
|
79
|
-
# Create SCTP packet and populate SCTP header fields
|
80
|
-
sctp = SCTP.new
|
81
|
-
sctp.src_port = sport
|
82
|
-
sctp.dst_port = dport
|
83
|
-
sctp.verify_tag = vtag
|
84
|
-
sctp.checksum = cksum
|
85
|
-
|
86
|
-
# Initialize the counter
|
87
|
-
length = 12
|
88
|
-
|
89
|
-
# Collect the chunks
|
90
|
-
while length < bytes.length
|
91
|
-
# Parse new chunk from the bytes
|
92
|
-
chunk = Chunk.from_bytes(bytes[length..-1])
|
93
|
-
|
94
|
-
# Get chunk size with padding
|
95
|
-
length += chunk.padded_size
|
96
|
-
|
97
|
-
# Add chunk to the list
|
98
|
-
sctp << chunk
|
99
|
-
end
|
100
|
-
|
101
|
-
# Sync the payload
|
102
|
-
sctp.sync_payload
|
103
|
-
|
104
|
-
# Return the result
|
105
|
-
return sctp
|
106
|
-
end
|
107
|
-
|
108
|
-
class ReorderError < StandardError ; end
|
109
|
-
|
110
|
-
# Reorders SCTP packets, if necessary
|
111
|
-
def self.reorder packets
|
112
|
-
# Initialize
|
113
|
-
tsns = {}
|
114
|
-
init_packets = {}
|
115
|
-
init_ack_packets = {}
|
116
|
-
reordered_packets = []
|
117
|
-
|
118
|
-
# Iterate over each packet
|
119
|
-
while not packets.empty?
|
120
|
-
# Get next packet
|
121
|
-
packet = packets.shift
|
122
|
-
|
123
|
-
# Do not reorder non-SCTP packets
|
124
|
-
if not sctp?(packet)
|
125
|
-
reordered_packets << packet
|
126
|
-
else
|
127
|
-
# Get SCTP portion
|
128
|
-
sctp = packet.payload.payload
|
129
|
-
|
130
|
-
# Sanity checks and packet filtering/preprocessing
|
131
|
-
if 0 == sctp.verify_tag and not sctp.init?
|
132
|
-
# Non-Init packet with 0 verify tag
|
133
|
-
raise ReorderError, "Non-Init packet with zero verify tag"
|
134
|
-
elsif sctp.init_or_ack? and 1 < sctp.chunk_count
|
135
|
-
# Init/InitAck packet with more with one chunk
|
136
|
-
raise ReorderError, "Init/Ack packet with more than 1 chunk"
|
137
|
-
elsif sctp.init?
|
138
|
-
# Use checksum to save reverse verify tag in the Init packet
|
139
|
-
sctp.checksum = sctp[0].init_tag
|
140
|
-
|
141
|
-
# Save orphaned Init packets until we find the Ack
|
142
|
-
init_packets[sctp.reverse_flow_id] = sctp
|
143
|
-
|
144
|
-
# Add packet for further processing
|
145
|
-
reordered_packets << packet
|
146
|
-
elsif sctp.init_ack?
|
147
|
-
# Lookup Init packet and construct it's flow it
|
148
|
-
init_packet = init_packets.delete(sctp.flow_id)
|
149
|
-
|
150
|
-
# Did we find anything?
|
151
|
-
if init_packet
|
152
|
-
# Set verify tag in the Init packet
|
153
|
-
init_packet.verify_tag = sctp[0].init_tag
|
154
|
-
|
155
|
-
# Set reverse verify tag in the InitAck packet
|
156
|
-
sctp.checksum = init_packet.verify_tag
|
157
|
-
|
158
|
-
# Re-insert INIT packet keyed by its flow id
|
159
|
-
init_packets[init_packet.flow_id] = init_packet
|
160
|
-
else
|
161
|
-
Pcap.warning("Orphaned SCTP INIT_ACK packet")
|
162
|
-
end
|
163
|
-
|
164
|
-
# Save InitAck packet
|
165
|
-
init_ack_packets[sctp.flow_id] = sctp
|
166
|
-
|
167
|
-
# Add packet for further processing
|
168
|
-
reordered_packets << packet
|
169
|
-
elsif sctp.has_data?
|
170
|
-
# SCTP packet with user data; lookup Init or InitAck packet
|
171
|
-
init_packet = init_packets[sctp.flow_id]
|
172
|
-
init_ack_packet = init_ack_packets[sctp.flow_id]
|
173
|
-
|
174
|
-
# It should belong to either one flow id or the other
|
175
|
-
if init_packet
|
176
|
-
# Set reverse verify tag from Init packet
|
177
|
-
sctp.checksum = init_packet.checksum
|
178
|
-
elsif init_ack_packet
|
179
|
-
# Set reverse flow id from InitAck packet
|
180
|
-
sctp.checksum = init_ack_packet.checksum
|
181
|
-
else
|
182
|
-
# Orphaned SCTP packet -- not very good
|
183
|
-
Pcap.warning("Orphaned SCTP DATA packet detected")
|
184
|
-
end
|
185
|
-
|
186
|
-
# If we have just one chunk we are done
|
187
|
-
if 1 == sctp.chunk_count and not tsns.member?(sctp[0].tsn)
|
188
|
-
# Save TSN
|
189
|
-
tsns[sctp[0].tsn] = sctp[0]
|
190
|
-
|
191
|
-
# sync the payload
|
192
|
-
sctp.sync_payload
|
193
|
-
|
194
|
-
# Add packet for further processing
|
195
|
-
reordered_packets << packet
|
196
|
-
else
|
197
|
-
# Split each data chunk in a separate SCTP packet
|
198
|
-
sctp.chunk_count.times do
|
199
|
-
# Get next chunk
|
200
|
-
chunk = sctp.shift
|
201
|
-
|
202
|
-
# Is it data?
|
203
|
-
if CHUNK_DATA == chunk.type
|
204
|
-
# Yes, check for duplicate TSNs
|
205
|
-
if not tsns.member?(chunk.tsn)
|
206
|
-
# Not a duplicate; create new SCTP packet
|
207
|
-
packet_new = packet.deepdup
|
208
|
-
|
209
|
-
# Create new SCTP payload
|
210
|
-
sctp_new = SCTP.new
|
211
|
-
sctp_new.src_port = sctp.src_port
|
212
|
-
sctp_new.dst_port = sctp.dst_port
|
213
|
-
sctp_new.verify_tag = sctp.verify_tag
|
214
|
-
sctp_new.checksum = sctp.checksum
|
215
|
-
|
216
|
-
# Add the chunk
|
217
|
-
sctp_new << chunk
|
218
|
-
|
219
|
-
# Add SCTP payload to the new packet
|
220
|
-
packet_new.payload.payload = sctp_new
|
221
|
-
|
222
|
-
# Save TSN
|
223
|
-
tsns[chunk.tsn] = chunk
|
224
|
-
|
225
|
-
# Sync the payload
|
226
|
-
sctp_new.sync_payload
|
227
|
-
|
228
|
-
# Add packet for further processing
|
229
|
-
reordered_packets << packet_new
|
230
|
-
else
|
231
|
-
Pcap.warning("Duplicate chunk: #{chunk.tsn}")
|
232
|
-
end
|
233
|
-
else
|
234
|
-
Pcap.warning("Non-data chunk: #{chunk.type}")
|
235
|
-
end
|
236
|
-
end
|
237
|
-
end
|
238
|
-
else
|
239
|
-
# Other SCTP packet; we are not interested at this time
|
240
|
-
end
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
# Return the result
|
245
|
-
return reordered_packets
|
246
|
-
end
|
247
|
-
|
248
|
-
def write io, ip
|
249
|
-
# Give a warning if packet size exceeds maximum allowed
|
250
|
-
if @payload_raw and @payload_raw.length + 20 > 65535
|
251
|
-
Pcap.warning("SCTP payload is too large")
|
252
|
-
end
|
253
|
-
|
254
|
-
# Calculate CRC32 checksum on the packet; temporarily removed due to a
|
255
|
-
# hack that uses checksum to link forward and reverse SCTP flow IDs.
|
256
|
-
#header = [@src_port, @dst_port, @verify_tag, 0].pack('nnNN')
|
257
|
-
#checksum = SCTP.crc32(header + @payload_raw)
|
258
|
-
header = [@src_port, @dst_port, @verify_tag, @checksum].pack('nnNN')
|
259
|
-
|
260
|
-
# Write SCTP header followed by each chunk
|
261
|
-
io.write(header)
|
262
|
-
|
263
|
-
# Write each chunks' data
|
264
|
-
@payload.each do |chunk|
|
265
|
-
chunk.write(io, ip)
|
266
|
-
end
|
267
|
-
end
|
268
|
-
|
269
|
-
def sync_payload
|
270
|
-
# Reset raw bytes
|
271
|
-
@payload_raw = ''
|
272
|
-
|
273
|
-
# Iterate over each chunk
|
274
|
-
@payload.each do |chunk|
|
275
|
-
@payload_raw << chunk.payload_raw
|
276
|
-
end
|
277
|
-
|
278
|
-
# Reset raw payload if it's empty
|
279
|
-
@payload_raw = nil if @payload_raw == ''
|
280
|
-
end
|
281
|
-
|
282
|
-
def self.crc32 bytes
|
283
|
-
r = 0xFFFFFFFF
|
284
|
-
|
285
|
-
bytes.each_byte do |b|
|
286
|
-
r ^= b
|
287
|
-
|
288
|
-
8.times do
|
289
|
-
r = (r >> 1) ^ (0xEDB88320 * (r & 1))
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
|
-
return r ^ 0xFFFFFFFF
|
294
|
-
end
|
295
|
-
|
296
|
-
def self.sctp? packet
|
297
|
-
return packet.is_a?(Ethernet) &&
|
298
|
-
packet.payload.is_a?(IP) &&
|
299
|
-
packet.payload.payload.is_a?(SCTP)
|
300
|
-
end
|
301
|
-
|
302
|
-
def << chunk
|
303
|
-
@payload << chunk
|
304
|
-
end
|
305
|
-
|
306
|
-
def shift
|
307
|
-
return @payload.shift
|
308
|
-
end
|
309
|
-
|
310
|
-
def [] index
|
311
|
-
return @payload[index]
|
312
|
-
end
|
313
|
-
|
314
|
-
def chunk_count
|
315
|
-
return @payload.size
|
316
|
-
end
|
317
|
-
|
318
|
-
def has_data?
|
319
|
-
return @payload.any? do |chunk|
|
320
|
-
CHUNK_DATA == chunk.type
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
|
-
def to_s
|
325
|
-
return "sctp(%d, %d, %d, %s)" % [@src_port,
|
326
|
-
@dst_port,
|
327
|
-
@verify_tag,
|
328
|
-
@payload.join(", ")]
|
329
|
-
end
|
330
|
-
|
331
|
-
def == other
|
332
|
-
return super &&
|
333
|
-
self.src_port == other.src_port &&
|
334
|
-
self.dst_port == other.dst_port &&
|
335
|
-
self.verify_tag == other.verify_tag &&
|
336
|
-
self.payload.size == other.payload.size
|
337
|
-
end
|
338
|
-
|
339
|
-
def init?
|
340
|
-
if CHUNK_INIT == @payload[0].type
|
341
|
-
return true
|
342
|
-
else
|
343
|
-
return false
|
344
|
-
end
|
345
|
-
end
|
346
|
-
|
347
|
-
def init_ack?
|
348
|
-
if CHUNK_INIT_ACK == @payload[0].type
|
349
|
-
return true
|
350
|
-
else
|
351
|
-
return false
|
352
|
-
end
|
353
|
-
end
|
354
|
-
|
355
|
-
def init_or_ack?
|
356
|
-
if CHUNK_INIT == @payload[0].type or CHUNK_INIT_ACK == @payload[0].type
|
357
|
-
return true
|
358
|
-
else
|
359
|
-
return false
|
360
|
-
end
|
361
|
-
end
|
362
|
-
end # class SCTP
|
363
|
-
|
364
|
-
end # class Pcap
|
365
|
-
end # module Mu
|
366
|
-
|
367
|
-
require 'mu/pcap/sctp/chunk'
|
1
|
+
# http://www.mudynamics.com
|
2
|
+
# http://labs.mudynamics.com
|
3
|
+
# http://www.pcapr.net
|
4
|
+
|
5
|
+
module Mu
|
6
|
+
class Pcap
|
7
|
+
|
8
|
+
class SCTP < Packet
|
9
|
+
attr_accessor :src_port, :dst_port, :verify_tag, :checksum
|
10
|
+
|
11
|
+
# SCTP chunk types
|
12
|
+
CHUNK_DATA = 0x00
|
13
|
+
CHUNK_INIT = 0x01
|
14
|
+
CHUNK_INIT_ACK = 0x02
|
15
|
+
CHUNK_SACK = 0x03
|
16
|
+
CHUNK_HEARTBEAT = 0x04
|
17
|
+
CHUNK_HEARTBEAT_ACK = 0x05
|
18
|
+
CHUNK_ABORT = 0x06
|
19
|
+
CHUNK_SHUTDOWN = 0x07
|
20
|
+
CHUNK_SHUTDOWN_ACK = 0x08
|
21
|
+
CHUNK_ERROR = 0x09
|
22
|
+
CHUNK_COOKIE_ECHO = 0x0A
|
23
|
+
CHUNK_COOKIE_ACK = 0x0B
|
24
|
+
CHUNK_ECNE = 0x0C
|
25
|
+
CHUNK_CWR = 0x0D
|
26
|
+
CHUNK_SHUTDOWN_COMPLETE = 0x0E
|
27
|
+
CHUNK_AUTH = 0x0F
|
28
|
+
CHUNK_ASCONF_ACK = 0x80
|
29
|
+
CHUNK_PADDING = 0x84
|
30
|
+
CHUNK_FORWARD_TSN = 0xC0
|
31
|
+
CHUNK_ASCONF = 0xC1
|
32
|
+
|
33
|
+
# SCTP parameter types
|
34
|
+
PARAM_IPV4 = 0x0005
|
35
|
+
PARAM_IPV6 = 0x0006
|
36
|
+
PARAM_STATE_COOKIE = 0x0007
|
37
|
+
PARAM_COOKIE_PRESERVATIVE = 0x0009
|
38
|
+
PARAM_HOST_NAME_ADDR = 0x000B
|
39
|
+
PARAM_SUPPORTED_ADDR_TYPES = 0x000C
|
40
|
+
PARAM_ECN = 0x8000
|
41
|
+
PARAM_RANDOM = 0x8002
|
42
|
+
PARAM_CHUNK_LIST = 0x8003
|
43
|
+
PARAM_HMAC_ALGORITHM = 0x8004
|
44
|
+
PARAM_PADDING = 0x8005
|
45
|
+
PARAM_SUPPORTED_EXTENSIONS = 0x8006
|
46
|
+
PARAM_FORWARD_TSN = 0xC000
|
47
|
+
PARAM_SET_PRIMARY_ADDR = 0xC004
|
48
|
+
PARAM_ADAPTATION_LAYER_INDICATION = 0xC006
|
49
|
+
|
50
|
+
def initialize
|
51
|
+
super
|
52
|
+
|
53
|
+
@src_port = 0
|
54
|
+
@dst_port = 0
|
55
|
+
@verify_tag = 0
|
56
|
+
@checksum = 0
|
57
|
+
@payload = []
|
58
|
+
end
|
59
|
+
|
60
|
+
def flow_id
|
61
|
+
return [:sctp, @src_port, @dst_port, @verify_tag]
|
62
|
+
end
|
63
|
+
|
64
|
+
def reverse_flow_id
|
65
|
+
return [:sctp, @dst_port, @src_port, @checksum]
|
66
|
+
end
|
67
|
+
|
68
|
+
# Creates SCTP packet from the payload
|
69
|
+
def self.from_bytes bytes
|
70
|
+
# Basic packet validation
|
71
|
+
Pcap.assert(bytes.length >= 12,
|
72
|
+
"Truncated SCTP header: 12 > #{bytes.length}")
|
73
|
+
Pcap.assert(bytes.length >= 16,
|
74
|
+
"Truncated SCTP packet: got only #{bytes.length} bytes")
|
75
|
+
|
76
|
+
# Read SCTP header
|
77
|
+
sport, dport, vtag, cksum = bytes.unpack('nnNN')
|
78
|
+
|
79
|
+
# Create SCTP packet and populate SCTP header fields
|
80
|
+
sctp = SCTP.new
|
81
|
+
sctp.src_port = sport
|
82
|
+
sctp.dst_port = dport
|
83
|
+
sctp.verify_tag = vtag
|
84
|
+
sctp.checksum = cksum
|
85
|
+
|
86
|
+
# Initialize the counter
|
87
|
+
length = 12
|
88
|
+
|
89
|
+
# Collect the chunks
|
90
|
+
while length < bytes.length
|
91
|
+
# Parse new chunk from the bytes
|
92
|
+
chunk = Chunk.from_bytes(bytes[length..-1])
|
93
|
+
|
94
|
+
# Get chunk size with padding
|
95
|
+
length += chunk.padded_size
|
96
|
+
|
97
|
+
# Add chunk to the list
|
98
|
+
sctp << chunk
|
99
|
+
end
|
100
|
+
|
101
|
+
# Sync the payload
|
102
|
+
sctp.sync_payload
|
103
|
+
|
104
|
+
# Return the result
|
105
|
+
return sctp
|
106
|
+
end
|
107
|
+
|
108
|
+
class ReorderError < StandardError ; end
|
109
|
+
|
110
|
+
# Reorders SCTP packets, if necessary
|
111
|
+
def self.reorder packets
|
112
|
+
# Initialize
|
113
|
+
tsns = {}
|
114
|
+
init_packets = {}
|
115
|
+
init_ack_packets = {}
|
116
|
+
reordered_packets = []
|
117
|
+
|
118
|
+
# Iterate over each packet
|
119
|
+
while not packets.empty?
|
120
|
+
# Get next packet
|
121
|
+
packet = packets.shift
|
122
|
+
|
123
|
+
# Do not reorder non-SCTP packets
|
124
|
+
if not sctp?(packet)
|
125
|
+
reordered_packets << packet
|
126
|
+
else
|
127
|
+
# Get SCTP portion
|
128
|
+
sctp = packet.payload.payload
|
129
|
+
|
130
|
+
# Sanity checks and packet filtering/preprocessing
|
131
|
+
if 0 == sctp.verify_tag and not sctp.init?
|
132
|
+
# Non-Init packet with 0 verify tag
|
133
|
+
raise ReorderError, "Non-Init packet with zero verify tag"
|
134
|
+
elsif sctp.init_or_ack? and 1 < sctp.chunk_count
|
135
|
+
# Init/InitAck packet with more with one chunk
|
136
|
+
raise ReorderError, "Init/Ack packet with more than 1 chunk"
|
137
|
+
elsif sctp.init?
|
138
|
+
# Use checksum to save reverse verify tag in the Init packet
|
139
|
+
sctp.checksum = sctp[0].init_tag
|
140
|
+
|
141
|
+
# Save orphaned Init packets until we find the Ack
|
142
|
+
init_packets[sctp.reverse_flow_id] = sctp
|
143
|
+
|
144
|
+
# Add packet for further processing
|
145
|
+
reordered_packets << packet
|
146
|
+
elsif sctp.init_ack?
|
147
|
+
# Lookup Init packet and construct it's flow it
|
148
|
+
init_packet = init_packets.delete(sctp.flow_id)
|
149
|
+
|
150
|
+
# Did we find anything?
|
151
|
+
if init_packet
|
152
|
+
# Set verify tag in the Init packet
|
153
|
+
init_packet.verify_tag = sctp[0].init_tag
|
154
|
+
|
155
|
+
# Set reverse verify tag in the InitAck packet
|
156
|
+
sctp.checksum = init_packet.verify_tag
|
157
|
+
|
158
|
+
# Re-insert INIT packet keyed by its flow id
|
159
|
+
init_packets[init_packet.flow_id] = init_packet
|
160
|
+
else
|
161
|
+
Pcap.warning("Orphaned SCTP INIT_ACK packet")
|
162
|
+
end
|
163
|
+
|
164
|
+
# Save InitAck packet
|
165
|
+
init_ack_packets[sctp.flow_id] = sctp
|
166
|
+
|
167
|
+
# Add packet for further processing
|
168
|
+
reordered_packets << packet
|
169
|
+
elsif sctp.has_data?
|
170
|
+
# SCTP packet with user data; lookup Init or InitAck packet
|
171
|
+
init_packet = init_packets[sctp.flow_id]
|
172
|
+
init_ack_packet = init_ack_packets[sctp.flow_id]
|
173
|
+
|
174
|
+
# It should belong to either one flow id or the other
|
175
|
+
if init_packet
|
176
|
+
# Set reverse verify tag from Init packet
|
177
|
+
sctp.checksum = init_packet.checksum
|
178
|
+
elsif init_ack_packet
|
179
|
+
# Set reverse flow id from InitAck packet
|
180
|
+
sctp.checksum = init_ack_packet.checksum
|
181
|
+
else
|
182
|
+
# Orphaned SCTP packet -- not very good
|
183
|
+
Pcap.warning("Orphaned SCTP DATA packet detected")
|
184
|
+
end
|
185
|
+
|
186
|
+
# If we have just one chunk we are done
|
187
|
+
if 1 == sctp.chunk_count and not tsns.member?(sctp[0].tsn)
|
188
|
+
# Save TSN
|
189
|
+
tsns[sctp[0].tsn] = sctp[0]
|
190
|
+
|
191
|
+
# sync the payload
|
192
|
+
sctp.sync_payload
|
193
|
+
|
194
|
+
# Add packet for further processing
|
195
|
+
reordered_packets << packet
|
196
|
+
else
|
197
|
+
# Split each data chunk in a separate SCTP packet
|
198
|
+
sctp.chunk_count.times do
|
199
|
+
# Get next chunk
|
200
|
+
chunk = sctp.shift
|
201
|
+
|
202
|
+
# Is it data?
|
203
|
+
if CHUNK_DATA == chunk.type
|
204
|
+
# Yes, check for duplicate TSNs
|
205
|
+
if not tsns.member?(chunk.tsn)
|
206
|
+
# Not a duplicate; create new SCTP packet
|
207
|
+
packet_new = packet.deepdup
|
208
|
+
|
209
|
+
# Create new SCTP payload
|
210
|
+
sctp_new = SCTP.new
|
211
|
+
sctp_new.src_port = sctp.src_port
|
212
|
+
sctp_new.dst_port = sctp.dst_port
|
213
|
+
sctp_new.verify_tag = sctp.verify_tag
|
214
|
+
sctp_new.checksum = sctp.checksum
|
215
|
+
|
216
|
+
# Add the chunk
|
217
|
+
sctp_new << chunk
|
218
|
+
|
219
|
+
# Add SCTP payload to the new packet
|
220
|
+
packet_new.payload.payload = sctp_new
|
221
|
+
|
222
|
+
# Save TSN
|
223
|
+
tsns[chunk.tsn] = chunk
|
224
|
+
|
225
|
+
# Sync the payload
|
226
|
+
sctp_new.sync_payload
|
227
|
+
|
228
|
+
# Add packet for further processing
|
229
|
+
reordered_packets << packet_new
|
230
|
+
else
|
231
|
+
Pcap.warning("Duplicate chunk: #{chunk.tsn}")
|
232
|
+
end
|
233
|
+
else
|
234
|
+
Pcap.warning("Non-data chunk: #{chunk.type}")
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
else
|
239
|
+
# Other SCTP packet; we are not interested at this time
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# Return the result
|
245
|
+
return reordered_packets
|
246
|
+
end
|
247
|
+
|
248
|
+
def write io, ip
|
249
|
+
# Give a warning if packet size exceeds maximum allowed
|
250
|
+
if @payload_raw and @payload_raw.length + 20 > 65535
|
251
|
+
Pcap.warning("SCTP payload is too large")
|
252
|
+
end
|
253
|
+
|
254
|
+
# Calculate CRC32 checksum on the packet; temporarily removed due to a
|
255
|
+
# hack that uses checksum to link forward and reverse SCTP flow IDs.
|
256
|
+
#header = [@src_port, @dst_port, @verify_tag, 0].pack('nnNN')
|
257
|
+
#checksum = SCTP.crc32(header + @payload_raw)
|
258
|
+
header = [@src_port, @dst_port, @verify_tag, @checksum].pack('nnNN')
|
259
|
+
|
260
|
+
# Write SCTP header followed by each chunk
|
261
|
+
io.write(header)
|
262
|
+
|
263
|
+
# Write each chunks' data
|
264
|
+
@payload.each do |chunk|
|
265
|
+
chunk.write(io, ip)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
def sync_payload
|
270
|
+
# Reset raw bytes
|
271
|
+
@payload_raw = ''
|
272
|
+
|
273
|
+
# Iterate over each chunk
|
274
|
+
@payload.each do |chunk|
|
275
|
+
@payload_raw << chunk.payload_raw
|
276
|
+
end
|
277
|
+
|
278
|
+
# Reset raw payload if it's empty
|
279
|
+
@payload_raw = nil if @payload_raw == ''
|
280
|
+
end
|
281
|
+
|
282
|
+
def self.crc32 bytes
|
283
|
+
r = 0xFFFFFFFF
|
284
|
+
|
285
|
+
bytes.each_byte do |b|
|
286
|
+
r ^= b
|
287
|
+
|
288
|
+
8.times do
|
289
|
+
r = (r >> 1) ^ (0xEDB88320 * (r & 1))
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
return r ^ 0xFFFFFFFF
|
294
|
+
end
|
295
|
+
|
296
|
+
def self.sctp? packet
|
297
|
+
return packet.is_a?(Ethernet) &&
|
298
|
+
packet.payload.is_a?(IP) &&
|
299
|
+
packet.payload.payload.is_a?(SCTP)
|
300
|
+
end
|
301
|
+
|
302
|
+
def << chunk
|
303
|
+
@payload << chunk
|
304
|
+
end
|
305
|
+
|
306
|
+
def shift
|
307
|
+
return @payload.shift
|
308
|
+
end
|
309
|
+
|
310
|
+
def [] index
|
311
|
+
return @payload[index]
|
312
|
+
end
|
313
|
+
|
314
|
+
def chunk_count
|
315
|
+
return @payload.size
|
316
|
+
end
|
317
|
+
|
318
|
+
def has_data?
|
319
|
+
return @payload.any? do |chunk|
|
320
|
+
CHUNK_DATA == chunk.type
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
def to_s
|
325
|
+
return "sctp(%d, %d, %d, %s)" % [@src_port,
|
326
|
+
@dst_port,
|
327
|
+
@verify_tag,
|
328
|
+
@payload.join(", ")]
|
329
|
+
end
|
330
|
+
|
331
|
+
def == other
|
332
|
+
return super &&
|
333
|
+
self.src_port == other.src_port &&
|
334
|
+
self.dst_port == other.dst_port &&
|
335
|
+
self.verify_tag == other.verify_tag &&
|
336
|
+
self.payload.size == other.payload.size
|
337
|
+
end
|
338
|
+
|
339
|
+
def init?
|
340
|
+
if CHUNK_INIT == @payload[0].type
|
341
|
+
return true
|
342
|
+
else
|
343
|
+
return false
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
def init_ack?
|
348
|
+
if CHUNK_INIT_ACK == @payload[0].type
|
349
|
+
return true
|
350
|
+
else
|
351
|
+
return false
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
def init_or_ack?
|
356
|
+
if CHUNK_INIT == @payload[0].type or CHUNK_INIT_ACK == @payload[0].type
|
357
|
+
return true
|
358
|
+
else
|
359
|
+
return false
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end # class SCTP
|
363
|
+
|
364
|
+
end # class Pcap
|
365
|
+
end # module Mu
|
366
|
+
|
367
|
+
require 'mu/pcap/sctp/chunk'
|