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
data/.gitignore CHANGED
@@ -1,5 +1,5 @@
1
- *.gem
2
- .bundle
3
- Gemfile.lock
4
- pkg/*
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
5
  *.txt
@@ -1,19 +1,19 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "diy/version"
4
-
5
- Gem::Specification.new do |s|
6
- s.name = "DIY-pcap"
7
- s.version = DIY::PCAP::VERSION
8
- s.authors = ["yafei Lee"]
9
- s.email = ["lyfi2003@gmail.com"]
10
- s.homepage = ""
11
- s.summary = %q{DIY pcap send and recv}
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "diy/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "DIY-pcap"
7
+ s.version = DIY::PCAP::VERSION
8
+ s.authors = ["yafei Lee"]
9
+ s.email = ["lyfi2003@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{DIY pcap send and recv}
12
12
  s.description = %q{DIY pcap send and recv}
13
-
14
- s.files = `git ls-files`.split("\n")
15
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
17
  s.require_paths = ["lib"]
18
- s.add_dependency "ffi-pcap", ">=0.2.0"
19
- end
18
+ s.add_dependency "ffi-pcap", ">=0.2.0"
19
+ end
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source "http://ruby.taobao.org"
2
-
3
- # Specify your gem's dependencies in DIY-pcap.gemspec
1
+ source "http://ruby.taobao.org"
2
+
3
+ # Specify your gem's dependencies in DIY-pcap.gemspec
4
4
  gem "ffi-pcap", ">=0.2.0"
data/Rakefile CHANGED
@@ -1 +1 @@
1
- require 'bundler/gem_tasks'
1
+ require 'bundler/gem_tasks'
@@ -1,3 +1,3 @@
1
- require "diy/version"
2
- require 'diy/pcap'
1
+ require "diy/version"
2
+ require 'diy/pcap'
3
3
  require 'diy/dig'
@@ -12,6 +12,7 @@ end
12
12
 
13
13
  options = {
14
14
  :ip => "0.0.0.0:#{$_PORT}",
15
+ :promisc => true,
15
16
  }
16
17
 
17
18
  OptionParser.new do |opts|
@@ -34,6 +35,11 @@ OptionParser.new do |opts|
34
35
  exit 0
35
36
  end
36
37
 
38
+ opts.on_tail("-o", "--non-promious", "Disable device's promiscuous mode", "default is enabled") do
39
+ DIY::Logger.info "Device's promiscuous mode is DISABLED"
40
+ options[:promisc] = false
41
+ end
42
+
37
43
  opts.on_tail("--timer","Use TimerIdConv module instead of DRb's default idconv") do
38
44
  options[:timer] = true
39
45
  end
@@ -70,7 +76,7 @@ else
70
76
  require 'diy/device_finder'
71
77
  device_name = DIY::DeviceFinder.smart_select
72
78
  end
73
- device = DIY::Live.new(device_name)
79
+ device = DIY::Live.new(device_name, :promisc=> options[:promisc])
74
80
  worker = DIY::Worker.new(device)
75
81
  worker_keeper = DIY::WorkerKeeper.new(worker, uri)
76
82
  if options[:timer]
@@ -124,7 +124,6 @@ module DIY
124
124
  end
125
125
 
126
126
  def client_send(client, pkts)
127
- #~ pkt_contents = pkts.collect { |pkt| pkt.content }
128
127
  begin
129
128
  client.inject(pkts)
130
129
  rescue FFI::PCap::LibError =>e
@@ -172,22 +171,18 @@ module DIY
172
171
  end
173
172
 
174
173
  def wait_recv_ok(pkts)
175
- loop do
174
+ @timeout ||= 10
175
+ tick = 0
176
+ until pkts.empty? do
176
177
  now_size = pkts.size
177
- break if now_size == 0
178
- wait_until(@timeout ||= 10) do
179
- raise @error_flag if @error_flag
180
- pkts.size < now_size
181
- end
182
- end
183
- end
184
-
185
- def wait_until( timeout = 10, &block )
186
- Timeout.timeout(timeout, DIY::HopePacketTimeoutError.new("hope packet wait timeout after #{timeout} seconds") ) do
187
- loop do
188
- break if block.call
189
- sleep 0.01
178
+ raise @error_flag if @error_flag
179
+ tick += 0.01
180
+ sleep 0.01
181
+ if pkts.size < now_size
182
+ # clear tick time if at least one packet has passed the judgement of stratgies.
183
+ tick = 0
190
184
  end
185
+ raise DIY::HopePacketTimeoutError.new("hope packet wait timeout after #{@timeout} seconds") if !pkts.empty? && tick >= @timeout
191
186
  end
192
187
  end
193
188
 
@@ -4,9 +4,12 @@ require 'diy/ext/capture_wrapper'
4
4
 
5
5
  module DIY
6
6
  class Live
7
- def initialize(device_name)
7
+ def initialize(device_name, args = {})
8
8
  DIY::Logger.info( "Initialize Live: #{device_name}" )
9
- @live = FFI::PCap::Live.new(:dev=>device_name, :handler => FFI::PCap::CopyHandler, :promisc => true, :timeout=>1)
9
+
10
+ default = { :dev=>device_name, :handler => FFI::PCap::CopyHandler, :promisc => true, :timeout=>1 }
11
+ default = merge_arguments(default, args)
12
+ @live = FFI::PCap::Live.new(default)
10
13
  DIY::Logger.info( "Listen on: #{net} " )
11
14
  @running = false
12
15
  @live.non_blocking= true
@@ -45,6 +48,10 @@ module DIY
45
48
  @live.network + " / " + @live.netmask
46
49
  end
47
50
 
51
+ def merge_arguments(default, new)
52
+ default.merge(new)
53
+ end
54
+
48
55
  end # end of class Live
49
56
 
50
57
  end
@@ -1,160 +1,160 @@
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 Ethernet < Packet
9
- attr_accessor :src, :dst, :type
10
-
11
- ETHERTYPE_IP = 0x0800
12
- ETHERTYPE_IP6 = 0x86dd
13
- ETHERTYPE_ARP = 0x0806
14
- ETHERTYPE_PPPOE_SESSION = 0x8864
15
- ETHERTYPE_802_1Q = 0X8100
16
-
17
- PPP_IP = 0x0021
18
- PPP_IPV6 = 0x0057
19
-
20
- def initialize src=nil, dst=nil, type=0
21
- super()
22
- @src = src
23
- @dst = dst
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 Ethernet < Packet
9
+ attr_accessor :src, :dst, :type
10
+
11
+ ETHERTYPE_IP = 0x0800
12
+ ETHERTYPE_IP6 = 0x86dd
13
+ ETHERTYPE_ARP = 0x0806
14
+ ETHERTYPE_PPPOE_SESSION = 0x8864
15
+ ETHERTYPE_802_1Q = 0X8100
16
+
17
+ PPP_IP = 0x0021
18
+ PPP_IPV6 = 0x0057
19
+
20
+ def initialize src=nil, dst=nil, type=0
21
+ super()
22
+ @src = src
23
+ @dst = dst
24
24
  @type = type
25
- @vlan = false
25
+ @vlan = false
26
26
  end
27
27
  attr_accessor :vlan
28
28
 
29
29
  def vlan?
30
30
  vlan
31
- end
32
-
33
- def flow_id
34
- if not @payload or @payload.is_a? String
35
- return [:ethernet, @src, @dst, @type]
36
- else
37
- return @payload.flow_id
38
- end
39
- end
40
-
41
- FMT_MAC = "C6"
42
- FMT_n = 'n'
43
- MAC_TEMPLATE = '%02x:%02x:%02x:%02x:%02x:%02x'
44
- def self.from_bytes bytes
45
- if bytes.length < 14
46
- raise ParseError, "Truncated Ethernet header: expected 14 bytes, got #{bytes.length} bytes"
47
- end
48
-
49
- dst = bytes.slice!(0,6).unpack FMT_MAC
50
- dst = MAC_TEMPLATE % dst
51
- src = bytes.slice!(0,6).unpack FMT_MAC
52
- src = MAC_TEMPLATE % src
31
+ end
32
+
33
+ def flow_id
34
+ if not @payload or @payload.is_a? String
35
+ return [:ethernet, @src, @dst, @type]
36
+ else
37
+ return @payload.flow_id
38
+ end
39
+ end
40
+
41
+ FMT_MAC = "C6"
42
+ FMT_n = 'n'
43
+ MAC_TEMPLATE = '%02x:%02x:%02x:%02x:%02x:%02x'
44
+ def self.from_bytes bytes
45
+ if bytes.length < 14
46
+ raise ParseError, "Truncated Ethernet header: expected 14 bytes, got #{bytes.length} bytes"
47
+ end
48
+
49
+ dst = bytes.slice!(0,6).unpack FMT_MAC
50
+ dst = MAC_TEMPLATE % dst
51
+ src = bytes.slice!(0,6).unpack FMT_MAC
52
+ src = MAC_TEMPLATE % src
53
53
  type = bytes.slice!(0,2).unpack(FMT_n)[0]
54
54
  if type == ETHERTYPE_802_1Q
55
55
  @vlan = true
56
56
  else
57
57
  @vlan = false
58
- end
59
- while (type == ETHERTYPE_802_1Q)
60
- # Skip 4 bytes for 802.1q vlan tag field
61
- bytes.slice!(0,2)
62
- type = bytes.slice!(0,2).unpack(FMT_n)[0]
63
- end
58
+ end
59
+ while (type == ETHERTYPE_802_1Q)
60
+ # Skip 4 bytes for 802.1q vlan tag field
61
+ bytes.slice!(0,2)
62
+ type = bytes.slice!(0,2).unpack(FMT_n)[0]
63
+ end
64
64
  ethernet = Ethernet.new src, dst, type
65
- ethernet.vlan = @vlan
66
- ethernet.payload = bytes
67
- ethernet.payload_raw = bytes
68
- begin
69
- case type
70
- when ETHERTYPE_IP
71
- ethernet.payload = IPv4.from_bytes bytes
72
- when ETHERTYPE_IP6
73
- ethernet.payload = IPv6.from_bytes bytes
74
- when ETHERTYPE_PPPOE_SESSION
75
- # Remove PPPoE/PPP session layer
76
- ethernet.payload = bytes
77
- ethernet.remove_pppoe!
78
- else
79
- ethernet.payload = bytes
80
- end
81
- rescue ParseError => e
82
- Pcap.warning e
83
- end
84
- return ethernet
85
- end
86
-
87
- def ip?
88
- return payload.is_a?(IP)
89
- end
90
-
91
- ADDR_TO_BYTES = {}
92
- FMT_HEADER = 'a6a6n'
93
- def write io
94
- dst_mac = ADDR_TO_BYTES[@dst] ||= @dst.split(':').inject('') {|m, b| m << b.to_i(16).chr}
95
- src_mac = ADDR_TO_BYTES[@src] ||= @src.split(':').inject('') {|m, b| m << b.to_i(16).chr}
96
- bytes = [dst_mac, src_mac, @type].pack(FMT_HEADER)
97
- io.write bytes
98
- if @payload.is_a? String
99
- io.write @payload
100
- else
101
- @payload.write io
102
- end
103
- end
104
-
105
- # Remove the PPPoE and PPP headers. PPPoE is documented in RFC 2516.
106
- def remove_pppoe!
107
- bytes = self.payload_raw
108
-
109
- # Remove PPPoE header
110
- Pcap.assert bytes.length >= 6, 'Truncated PPPoE header: ' +
111
- "expected at least 6 bytes, got #{bytes.length} bytes"
112
- version_type, code, session_id, length = bytes.unpack 'CCnn'
113
- version = version_type >> 4 & 0b1111
114
- type = version_type & 0b1111
115
- Pcap.assert version == 1, "Unknown PPPoE version: #{version}"
116
- Pcap.assert type == 1, "Unknown PPPoE type: #{type}"
117
- Pcap.assert code == 0, "Unknown PPPoE code: #{code}"
118
- bytes = bytes[6..-1]
119
- Pcap.assert bytes.length >= length, 'Truncated PPoE packet: ' +
120
- "expected #{length} bytes, got #{bytes.length} bytes"
121
-
122
- # Remove PPP header
123
- Pcap.assert bytes.length >= 2, 'Truncated PPP packet: ' +
124
- "expected at least bytes, got #{bytes.length} bytes"
125
- protocol_id, = bytes.unpack 'n'
126
- bytes = bytes[2..-1]
127
- case protocol_id
128
- when PPP_IP
129
- self.payload = IPv4.from_bytes bytes
130
- self.payload_raw = bytes
131
- self.type = ETHERTYPE_IP
132
- when PPP_IPV6
133
- self.payload = IPv6.from_bytes bytes
134
- self.payload_raw = bytes
135
- self.type = ETHERTYPE_IP6
136
- else
137
- # Failed. Don't update payload or type.
138
- raise ParseError, "Unknown PPP protocol: 0x#{'%04x' % protocol_id}"
139
- end
140
- end
141
-
142
- def to_s
143
- if @payload.is_a? String
144
- payload = @payload.inspect
145
- else
146
- payload = @payload.to_s
147
- end
148
- return "ethernet(%s, %s, %d, %s)" % [@src, @dst, @type, payload]
149
- end
150
-
151
- def == other
152
- return super &&
153
- self.src == other.src &&
154
- self.dst == other.dst &&
155
- self.type == other.type
156
- end
157
- end
158
-
159
- end
160
- end
65
+ ethernet.vlan = @vlan
66
+ ethernet.payload = bytes
67
+ ethernet.payload_raw = bytes
68
+ begin
69
+ case type
70
+ when ETHERTYPE_IP
71
+ ethernet.payload = IPv4.from_bytes bytes
72
+ when ETHERTYPE_IP6
73
+ ethernet.payload = IPv6.from_bytes bytes
74
+ when ETHERTYPE_PPPOE_SESSION
75
+ # Remove PPPoE/PPP session layer
76
+ ethernet.payload = bytes
77
+ ethernet.remove_pppoe!
78
+ else
79
+ ethernet.payload = bytes
80
+ end
81
+ rescue ParseError => e
82
+ Pcap.warning e
83
+ end
84
+ return ethernet
85
+ end
86
+
87
+ def ip?
88
+ return payload.is_a?(IP)
89
+ end
90
+
91
+ ADDR_TO_BYTES = {}
92
+ FMT_HEADER = 'a6a6n'
93
+ def write io
94
+ dst_mac = ADDR_TO_BYTES[@dst] ||= @dst.split(':').inject('') {|m, b| m << b.to_i(16).chr}
95
+ src_mac = ADDR_TO_BYTES[@src] ||= @src.split(':').inject('') {|m, b| m << b.to_i(16).chr}
96
+ bytes = [dst_mac, src_mac, @type].pack(FMT_HEADER)
97
+ io.write bytes
98
+ if @payload.is_a? String
99
+ io.write @payload
100
+ else
101
+ @payload.write io
102
+ end
103
+ end
104
+
105
+ # Remove the PPPoE and PPP headers. PPPoE is documented in RFC 2516.
106
+ def remove_pppoe!
107
+ bytes = self.payload_raw
108
+
109
+ # Remove PPPoE header
110
+ Pcap.assert bytes.length >= 6, 'Truncated PPPoE header: ' +
111
+ "expected at least 6 bytes, got #{bytes.length} bytes"
112
+ version_type, code, session_id, length = bytes.unpack 'CCnn'
113
+ version = version_type >> 4 & 0b1111
114
+ type = version_type & 0b1111
115
+ Pcap.assert version == 1, "Unknown PPPoE version: #{version}"
116
+ Pcap.assert type == 1, "Unknown PPPoE type: #{type}"
117
+ Pcap.assert code == 0, "Unknown PPPoE code: #{code}"
118
+ bytes = bytes[6..-1]
119
+ Pcap.assert bytes.length >= length, 'Truncated PPoE packet: ' +
120
+ "expected #{length} bytes, got #{bytes.length} bytes"
121
+
122
+ # Remove PPP header
123
+ Pcap.assert bytes.length >= 2, 'Truncated PPP packet: ' +
124
+ "expected at least bytes, got #{bytes.length} bytes"
125
+ protocol_id, = bytes.unpack 'n'
126
+ bytes = bytes[2..-1]
127
+ case protocol_id
128
+ when PPP_IP
129
+ self.payload = IPv4.from_bytes bytes
130
+ self.payload_raw = bytes
131
+ self.type = ETHERTYPE_IP
132
+ when PPP_IPV6
133
+ self.payload = IPv6.from_bytes bytes
134
+ self.payload_raw = bytes
135
+ self.type = ETHERTYPE_IP6
136
+ else
137
+ # Failed. Don't update payload or type.
138
+ raise ParseError, "Unknown PPP protocol: 0x#{'%04x' % protocol_id}"
139
+ end
140
+ end
141
+
142
+ def to_s
143
+ if @payload.is_a? String
144
+ payload = @payload.inspect
145
+ else
146
+ payload = @payload.to_s
147
+ end
148
+ return "ethernet(%s, %s, %d, %s)" % [@src, @dst, @type, payload]
149
+ end
150
+
151
+ def == other
152
+ return super &&
153
+ self.src == other.src &&
154
+ self.dst == other.dst &&
155
+ self.type == other.type
156
+ end
157
+ end
158
+
159
+ end
160
+ end