woolen_common 0.0.1
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 +15 -0
- data/README.md +31 -0
- data/ext/woolen_common/extconf.rb +26 -0
- data/ext/woolen_common/linux.h +3 -0
- data/ext/woolen_common/win.c +18 -0
- data/ext/woolen_common/win.h +4 -0
- data/ext/woolen_common/win/puts_color.c +139 -0
- data/ext/woolen_common/win/puts_color.h +5 -0
- data/ext/woolen_common/woolen_common.c +20 -0
- data/ext/woolen_common/woolen_common.h +7 -0
- data/lib/woolen_common.rb +39 -0
- data/lib/woolen_common/abstract_middleware/builder.rb +138 -0
- data/lib/woolen_common/abstract_middleware/map_cfg_manager.rb +52 -0
- data/lib/woolen_common/abstract_middleware/runner.rb +72 -0
- data/lib/woolen_common/action_pool_proxy.rb +28 -0
- data/lib/woolen_common/actionpool.rb +10 -0
- data/lib/woolen_common/actionpool/pool.rb +295 -0
- data/lib/woolen_common/actionpool/queue.rb +41 -0
- data/lib/woolen_common/actionpool/thread.rb +181 -0
- data/lib/woolen_common/addr_helper.rb +93 -0
- data/lib/woolen_common/cache.rb +305 -0
- data/lib/woolen_common/common_helper.rb +42 -0
- data/lib/woolen_common/config_manager.rb +36 -0
- data/lib/woolen_common/drb_helper.rb +125 -0
- data/lib/woolen_common/ffi/win32_kernel32.rb +86 -0
- data/lib/woolen_common/logger.rb +419 -0
- data/lib/woolen_common/pcap/mu/fixnum_ext.rb +8 -0
- data/lib/woolen_common/pcap/mu/pcap/ethernet.rb +164 -0
- data/lib/woolen_common/pcap/mu/pcap/header.rb +76 -0
- data/lib/woolen_common/pcap/mu/pcap/io_pair.rb +68 -0
- data/lib/woolen_common/pcap/mu/pcap/io_wrapper.rb +77 -0
- data/lib/woolen_common/pcap/mu/pcap/ip.rb +62 -0
- data/lib/woolen_common/pcap/mu/pcap/ipv4.rb +274 -0
- data/lib/woolen_common/pcap/mu/pcap/ipv6.rb +149 -0
- data/lib/woolen_common/pcap/mu/pcap/packet.rb +106 -0
- data/lib/woolen_common/pcap/mu/pcap/pkthdr.rb +162 -0
- data/lib/woolen_common/pcap/mu/pcap/reader.rb +62 -0
- data/lib/woolen_common/pcap/mu/pcap/reader/http_family.rb +175 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp.rb +369 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp/chunk.rb +124 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp/chunk/data.rb +135 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp/chunk/init.rb +101 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp/chunk/init_ack.rb +69 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp/parameter.rb +111 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp/parameter/ip_address.rb +49 -0
- data/lib/woolen_common/pcap/mu/pcap/stream_packetizer.rb +74 -0
- data/lib/woolen_common/pcap/mu/pcap/tcp.rb +522 -0
- data/lib/woolen_common/pcap/mu/pcap/udp.rb +81 -0
- data/lib/woolen_common/pcap/mu/scenario/pcap.rb +175 -0
- data/lib/woolen_common/pcap/mu/scenario/pcap/fields.rb +51 -0
- data/lib/woolen_common/pcap/mu/scenario/pcap/rtp.rb +72 -0
- data/lib/woolen_common/pcap/pcap.rb +115 -0
- data/lib/woolen_common/pcap/readme.md +72 -0
- data/lib/woolen_common/ruby_ext/blank.rb +126 -0
- data/lib/woolen_common/ruby_ext/drb_ext.rb +7 -0
- data/lib/woolen_common/ruby_ext/string.rb +116 -0
- data/lib/woolen_common/ruby_ext/win32_ole.rb +4 -0
- data/lib/woolen_common/ruby_proxy.rb +5 -0
- data/lib/woolen_common/ruby_proxy/client.rb +305 -0
- data/lib/woolen_common/ruby_proxy/config.rb +44 -0
- data/lib/woolen_common/ruby_proxy/exceptions.rb +17 -0
- data/lib/woolen_common/ruby_proxy/proxy.rb +157 -0
- data/lib/woolen_common/ruby_proxy/proxy_global_set.rb +44 -0
- data/lib/woolen_common/ruby_proxy/proxy_load.rb +34 -0
- data/lib/woolen_common/ruby_proxy/server.rb +53 -0
- data/lib/woolen_common/splib.rb +36 -0
- data/lib/woolen_common/splib/Array.rb +33 -0
- data/lib/woolen_common/splib/CodeReloader.rb +59 -0
- data/lib/woolen_common/splib/Constants.rb +44 -0
- data/lib/woolen_common/splib/Conversions.rb +47 -0
- data/lib/woolen_common/splib/Exec.rb +132 -0
- data/lib/woolen_common/splib/Float.rb +13 -0
- data/lib/woolen_common/splib/HumanIdealRandomIterator.rb +40 -0
- data/lib/woolen_common/splib/Monitor.rb +214 -0
- data/lib/woolen_common/splib/PriorityQueue.rb +110 -0
- data/lib/woolen_common/splib/Sleep.rb +10 -0
- data/lib/woolen_common/splib/UrlShorteners.rb +48 -0
- data/lib/woolen_common/ssh_proxy.rb +146 -0
- data/lib/woolen_common/system_helper.rb +123 -0
- data/lib/woolen_common/system_monitor.rb +23 -0
- data/lib/woolen_common/system_monitor/linux_monitor.rb +250 -0
- data/lib/woolen_common/system_monitor/windows_monitor.rb +145 -0
- data/lib/woolen_common/type_helper.rb +42 -0
- data/lib/woolen_common/ver_ctrl_middle_ware.rb +92 -0
- data/lib/woolen_common/version.rb +3 -0
- metadata +210 -0
@@ -0,0 +1,274 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# http://www.mudynamics.com
|
3
|
+
# http://labs.mudynamics.com
|
4
|
+
# http://www.pcapr.net
|
5
|
+
|
6
|
+
require 'ipaddr'
|
7
|
+
|
8
|
+
module Mu
|
9
|
+
class Pcap
|
10
|
+
|
11
|
+
class IPv4 < IP
|
12
|
+
IP_RF = 0x8000 # Reserved
|
13
|
+
IP_DF = 0x4000 # Don't fragment
|
14
|
+
IP_MF = 0x2000 # More fragments
|
15
|
+
IP_OFFMASK = 0x1fff
|
16
|
+
|
17
|
+
FMT_HEADER = 'CCnnnCCna4a4'
|
18
|
+
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def build_pkt(lv2_pkt,src=nil, dst=nil, ip_id=0, offset=0, ttl=64, proto=0, dscp=0)
|
22
|
+
if lv2_pkt && lv2_pkt.is_a?(Packet)
|
23
|
+
the_ip_pkt = self.new(src, dst, ip_id, offset, ttl, proto, dscp)
|
24
|
+
lv2_pkt.payload = the_ip_pkt
|
25
|
+
the_ip_pkt
|
26
|
+
else
|
27
|
+
raise 'can not build udp pkt with not Packet'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_accessor :ip_id, :offset, :ttl, :proto, :src, :dst, :dscp
|
33
|
+
|
34
|
+
def initialize src=nil, dst=nil, ip_id=0, offset=0, ttl=64, proto=0, dscp=0
|
35
|
+
super()
|
36
|
+
@ip_id = ip_id
|
37
|
+
@offset = offset
|
38
|
+
@ttl = ttl
|
39
|
+
@proto = proto
|
40
|
+
@src = src
|
41
|
+
@dst = dst
|
42
|
+
@dscp = dscp
|
43
|
+
end
|
44
|
+
|
45
|
+
def v4?
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
def flow_id
|
50
|
+
if not @payload or @payload.is_a? String
|
51
|
+
[:ipv4, @proto, @src, @dst]
|
52
|
+
else
|
53
|
+
[:ipv4, @src, @dst, @payload.flow_id]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
NTOP = {} # Network to human cache
|
58
|
+
HTON = {} # Human to network cache
|
59
|
+
|
60
|
+
def self.from_bytes bytes
|
61
|
+
bytes.length >= 20 or
|
62
|
+
raise ParseError, "Truncated IPv4 header: expected at least 20 bytes, got #{bytes.length} bytes"
|
63
|
+
|
64
|
+
vhl, tos, length, id, offset, ttl, proto, checksum, src, dst = bytes[0, 20].unpack FMT_HEADER
|
65
|
+
version = vhl >> 4
|
66
|
+
hl = (vhl & 0b1111) * 4
|
67
|
+
|
68
|
+
version == 4 or
|
69
|
+
raise ParseError, "Wrong IPv4 version: got (#{version})"
|
70
|
+
hl >= 20 or
|
71
|
+
raise ParseError, "Bad IPv4 header length: expected at least 20 bytes raise ParseError, got #{hl} bytes"
|
72
|
+
bytes.length >= hl or
|
73
|
+
raise ParseError, "Truncated IPv4 header: expected #{hl} bytes raise ParseError, got #{bytes.length} bytes"
|
74
|
+
length >= 20 or
|
75
|
+
raise ParseError, "Bad IPv4 packet length: expected at least 20 bytes raise ParseError, got #{length} bytes"
|
76
|
+
bytes.length >= length or
|
77
|
+
raise ParseError, "Truncated IPv4 packet: expected #{length} bytes raise ParseError, got #{bytes.length} bytes"
|
78
|
+
|
79
|
+
if hl != 20
|
80
|
+
IPv4.check_options bytes[20, hl-20]
|
81
|
+
end
|
82
|
+
|
83
|
+
src = NTOP[src] ||= IPAddr.ntop(src)
|
84
|
+
dst = NTOP[dst] ||= IPAddr.ntop(dst)
|
85
|
+
dscp = tos >> 2
|
86
|
+
ipv4 = IPv4.new(src, dst, id, offset, ttl, proto, dscp)
|
87
|
+
ipv4.payload_raw = bytes[hl..-1]
|
88
|
+
|
89
|
+
payload = bytes[hl...length]
|
90
|
+
if offset & (IP_OFFMASK | IP_MF) == 0
|
91
|
+
begin
|
92
|
+
case proto
|
93
|
+
when IPPROTO_TCP
|
94
|
+
ipv4.payload = TCP.from_bytes payload
|
95
|
+
when IPPROTO_UDP
|
96
|
+
ipv4.payload = UDP.from_bytes payload
|
97
|
+
when IPPROTO_SCTP
|
98
|
+
#ipv4.payload = SCTP.from_bytes payload
|
99
|
+
ipv4.payload = payload
|
100
|
+
else
|
101
|
+
ipv4.payload = payload
|
102
|
+
end
|
103
|
+
rescue ParseError => e
|
104
|
+
Pcap.warning e
|
105
|
+
end
|
106
|
+
else
|
107
|
+
ipv4.payload = payload
|
108
|
+
end
|
109
|
+
return ipv4
|
110
|
+
end
|
111
|
+
|
112
|
+
def write io
|
113
|
+
if @payload.is_a? String
|
114
|
+
payload = @payload
|
115
|
+
else
|
116
|
+
string_io = StringIO.new
|
117
|
+
@payload.write string_io, self
|
118
|
+
string_io.close
|
119
|
+
payload = string_io.string
|
120
|
+
end
|
121
|
+
length = 20 + payload.bytesize
|
122
|
+
if length > 65535
|
123
|
+
Pcap.warning "IPv4 payload is too large"
|
124
|
+
end
|
125
|
+
|
126
|
+
src = HTON[@src] ||= IPAddr.new(@src).hton
|
127
|
+
dst = HTON[@dst] ||= IPAddr.new(@dst).hton
|
128
|
+
fields = [0x45, @dscp << 2, length, @ip_id, @offset, @ttl, @proto, 0, src, dst]
|
129
|
+
header = fields.pack(FMT_HEADER)
|
130
|
+
fields[7] = IP.checksum(header)
|
131
|
+
header = fields.pack(FMT_HEADER)
|
132
|
+
io.write header
|
133
|
+
io.write payload
|
134
|
+
end
|
135
|
+
|
136
|
+
FMT_PSEUDO_HEADER = 'a4a4CCn'
|
137
|
+
|
138
|
+
def pseudo_header payload_length
|
139
|
+
src = HTON[@src] ||= IPAddr.new(@src).hton
|
140
|
+
dst = HTON[@dst] ||= IPAddr.new(@dst).hton
|
141
|
+
return [src, dst, 0, @proto, payload_length].pack(FMT_PSEUDO_HEADER)
|
142
|
+
end
|
143
|
+
|
144
|
+
def fragment?
|
145
|
+
return (@offset & (IP_OFFMASK | IP_MF) != 0)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Check that IP or TCP options are valid. Do nothing if they are valid.
|
149
|
+
# Both IP and TCP options are 8-bit TLVs with an inclusive length. Both
|
150
|
+
# have one byte options 0 and 1.
|
151
|
+
def self.check_options options, label='IPv4'
|
152
|
+
while not options.empty?
|
153
|
+
type = options.slice!(0, 1)[0].ord
|
154
|
+
if type == 0 or type == 1
|
155
|
+
next
|
156
|
+
end
|
157
|
+
Pcap.assert !options.empty?,
|
158
|
+
"#{label} option #{type} is missing the length field"
|
159
|
+
length = options.slice!(0, 1)[0].ord
|
160
|
+
Pcap.assert length >= 2,
|
161
|
+
"#{label} option #{type} has invalid length: #{length}"
|
162
|
+
Pcap.assert length - 2 <= options.length,
|
163
|
+
"#{label} option #{type} has truncated data"
|
164
|
+
options.slice! 0, length - 2
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
ReassembleState = ::Struct.new :packets, :bytes, :mf, :overlap
|
169
|
+
|
170
|
+
# Reassemble fragmented IPv4 packets
|
171
|
+
def self.reassemble packets
|
172
|
+
reassembled_packets = []
|
173
|
+
flow_id_to_state = {}
|
174
|
+
packets.each do |packet|
|
175
|
+
if not packet.is_a?(Ethernet) or not packet.payload.is_a?(IPv4)
|
176
|
+
# Ignore non-IPv4 packet
|
177
|
+
elsif not packet.payload.fragment?
|
178
|
+
# Ignore non-fragments
|
179
|
+
else
|
180
|
+
# Get reassembly state
|
181
|
+
ip = packet.payload
|
182
|
+
flow_id = [ip.ip_id, ip.proto, ip.src, ip.dst]
|
183
|
+
state = flow_id_to_state[flow_id]
|
184
|
+
if not state
|
185
|
+
state = ReassembleState.new [], [], true, false
|
186
|
+
flow_id_to_state[flow_id] = state
|
187
|
+
end
|
188
|
+
state.packets << packet
|
189
|
+
|
190
|
+
# Clear the more-fragments flag if no more fragments
|
191
|
+
if ip.offset & IP_MF == 0
|
192
|
+
state.mf = false
|
193
|
+
end
|
194
|
+
|
195
|
+
# Add the bytes
|
196
|
+
start = (ip.offset & IP_OFFMASK) * 8
|
197
|
+
finish = start + ip.payload.length
|
198
|
+
state.bytes.fill nil, start, finish - start
|
199
|
+
start.upto(finish-1) do |i|
|
200
|
+
if not state.bytes[i]
|
201
|
+
byte = ip.payload[i - start].chr
|
202
|
+
state.bytes[i] = byte
|
203
|
+
elsif not state.overlap
|
204
|
+
name = "%s:%s:%d" % [ip.src, ip.dst, ip.proto]
|
205
|
+
Pcap.warning \
|
206
|
+
"IPv4 flow #{name} contains overlapping fragements"
|
207
|
+
state.overlap = true
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# We're done if we've received a fragment without the
|
212
|
+
# more-fragments flag and all the bytes in the buffer have been
|
213
|
+
# set.
|
214
|
+
if not state.mf and state.bytes.all?
|
215
|
+
# Remove fragments from reassembled_packets
|
216
|
+
state.packets.each do |packet|
|
217
|
+
reassembled_packets.delete_if do |reassembled_packet|
|
218
|
+
packet.object_id == reassembled_packet.object_id
|
219
|
+
end
|
220
|
+
end
|
221
|
+
# Remove state
|
222
|
+
flow_id_to_state.delete flow_id
|
223
|
+
# Create new packet
|
224
|
+
packet = state.packets[0].deepdup
|
225
|
+
ipv4 = packet.payload
|
226
|
+
ipv4.offset = 0
|
227
|
+
ipv4.payload = state.bytes.join
|
228
|
+
# Decode
|
229
|
+
begin
|
230
|
+
case ipv4.proto
|
231
|
+
when IPPROTO_TCP
|
232
|
+
ipv4.payload = TCP.from_bytes ipv4.payload
|
233
|
+
when IPPROTO_UDP
|
234
|
+
ipv4.payload = UDP.from_bytes ipv4.payload
|
235
|
+
when IPPROTO_SCTP
|
236
|
+
ipv4.payload = SCTP.from_bytes ipv4.payload
|
237
|
+
end
|
238
|
+
rescue ParseError => e
|
239
|
+
Pcap.warning e
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
reassembled_packets << packet
|
244
|
+
end
|
245
|
+
if !flow_id_to_state.empty?
|
246
|
+
Pcap.warning \
|
247
|
+
"#{flow_id_to_state.length} flow(s) have IPv4 fragments " \
|
248
|
+
"that can't be reassembled"
|
249
|
+
end
|
250
|
+
|
251
|
+
return reassembled_packets
|
252
|
+
end
|
253
|
+
|
254
|
+
def to_s
|
255
|
+
if @payload.is_a? String
|
256
|
+
payload = @payload.inspect
|
257
|
+
else
|
258
|
+
payload = @payload.to_s
|
259
|
+
end
|
260
|
+
return "ipv4(%d, %s, %s, %s)" % [@proto, @src, @dst, payload]
|
261
|
+
end
|
262
|
+
|
263
|
+
def == other
|
264
|
+
return super &&
|
265
|
+
self.proto == other.proto &&
|
266
|
+
self.ip_id == other.ip_id &&
|
267
|
+
self.offset == other.offset &&
|
268
|
+
self.ttl == other.ttl &&
|
269
|
+
self.dscp == other.dscp
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
end
|
274
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# http://www.mudynamics.com
|
3
|
+
# http://labs.mudynamics.com
|
4
|
+
# http://www.pcapr.net
|
5
|
+
|
6
|
+
require 'ipaddr'
|
7
|
+
|
8
|
+
module Mu
|
9
|
+
class Pcap
|
10
|
+
|
11
|
+
class IPv6 < IP
|
12
|
+
FORMAT = 'NnCCa16a16'
|
13
|
+
|
14
|
+
attr_accessor :next_header, :hop_limit
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
super
|
18
|
+
@next_header = 0
|
19
|
+
@hop_limit = 64
|
20
|
+
end
|
21
|
+
|
22
|
+
def v6?
|
23
|
+
return true
|
24
|
+
end
|
25
|
+
|
26
|
+
alias :proto :next_header
|
27
|
+
alias :ttl :hop_limit
|
28
|
+
|
29
|
+
def flow_id
|
30
|
+
if not @payload or @payload.is_a? String
|
31
|
+
return [:ipv6, @next_header, @src, @dst]
|
32
|
+
else
|
33
|
+
return [:ipv6, @src, @dst, @payload.flow_id]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.from_bytes bytes
|
38
|
+
Pcap.assert bytes.bytesize >= 40, 'Truncated IPv6 header: ' +
|
39
|
+
"expected at least 40 bytes, got #{bytes.bytesize} bytes"
|
40
|
+
|
41
|
+
vcl, length, next_header, hop_limit, src, dst =
|
42
|
+
bytes[0, 40].unpack FORMAT
|
43
|
+
version = vcl >> 28 & 0x0f
|
44
|
+
traffic_class = vcl >> 20 & 0xff
|
45
|
+
flow_label = vcl & 0xfffff
|
46
|
+
|
47
|
+
Pcap.assert version == 6, "Wrong IPv6 version: got (#{version})"
|
48
|
+
Pcap.assert bytes.length >= (40 + length), 'Truncated IPv6 header: ' +
|
49
|
+
"expected #{length + 40} bytes, got #{bytes.length} bytes"
|
50
|
+
|
51
|
+
ipv6 = IPv6.new
|
52
|
+
ipv6.next_header = next_header
|
53
|
+
ipv6.hop_limit = hop_limit
|
54
|
+
ipv6.src = IPAddr.new_ntoh(src).to_s
|
55
|
+
ipv6.dst = IPAddr.new_ntoh(dst).to_s
|
56
|
+
|
57
|
+
ipv6.payload_raw = bytes[40..-1]
|
58
|
+
ipv6.next_header, ipv6.payload =
|
59
|
+
payload_from_bytes ipv6, ipv6.next_header, bytes[40...40+length]
|
60
|
+
|
61
|
+
return ipv6
|
62
|
+
end
|
63
|
+
|
64
|
+
# Parse bytes and returns next_header and payload. Skips extension
|
65
|
+
# headers.
|
66
|
+
def self.payload_from_bytes ipv6, next_header, bytes
|
67
|
+
begin
|
68
|
+
case next_header
|
69
|
+
when IPPROTO_TCP
|
70
|
+
payload = TCP.from_bytes bytes
|
71
|
+
when IPPROTO_UDP
|
72
|
+
payload = UDP.from_bytes bytes
|
73
|
+
when IPPROTO_SCTP
|
74
|
+
payload = SCTP.from_bytes bytes
|
75
|
+
when IPPROTO_HOPOPTS
|
76
|
+
next_header, payload = eight_byte_header_from_bytes(ipv6,
|
77
|
+
bytes, 'hop-by-hop options')
|
78
|
+
when IPPROTO_ROUTING
|
79
|
+
next_header, payload = eight_byte_header_from_bytes(ipv6,
|
80
|
+
bytes, 'routing')
|
81
|
+
when IPPROTO_DSTOPTS
|
82
|
+
next_header, payload = eight_byte_header_from_bytes(ipv6,
|
83
|
+
bytes, 'destination options')
|
84
|
+
when IPPROTO_FRAGMENT
|
85
|
+
Pcap.assert bytes.length >= 8,
|
86
|
+
"Truncated IPv6 fragment header"
|
87
|
+
Pcap.assert false, 'IPv6 fragments are not supported'
|
88
|
+
when IPPROTO_AH
|
89
|
+
next_header, payload = ah_header_from_bytes(ipv6,
|
90
|
+
bytes, 'authentication header')
|
91
|
+
when IPPROTO_NONE
|
92
|
+
payload = ''
|
93
|
+
else
|
94
|
+
payload = bytes
|
95
|
+
end
|
96
|
+
rescue ParseError => e
|
97
|
+
Pcap.warning e
|
98
|
+
payload = bytes
|
99
|
+
end
|
100
|
+
return [next_header, payload]
|
101
|
+
end
|
102
|
+
|
103
|
+
# Parse extension header that's a multiple of 8 bytes
|
104
|
+
def self.eight_byte_header_from_bytes ipv6, bytes, name
|
105
|
+
Pcap.assert bytes.length >= 8, "Truncated IPv6 #{name} header"
|
106
|
+
length = (bytes[1].ord + 1) * 8
|
107
|
+
Pcap.assert bytes.length >= length, "Truncated IPv6 #{name} header"
|
108
|
+
return payload_from_bytes(ipv6, bytes[0].ord, bytes[length..-1])
|
109
|
+
end
|
110
|
+
|
111
|
+
# Parse authentication header (whose length field is interpeted differently)
|
112
|
+
def self.ah_header_from_bytes ipv6, bytes, name
|
113
|
+
Pcap.assert bytes.length >= 8, "Truncated IPv6 #{name} header"
|
114
|
+
length = (bytes[1].ord + 2) * 4
|
115
|
+
Pcap.assert bytes.length >= length, "Truncated IPv6 #{name} header"
|
116
|
+
return payload_from_bytes(ipv6, bytes[0].ord, bytes[length..-1])
|
117
|
+
end
|
118
|
+
|
119
|
+
def write io
|
120
|
+
if @payload.is_a? String
|
121
|
+
payload = @payload
|
122
|
+
else
|
123
|
+
string_io = StringIO.new
|
124
|
+
@payload.write string_io, self
|
125
|
+
payload = string_io.string
|
126
|
+
end
|
127
|
+
src = IPAddr.new(@src, Socket::AF_INET6).hton
|
128
|
+
dst = IPAddr.new(@dst, Socket::AF_INET6).hton
|
129
|
+
header = [0x60000000, payload.bytesize, @next_header, @hop_limit,
|
130
|
+
src, dst].pack FORMAT
|
131
|
+
io.write header
|
132
|
+
io.write payload
|
133
|
+
end
|
134
|
+
|
135
|
+
def pseudo_header payload_length
|
136
|
+
return IPAddr.new(@src, Socket::AF_INET6).hton +
|
137
|
+
IPAddr.new(@dst, Socket::AF_INET6).hton +
|
138
|
+
[payload_length, '', @next_header].pack('Na3C')
|
139
|
+
end
|
140
|
+
|
141
|
+
def == other
|
142
|
+
return super &&
|
143
|
+
self.next_header == other.next_header &&
|
144
|
+
self.hop_limit == other.hop_limit
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# http://www.mudynamics.com
|
3
|
+
# http://labs.mudynamics.com
|
4
|
+
# http://www.pcapr.net
|
5
|
+
|
6
|
+
module Mu
|
7
|
+
class Pcap
|
8
|
+
|
9
|
+
class Packet
|
10
|
+
attr_accessor :payload, :payload_raw
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@payload = ''
|
14
|
+
@payload_raw = ''
|
15
|
+
end
|
16
|
+
|
17
|
+
# Get payload as bytes. If the payload is a parsed object, returns
|
18
|
+
# raw payload. Otherwise return unparsed bytes.
|
19
|
+
def payload_bytes
|
20
|
+
if @payload.is_a? String
|
21
|
+
return @payload
|
22
|
+
end
|
23
|
+
return @payload_raw
|
24
|
+
end
|
25
|
+
|
26
|
+
def deepdup
|
27
|
+
dup = self.dup
|
28
|
+
if @payload.respond_to? :deepdup
|
29
|
+
dup.payload = @payload.deepdup
|
30
|
+
else
|
31
|
+
dup.payload = @payload.dup
|
32
|
+
end
|
33
|
+
return dup
|
34
|
+
end
|
35
|
+
|
36
|
+
def flow_id
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
# Reassemble, reorder, and merge packets.
|
41
|
+
def self.normalize packets
|
42
|
+
begin
|
43
|
+
packets = TCP.reorder packets
|
44
|
+
rescue TCP::ReorderError => e
|
45
|
+
Pcap.warning e
|
46
|
+
end
|
47
|
+
|
48
|
+
begin
|
49
|
+
packets = SCTP.reorder packets
|
50
|
+
rescue SCTP::ReorderError => e
|
51
|
+
Pcap.warning e
|
52
|
+
end
|
53
|
+
|
54
|
+
begin
|
55
|
+
packets = TCP.merge packets
|
56
|
+
rescue TCP::MergeError => e
|
57
|
+
Pcap.warning e
|
58
|
+
end
|
59
|
+
return packets
|
60
|
+
end
|
61
|
+
|
62
|
+
# Remove non-L7/DNS/DHCP traffic if there is L7 traffic. Returns
|
63
|
+
# original packets if there is no L7 traffic.
|
64
|
+
IGNORE_UDP_PORTS = [
|
65
|
+
53, # DNS
|
66
|
+
67, 68, # DHCP
|
67
|
+
546, 547 # DHCPv6
|
68
|
+
]
|
69
|
+
|
70
|
+
def self.isolate_l7 packets
|
71
|
+
cleaned_packets = []
|
72
|
+
packets.each do |packet|
|
73
|
+
if TCP.tcp? packet
|
74
|
+
cleaned_packets << packet
|
75
|
+
elsif UDP.udp? packet
|
76
|
+
src_port = packet.payload.payload.src_port
|
77
|
+
dst_port = packet.payload.payload.dst_port
|
78
|
+
if not IGNORE_UDP_PORTS.member? src_port and
|
79
|
+
not IGNORE_UDP_PORTS.member? dst_port
|
80
|
+
cleaned_packets << packet
|
81
|
+
end
|
82
|
+
elsif SCTP.sctp? packet
|
83
|
+
cleaned_packets << packet
|
84
|
+
end
|
85
|
+
end
|
86
|
+
if cleaned_packets.empty?
|
87
|
+
return packets
|
88
|
+
end
|
89
|
+
return cleaned_packets
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_bytes
|
93
|
+
io = StringIO.new
|
94
|
+
write io
|
95
|
+
io.close
|
96
|
+
return "#{io.string}\0"
|
97
|
+
end
|
98
|
+
|
99
|
+
def == other
|
100
|
+
return self.class == other.class && self.payload == other.payload &&
|
101
|
+
self.payload_raw == other.payload_raw
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|