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.
Files changed (35) hide show
  1. data/.gitignore +4 -4
  2. data/DIY-pcap.gemspec +17 -17
  3. data/Gemfile +3 -3
  4. data/Rakefile +1 -1
  5. data/lib/DIY-pcap.rb +2 -2
  6. data/lib/diy/command.rb +7 -1
  7. data/lib/diy/controller.rb +10 -15
  8. data/lib/diy/live.rb +9 -2
  9. data/lib/diy/parser/mu/pcap/ethernet.rb +148 -148
  10. data/lib/diy/parser/mu/pcap/header.rb +75 -75
  11. data/lib/diy/parser/mu/pcap/io_pair.rb +67 -67
  12. data/lib/diy/parser/mu/pcap/io_wrapper.rb +76 -76
  13. data/lib/diy/parser/mu/pcap/ip.rb +61 -61
  14. data/lib/diy/parser/mu/pcap/ipv4.rb +257 -257
  15. data/lib/diy/parser/mu/pcap/ipv6.rb +148 -148
  16. data/lib/diy/parser/mu/pcap/packet.rb +104 -104
  17. data/lib/diy/parser/mu/pcap/pkthdr.rb +155 -155
  18. data/lib/diy/parser/mu/pcap/reader.rb +61 -61
  19. data/lib/diy/parser/mu/pcap/reader/http_family.rb +170 -170
  20. data/lib/diy/parser/mu/pcap/sctp.rb +367 -367
  21. data/lib/diy/parser/mu/pcap/sctp/chunk.rb +123 -123
  22. data/lib/diy/parser/mu/pcap/sctp/chunk/data.rb +134 -134
  23. data/lib/diy/parser/mu/pcap/sctp/chunk/init.rb +100 -100
  24. data/lib/diy/parser/mu/pcap/sctp/chunk/init_ack.rb +68 -68
  25. data/lib/diy/parser/mu/pcap/sctp/parameter.rb +110 -110
  26. data/lib/diy/parser/mu/pcap/sctp/parameter/ip_address.rb +48 -48
  27. data/lib/diy/parser/mu/pcap/stream_packetizer.rb +72 -72
  28. data/lib/diy/parser/mu/pcap/tcp.rb +505 -505
  29. data/lib/diy/parser/mu/pcap/udp.rb +69 -69
  30. data/lib/diy/parser/mu/scenario/pcap.rb +172 -172
  31. data/lib/diy/parser/mu/scenario/pcap/fields.rb +50 -50
  32. data/lib/diy/parser/mu/scenario/pcap/rtp.rb +71 -71
  33. data/lib/diy/parser/pcap.rb +109 -109
  34. data/lib/diy/version.rb +1 -1
  35. metadata +7 -9
