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
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