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.
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,148 +1,148 @@
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 IPv6 < IP
11
- FORMAT = 'NnCCa16a16'
12
-
13
- attr_accessor :next_header, :hop_limit
14
-
15
- def initialize
16
- super
17
- @next_header = 0
18
- @hop_limit = 64
19
- end
20
-
21
- def v6?
22
- return true
23
- end
24
-
25
- alias :proto :next_header
26
- alias :ttl :hop_limit
27
-
28
- def flow_id
29
- if not @payload or @payload.is_a? String
30
- return [:ipv6, @next_header, @src, @dst]
31
- else
32
- return [:ipv6, @src, @dst, @payload.flow_id]
33
- end
34
- end
35
-
36
- def self.from_bytes bytes
37
- Pcap.assert bytes.length >= 40, 'Truncated IPv6 header: ' +
38
- "expected at least 40 bytes, got #{bytes.length} bytes"
39
-
40
- vcl, length, next_header, hop_limit, src, dst =
41
- bytes[0, 40].unpack FORMAT
42
- version = vcl >> 28 & 0x0f
43
- traffic_class = vcl >> 20 & 0xff
44
- flow_label = vcl & 0xfffff
45
-
46
- Pcap.assert version == 6, "Wrong IPv6 version: got (#{version})"
47
- Pcap.assert bytes.length >= (40 + length), 'Truncated IPv6 header: ' +
48
- "expected #{length + 40} bytes, got #{bytes.length} bytes"
49
-
50
- ipv6 = IPv6.new
51
- ipv6.next_header = next_header
52
- ipv6.hop_limit = hop_limit
53
- ipv6.src = IPAddr.new_ntoh(src).to_s
54
- ipv6.dst = IPAddr.new_ntoh(dst).to_s
55
-
56
- ipv6.payload_raw = bytes[40..-1]
57
- ipv6.next_header, ipv6.payload =
58
- payload_from_bytes ipv6, ipv6.next_header, bytes[40...40+length]
59
-
60
- return ipv6
61
- end
62
-
63
- # Parse bytes and returns next_header and payload. Skips extension
64
- # headers.
65
- def self.payload_from_bytes ipv6, next_header, bytes
66
- begin
67
- case next_header
68
- when IPPROTO_TCP
69
- payload = TCP.from_bytes bytes
70
- when IPPROTO_UDP
71
- payload = UDP.from_bytes bytes
72
- when IPPROTO_SCTP
73
- payload = SCTP.from_bytes bytes
74
- when IPPROTO_HOPOPTS
75
- next_header, payload = eight_byte_header_from_bytes(ipv6,
76
- bytes, 'hop-by-hop options')
77
- when IPPROTO_ROUTING
78
- next_header, payload = eight_byte_header_from_bytes(ipv6,
79
- bytes, 'routing')
80
- when IPPROTO_DSTOPTS
81
- next_header, payload = eight_byte_header_from_bytes(ipv6,
82
- bytes, 'destination options')
83
- when IPPROTO_FRAGMENT
84
- Pcap.assert bytes.length >= 8,
85
- "Truncated IPv6 fragment header"
86
- Pcap.assert false, 'IPv6 fragments are not supported'
87
- when IPPROTO_AH
88
- next_header, payload = ah_header_from_bytes(ipv6,
89
- bytes, 'authentication header')
90
- when IPPROTO_NONE
91
- payload = ''
92
- else
93
- payload = bytes
94
- end
95
- rescue ParseError => e
96
- Pcap.warning e
97
- payload = bytes
98
- end
99
- return [next_header, payload]
100
- end
101
-
102
- # Parse extension header that's a multiple of 8 bytes
103
- def self.eight_byte_header_from_bytes ipv6, bytes, name
104
- Pcap.assert bytes.length >= 8, "Truncated IPv6 #{name} header"
105
- length = (bytes[1].ord + 1) * 8
106
- Pcap.assert bytes.length >= length, "Truncated IPv6 #{name} header"
107
- return payload_from_bytes(ipv6, bytes[0].ord, bytes[length..-1])
108
- end
109
-
110
- # Parse authentication header (whose length field is interpeted differently)
111
- def self.ah_header_from_bytes ipv6, bytes, name
112
- Pcap.assert bytes.length >= 8, "Truncated IPv6 #{name} header"
113
- length = (bytes[1].ord + 2) * 4
114
- Pcap.assert bytes.length >= length, "Truncated IPv6 #{name} header"
115
- return payload_from_bytes(ipv6, bytes[0].ord, bytes[length..-1])
116
- end
117
-
118
- def write io
119
- if @payload.is_a? String
120
- payload = @payload
121
- else
122
- string_io = StringIO.new
123
- @payload.write string_io, self
124
- payload = string_io.string
125
- end
126
- src = IPAddr.new(@src, Socket::AF_INET6).hton
127
- dst = IPAddr.new(@dst, Socket::AF_INET6).hton
128
- header = [0x60000000, payload.length, @next_header, @hop_limit,
129
- src, dst].pack FORMAT
130
- io.write header
131
- io.write payload
132
- end
133
-
134
- def pseudo_header payload_length
135
- return IPAddr.new(@src, Socket::AF_INET6).hton +
136
- IPAddr.new(@dst, Socket::AF_INET6).hton +
137
- [payload_length, '', @next_header].pack('Na3C')
138
- end
139
-
140
- def == other
141
- return super &&
142
- self.next_header == other.next_header &&
143
- self.hop_limit == other.hop_limit
144
- end
145
- end
146
-
147
- end
148
- end
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 IPv6 < IP
11
+ FORMAT = 'NnCCa16a16'
12
+
13
+ attr_accessor :next_header, :hop_limit
14
+
15
+ def initialize
16
+ super
17
+ @next_header = 0
18
+ @hop_limit = 64
19
+ end
20
+
21
+ def v6?
22
+ return true
23
+ end
24
+
25
+ alias :proto :next_header
26
+ alias :ttl :hop_limit
27
+
28
+ def flow_id
29
+ if not @payload or @payload.is_a? String
30
+ return [:ipv6, @next_header, @src, @dst]
31
+ else
32
+ return [:ipv6, @src, @dst, @payload.flow_id]
33
+ end
34
+ end
35
+
36
+ def self.from_bytes bytes
37
+ Pcap.assert bytes.length >= 40, 'Truncated IPv6 header: ' +
38
+ "expected at least 40 bytes, got #{bytes.length} bytes"
39
+
40
+ vcl, length, next_header, hop_limit, src, dst =
41
+ bytes[0, 40].unpack FORMAT
42
+ version = vcl >> 28 & 0x0f
43
+ traffic_class = vcl >> 20 & 0xff
44
+ flow_label = vcl & 0xfffff
45
+
46
+ Pcap.assert version == 6, "Wrong IPv6 version: got (#{version})"
47
+ Pcap.assert bytes.length >= (40 + length), 'Truncated IPv6 header: ' +
48
+ "expected #{length + 40} bytes, got #{bytes.length} bytes"
49
+
50
+ ipv6 = IPv6.new
51
+ ipv6.next_header = next_header
52
+ ipv6.hop_limit = hop_limit
53
+ ipv6.src = IPAddr.new_ntoh(src).to_s
54
+ ipv6.dst = IPAddr.new_ntoh(dst).to_s
55
+
56
+ ipv6.payload_raw = bytes[40..-1]
57
+ ipv6.next_header, ipv6.payload =
58
+ payload_from_bytes ipv6, ipv6.next_header, bytes[40...40+length]
59
+
60
+ return ipv6
61
+ end
62
+
63
+ # Parse bytes and returns next_header and payload. Skips extension
64
+ # headers.
65
+ def self.payload_from_bytes ipv6, next_header, bytes
66
+ begin
67
+ case next_header
68
+ when IPPROTO_TCP
69
+ payload = TCP.from_bytes bytes
70
+ when IPPROTO_UDP
71
+ payload = UDP.from_bytes bytes
72
+ when IPPROTO_SCTP
73
+ payload = SCTP.from_bytes bytes
74
+ when IPPROTO_HOPOPTS
75
+ next_header, payload = eight_byte_header_from_bytes(ipv6,
76
+ bytes, 'hop-by-hop options')
77
+ when IPPROTO_ROUTING
78
+ next_header, payload = eight_byte_header_from_bytes(ipv6,
79
+ bytes, 'routing')
80
+ when IPPROTO_DSTOPTS
81
+ next_header, payload = eight_byte_header_from_bytes(ipv6,
82
+ bytes, 'destination options')
83
+ when IPPROTO_FRAGMENT
84
+ Pcap.assert bytes.length >= 8,
85
+ "Truncated IPv6 fragment header"
86
+ Pcap.assert false, 'IPv6 fragments are not supported'
87
+ when IPPROTO_AH
88
+ next_header, payload = ah_header_from_bytes(ipv6,
89
+ bytes, 'authentication header')
90
+ when IPPROTO_NONE
91
+ payload = ''
92
+ else
93
+ payload = bytes
94
+ end
95
+ rescue ParseError => e
96
+ Pcap.warning e
97
+ payload = bytes
98
+ end
99
+ return [next_header, payload]
100
+ end
101
+
102
+ # Parse extension header that's a multiple of 8 bytes
103
+ def self.eight_byte_header_from_bytes ipv6, bytes, name
104
+ Pcap.assert bytes.length >= 8, "Truncated IPv6 #{name} header"
105
+ length = (bytes[1].ord + 1) * 8
106
+ Pcap.assert bytes.length >= length, "Truncated IPv6 #{name} header"
107
+ return payload_from_bytes(ipv6, bytes[0].ord, bytes[length..-1])
108
+ end
109
+
110
+ # Parse authentication header (whose length field is interpeted differently)
111
+ def self.ah_header_from_bytes ipv6, bytes, name
112
+ Pcap.assert bytes.length >= 8, "Truncated IPv6 #{name} header"
113
+ length = (bytes[1].ord + 2) * 4
114
+ Pcap.assert bytes.length >= length, "Truncated IPv6 #{name} header"
115
+ return payload_from_bytes(ipv6, bytes[0].ord, bytes[length..-1])
116
+ end
117
+
118
+ def write io
119
+ if @payload.is_a? String
120
+ payload = @payload
121
+ else
122
+ string_io = StringIO.new
123
+ @payload.write string_io, self
124
+ payload = string_io.string
125
+ end
126
+ src = IPAddr.new(@src, Socket::AF_INET6).hton
127
+ dst = IPAddr.new(@dst, Socket::AF_INET6).hton
128
+ header = [0x60000000, payload.length, @next_header, @hop_limit,
129
+ src, dst].pack FORMAT
130
+ io.write header
131
+ io.write payload
132
+ end
133
+
134
+ def pseudo_header payload_length
135
+ return IPAddr.new(@src, Socket::AF_INET6).hton +
136
+ IPAddr.new(@dst, Socket::AF_INET6).hton +
137
+ [payload_length, '', @next_header].pack('Na3C')
138
+ end
139
+
140
+ def == other
141
+ return super &&
142
+ self.next_header == other.next_header &&
143
+ self.hop_limit == other.hop_limit
144
+ end
145
+ end
146
+
147
+ end
148
+ end
@@ -1,104 +1,104 @@
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 Packet
9
- attr_accessor :payload, :payload_raw
10
-
11
- def initialize
12
- @payload = ''
13
- @payload_raw = ''
14
- end
15
-
16
- # Get payload as bytes. If the payload is a parsed object, returns
17
- # raw payload. Otherwise return unparsed bytes.
18
- def payload_bytes
19
- if @payload.is_a? String
20
- return @payload
21
- end
22
- return @payload_raw
23
- end
24
-
25
- def deepdup
26
- dup = self.dup
27
- if @payload.respond_to? :deepdup
28
- dup.payload = @payload.deepdup
29
- else
30
- dup.payload = @payload.dup
31
- end
32
- return dup
33
- end
34
-
35
- def flow_id
36
- raise NotImplementedError
37
- end
38
-
39
- # Reassemble, reorder, and merge packets.
40
- def self.normalize packets
41
- begin
42
- packets = TCP.reorder packets
43
- rescue TCP::ReorderError => e
44
- Pcap.warning e
45
- end
46
-
47
- begin
48
- packets = SCTP.reorder packets
49
- rescue SCTP::ReorderError => e
50
- Pcap.warning e
51
- end
52
-
53
- begin
54
- packets = TCP.merge packets
55
- rescue TCP::MergeError => e
56
- Pcap.warning e
57
- end
58
- return packets
59
- end
60
-
61
- # Remove non-L7/DNS/DHCP traffic if there is L7 traffic. Returns
62
- # original packets if there is no L7 traffic.
63
- IGNORE_UDP_PORTS = [
64
- 53, # DNS
65
- 67, 68, # DHCP
66
- 546, 547 # DHCPv6
67
- ]
68
- def self.isolate_l7 packets
69
- cleaned_packets = []
70
- packets.each do |packet|
71
- if TCP.tcp? packet
72
- cleaned_packets << packet
73
- elsif UDP.udp? packet
74
- src_port = packet.payload.payload.src_port
75
- dst_port = packet.payload.payload.dst_port
76
- if not IGNORE_UDP_PORTS.member? src_port and
77
- not IGNORE_UDP_PORTS.member? dst_port
78
- cleaned_packets << packet
79
- end
80
- elsif SCTP.sctp? packet
81
- cleaned_packets << packet
82
- end
83
- end
84
- if cleaned_packets.empty?
85
- return packets
86
- end
87
- return cleaned_packets
88
- end
89
-
90
- def to_bytes
91
- io = StringIO.new
92
- write io
93
- io.close
94
- return io.string
95
- end
96
-
97
- def == other
98
- return self.class == other.class && self.payload == other.payload &&
99
- self.payload_raw == other.payload_raw
100
- end
101
- end
102
-
103
- end
104
- 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 Packet
9
+ attr_accessor :payload, :payload_raw
10
+
11
+ def initialize
12
+ @payload = ''
13
+ @payload_raw = ''
14
+ end
15
+
16
+ # Get payload as bytes. If the payload is a parsed object, returns
17
+ # raw payload. Otherwise return unparsed bytes.
18
+ def payload_bytes
19
+ if @payload.is_a? String
20
+ return @payload
21
+ end
22
+ return @payload_raw
23
+ end
24
+
25
+ def deepdup
26
+ dup = self.dup
27
+ if @payload.respond_to? :deepdup
28
+ dup.payload = @payload.deepdup
29
+ else
30
+ dup.payload = @payload.dup
31
+ end
32
+ return dup
33
+ end
34
+
35
+ def flow_id
36
+ raise NotImplementedError
37
+ end
38
+
39
+ # Reassemble, reorder, and merge packets.
40
+ def self.normalize packets
41
+ begin
42
+ packets = TCP.reorder packets
43
+ rescue TCP::ReorderError => e
44
+ Pcap.warning e
45
+ end
46
+
47
+ begin
48
+ packets = SCTP.reorder packets
49
+ rescue SCTP::ReorderError => e
50
+ Pcap.warning e
51
+ end
52
+
53
+ begin
54
+ packets = TCP.merge packets
55
+ rescue TCP::MergeError => e
56
+ Pcap.warning e
57
+ end
58
+ return packets
59
+ end
60
+
61
+ # Remove non-L7/DNS/DHCP traffic if there is L7 traffic. Returns
62
+ # original packets if there is no L7 traffic.
63
+ IGNORE_UDP_PORTS = [
64
+ 53, # DNS
65
+ 67, 68, # DHCP
66
+ 546, 547 # DHCPv6
67
+ ]
68
+ def self.isolate_l7 packets
69
+ cleaned_packets = []
70
+ packets.each do |packet|
71
+ if TCP.tcp? packet
72
+ cleaned_packets << packet
73
+ elsif UDP.udp? packet
74
+ src_port = packet.payload.payload.src_port
75
+ dst_port = packet.payload.payload.dst_port
76
+ if not IGNORE_UDP_PORTS.member? src_port and
77
+ not IGNORE_UDP_PORTS.member? dst_port
78
+ cleaned_packets << packet
79
+ end
80
+ elsif SCTP.sctp? packet
81
+ cleaned_packets << packet
82
+ end
83
+ end
84
+ if cleaned_packets.empty?
85
+ return packets
86
+ end
87
+ return cleaned_packets
88
+ end
89
+
90
+ def to_bytes
91
+ io = StringIO.new
92
+ write io
93
+ io.close
94
+ return io.string
95
+ end
96
+
97
+ def == other
98
+ return self.class == other.class && self.payload == other.payload &&
99
+ self.payload_raw == other.payload_raw
100
+ end
101
+ end
102
+
103
+ end
104
+ end