@@ -1,69 +1,69 @@
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 UDP < Packet
9
- attr_accessor :src_port, :dst_port
10
-
11
- def initialize src_port=0, dst_port=0
12
- super()
13
- @src_port = src_port
14
- @dst_port = dst_port
15
- end
16
-
17
- def flow_id
18
- return [:udp, @src_port, @dst_port]
19
- end
20
-
21
- FMT_nnnn = 'nnnn'
22
- def self.from_bytes bytes
23
- bytes_length = bytes.length
24
- bytes_length >= 8 or
25
- raise ParseError, "Truncated UDP header: expected 8 bytes, got #{bytes_length} bytes"
26
- sport, dport, length, checksum = bytes.unpack(FMT_nnnn)
27
- bytes_length >= length or
28
- raise ParseError, "Truncated UDP packet: expected #{length} bytes, got #{bytes_length} bytes"
29
- udp = UDP.new sport, dport
30
- udp.payload_raw = bytes[8..-1]
31
- udp.payload = bytes[8..length]
32
- return udp
33
- end
34
-
35
- def write io, ip
36
- length = @payload.length
37
- length_8 = length + 8
38
- if length_8 > 65535
39
- Pcap.warning "UDP payload is too large"
40
- end
41
- pseudo_header = ip.pseudo_header length_8
42
- header = [@src_port, @dst_port, length_8, 0] \
43
- .pack FMT_nnnn
44
- checksum = IP.checksum(pseudo_header + header + @payload)
45
- header = [@src_port, @dst_port, length_8, checksum] \
46
- .pack FMT_nnnn
47
- io.write header
48
- io.write @payload
49
- end
50
-
51
- def self.udp? packet
52
- return packet.is_a?(Ethernet) &&
53
- packet.payload.is_a?(IP) &&
54
- packet.payload.payload.is_a?(UDP)
55
- end
56
-
57
- def to_s
58
- return "udp(%d, %d, %s)" % [@src_port, @dst_port, @payload.inspect]
59
- end
60
-
61
- def == other
62
- return super &&
63
- self.src_port == other.src_port &&
64
- self.dst_port == other.dst_port
65
- end
66
- end
67
-
68
- end
69
- end
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 UDP < Packet
9
+ attr_accessor :src_port, :dst_port
10
+
11
+ def initialize src_port=0, dst_port=0
12
+ super()
13
+ @src_port = src_port
14
+ @dst_port = dst_port
15
+ end
16
+
17
+ def flow_id
18
+ return [:udp, @src_port, @dst_port]
19
+ end
20
+
21
+ FMT_nnnn = 'nnnn'
22
+ def self.from_bytes bytes
23
+ bytes_length = bytes.length
24
+ bytes_length >= 8 or
25
+ raise ParseError, "Truncated UDP header: expected 8 bytes, got #{bytes_length} bytes"
26
+ sport, dport, length, checksum = bytes.unpack(FMT_nnnn)
27
+ bytes_length >= length or
28
+ raise ParseError, "Truncated UDP packet: expected #{length} bytes, got #{bytes_length} bytes"
29
+ udp = UDP.new sport, dport
30
+ udp.payload_raw = bytes[8..-1]
31
+ udp.payload = bytes[8..length]
32
+ return udp
33
+ end
34
+
35
+ def write io, ip
36
+ length = @payload.length
37
+ length_8 = length + 8
38
+ if length_8 > 65535
39
+ Pcap.warning "UDP payload is too large"
40
+ end
41
+ pseudo_header = ip.pseudo_header length_8
42
+ header = [@src_port, @dst_port, length_8, 0] \
43
+ .pack FMT_nnnn
44
+ checksum = IP.checksum(pseudo_header + header + @payload)
45
+ header = [@src_port, @dst_port, length_8, checksum] \
46
+ .pack FMT_nnnn
47
+ io.write header
48
+ io.write @payload
49
+ end
50
+
51
+ def self.udp? packet
52
+ return packet.is_a?(Ethernet) &&
53
+ packet.payload.is_a?(IP) &&
54
+ packet.payload.payload.is_a?(UDP)
55
+ end
56
+
57
+ def to_s
58
+ return "udp(%d, %d, %s)" % [@src_port, @dst_port, @payload.inspect]
59
+ end
60
+
61
+ def == other
62
+ return super &&
63
+ self.src_port == other.src_port &&
64
+ self.dst_port == other.dst_port
65
+ end
66
+ end
67
+
68
+ end
69
+ end
@@ -1,172 +1,172 @@
1
- # http://www.mudynamics.com
2
- # http://labs.mudynamics.com
3
- # http://www.pcapr.net
4
-
5
- require 'tempfile'
6
- require 'fileutils'
7
- require 'mu/scenario/pcap/fields'
8
- require 'mu/pcap'
9
- require 'json'
10
-
11
- module Mu
12
- class Scenario
13
-
14
- module Pcap
15
- TSHARK_READ_TIMEOUT = 10.0 # seconds
16
- TSHARK_LINES_PER_PACKET = 16384
17
- TSHARK_OPTS = "-n -o tcp.desegment_tcp_streams:false"
18
- TSHARK_OPTS_SUFFIX = TSHARK_OPTS
19
- TSHARK_SIZE_OPTS = "-n -o 'column.format: cum_size, \"%B\"'"
20
- TSHARK_PSML_OPTS = %Q{#{TSHARK_OPTS} -o 'column.format: "Protocol", "%p", "Info", "%i"'}
21
-
22
- MAX_PCAP_SIZE = 102400 # 100KB
23
- MAX_RAW_PCAP_SIZE_MB = 25
24
- MAX_RAW_PCAP_SIZE = MAX_RAW_PCAP_SIZE_MB * 1024 * 1000
25
- EXCLUDE_FROM_SIZE_CHECK = ['rtp'].freeze
26
-
27
- class PcapTooLarge < StandardError; end
28
-
29
- def self.reset_options options
30
- return unless options
31
- tshark_opts = options << ' ' << TSHARK_OPTS_SUFFIX
32
- remove_const(:TSHARK_OPTS) if const_defined?(:TSHARK_OPTS)
33
- const_set(:TSHARK_OPTS, tshark_opts)
34
- end
35
-
36
- def self.validate_pcap_size(path)
37
- tshark_filter = EXCLUDE_FROM_SIZE_CHECK.map{ |proto| "not #{proto}" }.join " and "
38
- io = ::IO.popen "tshark #{TSHARK_SIZE_OPTS} -r #{path} -R '#{tshark_filter}' | tail -1"
39
- if ::IO.select [ io ], nil, nil, TSHARK_READ_TIMEOUT
40
- if io.eof?
41
- size = 0
42
- else
43
- last_line = io.readline
44
- size = last_line.to_i
45
- end
46
- end
47
-
48
- if size.nil? or size == 0
49
- size = File.size(path)
50
- end
51
-
52
- if size > MAX_PCAP_SIZE
53
- raise PcapTooLarge, "Selected packets have a size of #{size} bytes which " +
54
- "exceeds the #{MAX_PCAP_SIZE} byte maximum."
55
- end
56
-
57
- if size > MAX_RAW_PCAP_SIZE
58
- raise PcapTooLarge, "Selected packets have a raw size of #{size} bytes which " +
59
- "exceeds the #{MAX_RAW_PCAP_SIZE_MB}MB maximum."
60
- end
61
-
62
- return size
63
- end
64
-
65
- PAR_VERSION = 1
66
- def self.export_to_par pcap_path, opts=nil
67
- opts ||= {}
68
-
69
- # Open pcap file
70
- File.exist?(pcap_path) or raise "Cannot open file '#{pcap_path}'."
71
- validate_pcap_size pcap_path
72
- pcap = open pcap_path, 'rb'
73
-
74
- # Get Mu::Pcap::Packets
75
- packets = to_pcap_packets pcap, opts[:isolate_l7]
76
-
77
- # Write normalized packets to tempfile
78
- tmpdir = Dir.mktmpdir
79
- norm_pcap = File.open "#{tmpdir}/normalized.pcap", 'wb'
80
- pcap = Mu::Pcap.from_packets packets
81
- pcap.write norm_pcap
82
- norm_pcap.close
83
-
84
- # Get wireshark dissected field values for all packets.
85
- `tshark -T fields #{TSHARK_OPTS} #{Fields::TSHARK_OPTS} -Eseparator='\xff' -r #{norm_pcap.path} > #{tmpdir}/fields`
86
- fields = open "#{tmpdir}/fields", 'rb'
87
-
88
- # Get wireshark dissected field values for all packets.
89
- fields_array = []
90
- if fields
91
- packets.each do |packet|
92
- fields_array << Fields.next_from_io(fields)
93
- end
94
- end
95
-
96
- # Protocol specific preprocessing, packets may be deleted.
97
- Rtp.preprocess packets, fields_array
98
-
99
- File.open "#{tmpdir}/packets.dump", 'wb' do |f|
100
- Marshal.dump packets, f
101
- end
102
-
103
- # Create a second pcap with packets removed.
104
- norm_pcap = File.open "#{tmpdir}/normalized.pcap", 'wb'
105
- pcap = Mu::Pcap.from_packets packets
106
- pcap.write norm_pcap
107
- norm_pcap.close
108
-
109
- # Dump PSML to file.
110
- # (The no-op filter sometimes produces slighty more verbose descriptions.)
111
- `tshark -T psml #{TSHARK_PSML_OPTS} -r #{norm_pcap.path} -R 'rtp or not rtp' > #{tmpdir}/psml`
112
-
113
- # Dump PDML io file.
114
- `tshark -T pdml #{TSHARK_OPTS} -r #{norm_pcap.path} > #{tmpdir}/pdml`
115
- pdml = open "#{tmpdir}/pdml", 'rb'
116
-
117
- # Create about
118
- open "#{tmpdir}/about", 'w' do |about|
119
- about.puts({:par_version => PAR_VERSION}.to_json)
120
- end
121
-
122
- # Create zip
123
- Dir.chdir tmpdir do
124
- system "zip -q dissected.zip about pdml psml fields packets.dump normalized.pcap"
125
- return open("dissected.zip")
126
- end
127
- ensure
128
- if tmpdir
129
- FileUtils.rm_rf tmpdir
130
- end
131
- end
132
-
133
- def self.to_pcap_packets io, isolate_l7=true
134
- packets = []
135
-
136
- # Read Pcap packets from Pcap
137
- Mu::Pcap.each_pkthdr io do |pkthdr|
138
- if pkthdr.len != pkthdr.caplen
139
- raise Mu::Pcap::ParseError, "Error: Capture contains truncated packets. " +
140
- "Try recapturing with an increased snapshot length."
141
- end
142
- if not pkthdr.pkt.is_a? Mu::Pcap::Ethernet
143
- warning 'Unable to parse packet, skipping.'
144
- end
145
- packets << pkthdr.pkt
146
- end
147
-
148
- if (packets.length == 0)
149
- raise Mu::Pcap::ParseError, "No valid packets found!"
150
- end
151
-
152
- packets = Mu::Pcap::IPv4.reassemble packets
153
-
154
- if isolate_l7
155
- packets = Mu::Pcap::Packet.isolate_l7 packets
156
- end
157
-
158
- packets = Mu::Pcap::Packet.normalize packets
159
- packets = Mu::Pcap::TCP.split packets
160
-
161
- packets
162
- end
163
-
164
- def self.warning msg
165
- $stderr.puts "WARNING: #{msg}"#, caller, $!
166
- end
167
- end
168
-
169
- end
170
- end
171
-
172
- require 'mu/scenario/pcap/rtp'
1
+ # http://www.mudynamics.com
2
+ # http://labs.mudynamics.com
3
+ # http://www.pcapr.net
4
+
5
+ require 'tempfile'
6
+ require 'fileutils'
7
+ require 'mu/scenario/pcap/fields'
8
+ require 'mu/pcap'
9
+ require 'json'
10
+
11
+ module Mu
12
+ class Scenario
13
+
14
+ module Pcap
15
+ TSHARK_READ_TIMEOUT = 10.0 # seconds
16
+ TSHARK_LINES_PER_PACKET = 16384
17
+ TSHARK_OPTS = "-n -o tcp.desegment_tcp_streams:false"
18
+ TSHARK_OPTS_SUFFIX = TSHARK_OPTS
19
+ TSHARK_SIZE_OPTS = "-n -o 'column.format: cum_size, \"%B\"'"
20
+ TSHARK_PSML_OPTS = %Q{#{TSHARK_OPTS} -o 'column.format: "Protocol", "%p", "Info", "%i"'}
21
+
22
+ MAX_PCAP_SIZE = 102400 # 100KB
23
+ MAX_RAW_PCAP_SIZE_MB = 25
24
+ MAX_RAW_PCAP_SIZE = MAX_RAW_PCAP_SIZE_MB * 1024 * 1000
25
+ EXCLUDE_FROM_SIZE_CHECK = ['rtp'].freeze
26
+
27
+ class PcapTooLarge < StandardError; end
28
+
29
+ def self.reset_options options
30
+ return unless options
31
+ tshark_opts = options << ' ' << TSHARK_OPTS_SUFFIX
32
+ remove_const(:TSHARK_OPTS) if const_defined?(:TSHARK_OPTS)
33
+ const_set(:TSHARK_OPTS, tshark_opts)
34
+ end
35
+
36
+ def self.validate_pcap_size(path)
37
+ tshark_filter = EXCLUDE_FROM_SIZE_CHECK.map{ |proto| "not #{proto}" }.join " and "
38
+ io = ::IO.popen "tshark #{TSHARK_SIZE_OPTS} -r #{path} -R '#{tshark_filter}' | tail -1"
39
+ if ::IO.select [ io ], nil, nil, TSHARK_READ_TIMEOUT
40
+ if io.eof?
41
+ size = 0
42
+ else
43
+ last_line = io.readline
44
+ size = last_line.to_i
45
+ end
46
+ end
47
+
48
+ if size.nil? or size == 0
49
+ size = File.size(path)
50
+ end
51
+
52
+ if size > MAX_PCAP_SIZE
53
+ raise PcapTooLarge, "Selected packets have a size of #{size} bytes which " +
54
+ "exceeds the #{MAX_PCAP_SIZE} byte maximum."
55
+ end
56
+
57
+ if size > MAX_RAW_PCAP_SIZE
58
+ raise PcapTooLarge, "Selected packets have a raw size of #{size} bytes which " +
59
+ "exceeds the #{MAX_RAW_PCAP_SIZE_MB}MB maximum."
60
+ end
61
+
62
+ return size
63
+ end
64
+
65
+ PAR_VERSION = 1
66
+ def self.export_to_par pcap_path, opts=nil
67
+ opts ||= {}
68
+
69
+ # Open pcap file
70
+ File.exist?(pcap_path) or raise "Cannot open file '#{pcap_path}'."
71
+ validate_pcap_size pcap_path
72
+ pcap = open pcap_path, 'rb'
73
+
74
+ # Get Mu::Pcap::Packets
75
+ packets = to_pcap_packets pcap, opts[:isolate_l7]
76
+
77
+ # Write normalized packets to tempfile
78
+ tmpdir = Dir.mktmpdir
79
+ norm_pcap = File.open "#{tmpdir}/normalized.pcap", 'wb'
80
+ pcap = Mu::Pcap.from_packets packets
81
+ pcap.write norm_pcap
82
+ norm_pcap.close
83
+
84
+ # Get wireshark dissected field values for all packets.
85
+ `tshark -T fields #{TSHARK_OPTS} #{Fields::TSHARK_OPTS} -Eseparator='\xff' -r #{norm_pcap.path} > #{tmpdir}/fields`
86
+ fields = open "#{tmpdir}/fields", 'rb'
87
+
88
+ # Get wireshark dissected field values for all packets.
89
+ fields_array = []
90
+ if fields
91
+ packets.each do |packet|
92
+ fields_array << Fields.next_from_io(fields)
93
+ end
94
+ end
95
+
96
+ # Protocol specific preprocessing, packets may be deleted.
97
+ Rtp.preprocess packets, fields_array
98
+
99
+ File.open "#{tmpdir}/packets.dump", 'wb' do |f|
100
+ Marshal.dump packets, f
101
+ end
102
+
103
+ # Create a second pcap with packets removed.
104
+ norm_pcap = File.open "#{tmpdir}/normalized.pcap", 'wb'
105
+ pcap = Mu::Pcap.from_packets packets
106
+ pcap.write norm_pcap
107
+ norm_pcap.close
108
+
109
+ # Dump PSML to file.
110
+ # (The no-op filter sometimes produces slighty more verbose descriptions.)
111
+ `tshark -T psml #{TSHARK_PSML_OPTS} -r #{norm_pcap.path} -R 'rtp or not rtp' > #{tmpdir}/psml`
112
+
113
+ # Dump PDML io file.
114
+ `tshark -T pdml #{TSHARK_OPTS} -r #{norm_pcap.path} > #{tmpdir}/pdml`
115
+ pdml = open "#{tmpdir}/pdml", 'rb'
116
+
117
+ # Create about
118
+ open "#{tmpdir}/about", 'w' do |about|
119
+ about.puts({:par_version => PAR_VERSION}.to_json)
120
+ end
121
+
122
+ # Create zip
123
+ Dir.chdir tmpdir do
124
+ system "zip -q dissected.zip about pdml psml fields packets.dump normalized.pcap"
125
+ return open("dissected.zip")
126
+ end
127
+ ensure
128
+ if tmpdir
129
+ FileUtils.rm_rf tmpdir
130
+ end
131
+ end
132
+
133
+ def self.to_pcap_packets io, isolate_l7=true
134
+ packets = []
135
+
136
+ # Read Pcap packets from Pcap
137
+ Mu::Pcap.each_pkthdr io do |pkthdr|
138
+ if pkthdr.len != pkthdr.caplen
139
+ raise Mu::Pcap::ParseError, "Error: Capture contains truncated packets. " +
140
+ "Try recapturing with an increased snapshot length."
141
+ end
142
+ if not pkthdr.pkt.is_a? Mu::Pcap::Ethernet
143
+ warning 'Unable to parse packet, skipping.'
144
+ end
145
+ packets << pkthdr.pkt
146
+ end
147
+
148
+ if (packets.length == 0)
149
+ raise Mu::Pcap::ParseError, "No valid packets found!"
150
+ end
151
+
152
+ packets = Mu::Pcap::IPv4.reassemble packets
153
+
154
+ if isolate_l7
155
+ packets = Mu::Pcap::Packet.isolate_l7 packets
156
+ end
157
+
158
+ packets = Mu::Pcap::Packet.normalize packets
159
+ packets = Mu::Pcap::TCP.split packets
160
+
161
+ packets
162
+ end
163
+
164
+ def self.warning msg
165
+ $stderr.puts "WARNING: #{msg}"#, caller, $!
166
+ end
167
+ end
168
+
169
+ end
170
+ end
171
+
172
+ require 'mu/scenario/pcap/rtp'