em-sflow 1.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.
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+
19
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in em-sflow.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Norman Elton
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,56 @@
1
+ = sFlow Library for EventMachine
2
+
3
+ == Summary
4
+
5
+ This gem collects, parses, and optionally proxies sampled flow records from network switches and routers. For information about sFlow, refer to www.sflow.org[http://www.sflow.org].
6
+
7
+ == Features
8
+
9
+ Version 1.0.0
10
+
11
+ * Ability to parse the following sFlow messages: Raw Packet Header, Generic Interface Counter, Ethernet Interface Counter
12
+ * All other sFlow messages are stubbed out. Their implementation should take no more than a few minutes for someone with access to an sFlow-capable device exporting these message types.
13
+ * Ability to proxy a copy of the sFlow packet to a secondary collector.
14
+
15
+
16
+ == Examples
17
+
18
+ Collector:
19
+
20
+ EM.run {
21
+ c = EventMachine::SFlow::Collector.new(:host => "127.0.0.1")
22
+
23
+ c.on_sflow do |pkt|
24
+ puts "Got #{pkt.samples.count} samples"
25
+
26
+ pkt.samples.each do |sample|
27
+ sample.records.each do |record|
28
+ if record.is_a? EM::SFlow::RawPacketHeader
29
+ puts "Received a sampled packet from #{pkt.agent} of length #{record.header.size}"
30
+ elsif record.is_a? EM::SFlow::GenericInterfaceCounters
31
+ puts "Interface #{record.if_index} on #{pkt.agent} has seen #{record.if_in_octets} inbound bytes, #{record.if_out_octets} outbound bytes"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ }
37
+
38
+ Proxy:
39
+
40
+ EM.run {
41
+ c = EventMachine::SFlow::Collector.new(:host => "127.0.0.1")
42
+
43
+ c.proxy_to "192.168.1.1"
44
+ }
45
+
46
+ Multiple callbacks and proxy targets can be defined simultaneously
47
+
48
+ == Change Log
49
+
50
+ Version 1.0.0:
51
+
52
+ * Initial release
53
+
54
+ == Credits
55
+
56
+ Author: Norman Elton normelton@gmail.com
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/em-sflow/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Norman Elton"]
6
+ gem.email = ["normelton@gmail.com"]
7
+ gem.description = "EventMachine-powered sFlow Collector"
8
+ gem.summary = "EventMachine-powered sFlow Collector"
9
+ gem.homepage = "http://github.com/normelton/em-sflow"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "em-sflow"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = EventMachine::SFlow::VERSION
17
+
18
+ gem.extra_rdoc_files = ["README.rdoc"]
19
+ gem.rdoc_options = ["--line-numbers", "--inline-source", "--title", "EM-SFLOW", "--main", "README.rdoc"]
20
+
21
+ gem.add_runtime_dependency "eventmachine", ">= 0.12.10"
22
+ end
@@ -0,0 +1,20 @@
1
+ require "em-sflow/version"
2
+
3
+ require "eventmachine"
4
+ require "ipaddr"
5
+
6
+ require "em-sflow/collector"
7
+ require "em-sflow/datagram_handler"
8
+ require "em-sflow/binary_string"
9
+ require "em-sflow/packet/datagram"
10
+ require "em-sflow/packet/flow_sample"
11
+ require "em-sflow/packet/counter_sample"
12
+ require "em-sflow/packet/raw_packet_header"
13
+ require "em-sflow/packet/generic_interface_counters"
14
+ require "em-sflow/packet/ethernet_interface_counters"
15
+
16
+ module EventMachine
17
+ module SFlow
18
+ # Your code goes here...
19
+ end
20
+ end
@@ -0,0 +1,9 @@
1
+ module EventMachine
2
+ module SFlow
3
+ module BinaryString
4
+ def advance i
5
+ slice!(0..(i - 1))
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+
2
+ module EventMachine
3
+ module SFlow
4
+ class Collector
5
+ def initialize args = {}
6
+ args[:host] ||= "127.0.0.1"
7
+ args[:port] ||= 6343
8
+
9
+ @callbacks = []
10
+ @proxy_targets = []
11
+
12
+ EventMachine.open_datagram_socket(args[:host], args[:port], DatagramHandler, {:host => args[:host], :callbacks => @callbacks, :proxy_targets => @proxy_targets})
13
+ end
14
+
15
+ def proxy_to target
16
+ @proxy_targets << target
17
+ end
18
+
19
+ def on_sflow &proc
20
+ @callbacks << proc
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,30 @@
1
+ module EventMachine
2
+ module SFlow
3
+ module DatagramHandler
4
+ def initialize args
5
+ @host = args[:host]
6
+ @callbacks = args[:callbacks]
7
+ @proxy_targets = args[:proxy_targets]
8
+ end
9
+
10
+ def receive_data data
11
+ unless @proxy_targets.empty?
12
+ @proxy_socket ||= EventMachine.open_datagram_socket(@host, 0)
13
+
14
+ @proxy_targets.each do |target|
15
+ target_host = target.split(":")[0]
16
+ target_port = target.split(":")[1] || "6343"
17
+
18
+ @proxy_socket.send_datagram(data, target_host, target_port)
19
+ end
20
+ end
21
+
22
+ datagram = Datagram.new(data)
23
+
24
+ @callbacks.each do |callback|
25
+ callback.call datagram
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,40 @@
1
+ class EventMachine::SFlow::CounterSample
2
+ attr_accessor :sequence_number, :source_class, :source_value, :records
3
+ def initialize data
4
+ @records = []
5
+
6
+ data.extend EM::SFlow::BinaryString
7
+
8
+ @sequence_number, source_id_class_value, record_count = data.unpack("NNN")
9
+
10
+ @source_class = source_id_class_value >> 24
11
+ @source_value = source_id_class_value & (2 ** 24 - 1)
12
+
13
+ data.advance(12)
14
+
15
+ record_count.times do
16
+ enterprise_format, length = data.unpack("NN")
17
+
18
+ enterprise = enterprise_format >> 12
19
+ format = enterprise_format & (2 ** 12 - 1)
20
+
21
+ data.advance(8)
22
+
23
+ record_data = data.advance(length)
24
+
25
+ if enterprise == 0 && format == 1
26
+ @records << EM::SFlow::GenericInterfaceCounters.new(record_data)
27
+ elsif enterprise == 0 && format == 2
28
+ @records << EM::SFlow::EthernetInterfaceCounters.new(record_data)
29
+ elsif enterprise == 0 && format == 3
30
+ # @records << EM::SFlow::TokenRingCounters.new(record_data)
31
+ elsif enterprise == 0 && format == 4
32
+ # @records << EM::SFlow::HundredBaseVgCounters.new(record_data)
33
+ elsif enterprise == 0 && format == 5
34
+ # @records << EM::SFlow::VlanCounters.new(record_data)
35
+ elsif enterprise == 0 && format == 1001
36
+ # @records << EM::SFlow::ProcessorInformation.new(record_data)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,46 @@
1
+ class EventMachine::SFlow::Datagram
2
+ attr_reader :version, :agent, :sub_agent_id, :datagram_sequence, :uptime, :samples
3
+
4
+ def initialize data
5
+ data.extend EM::SFlow::BinaryString
6
+
7
+ @samples = []
8
+
9
+ @version, ip_version = data.unpack("NN")
10
+
11
+ data.advance(8)
12
+
13
+ if ip_version == 1
14
+ @agent = IPAddr.new(data.unpack("N").first, Socket::AF_INET)
15
+ data.advance(4)
16
+ else
17
+ ip_elements = data.unpack("NNNN")
18
+ @agent = IPAddr.new((ip_elements[0] << 96) + (ip_elements[1] << 64) + (ip_elements[2] << 32) + ip_elements[3], Socket::AF_INET6)
19
+ data.advance(16)
20
+ end
21
+
22
+ @sub_agent_id, @datagram_sequence, @uptime, sample_count = data.unpack("NNNN")
23
+
24
+ data.advance(16)
25
+
26
+ sample_count.times do
27
+ enterprise_format, length = data.unpack("NN")
28
+ enterprise = enterprise_format >> 12
29
+ format = enterprise_format & (2 ** 12 - 1)
30
+
31
+ data.advance(8)
32
+
33
+ sample_data = data.advance(length)
34
+
35
+ if enterprise == 0 && format == 1
36
+ @samples << EM::SFlow::FlowSample.new(sample_data)
37
+ elsif enterprise == 0 && format == 2
38
+ @samples << EM::SFlow::CounterSample.new(sample_data)
39
+ elsif enterprise == 0 && format == 3
40
+ # @samples << EM::SFlow::ExpandedFlowSample.new(sample_data)
41
+ elsif enterprise == 0 && format == 4
42
+ # @samples << EM::SFlow::ExpandedCounterSample.new(sample_data)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,9 @@
1
+ class EventMachine::SFlow::EthernetInterfaceCounters
2
+ attr_accessor :alignment_errors, :fcs_errors, :single_collision_frames, :multiple_collision_frames, :sqe_test_errors, :deferred_transmissions, :late_collisions, :excessive_collisions, :internal_mac_transmit_errors, :carrier_sense_errors, :frames_too_long, :internal_mac_receive_errors, :symbol_errors
3
+
4
+ def initialize data
5
+ data.extend EventMachine::SFlow::BinaryString
6
+
7
+ @alignment_errors, @fcs_errors, @single_collision_frames, @multiple_collision_frames, @sqe_test_errors, @deferred_transmissions, @late_collisions, @excessive_collisions, @internal_mac_transmit_errors, @carrier_sense_errors, @frames_too_long, @internal_mac_receive_errors, @symbol_errors = data.unpack("NNNNNNNNNNNNN")
8
+ end
9
+ end
@@ -0,0 +1,61 @@
1
+ class EventMachine::SFlow::FlowSample
2
+ attr_accessor :sequence_number, :source_class, :source_value, :sampling_rate, :sample_pool, :drop_count, :input_count, :output_count, :records
3
+
4
+ def initialize data
5
+ data.extend EM::SFlow::BinaryString
6
+
7
+ @records = []
8
+
9
+ @sequence_number, source_id_class_value, @sampling_rate, @sample_pool, @drop_count, @input_count, @output_count, record_count = data.unpack("NNNNNNNN")
10
+
11
+ @source_class = source_id_class_value >> 24
12
+ @source_value = source_id_class_value & (2 ** 24 - 1)
13
+
14
+ data.advance(32)
15
+
16
+ record_count.times do
17
+ enterprise_format, length = data.unpack("NN")
18
+
19
+ enterprise = enterprise_format >> 12
20
+ format = enterprise_format & (2 ** 12 - 1)
21
+
22
+ data.advance(8)
23
+
24
+ record_data = data.advance(length)
25
+
26
+ if enterprise == 0 && format == 1
27
+ @records << EM::SFlow::RawPacketHeader.new(record_data)
28
+ elsif enterprise == 0 && format == 2
29
+ # @records << EM::SFlow::EthernetFrameData.new(record_data)
30
+ elsif enterprise == 0 && format == 3
31
+ # @records << EM::SFlow::IPv4Data.new(record_data)
32
+ elsif enterprise == 0 && format == 4
33
+ # @records << EM::SFlow::IPv6Data.new(record_data)
34
+ elsif enterprise == 0 && format == 1001
35
+ # @records << EM::SFlow::ExtendedSwitchData.new(record_data)
36
+ elsif enterprise == 0 && format == 1002
37
+ # @records << EM::SFlow::ExtendedRouterData.new(record_data)
38
+ elsif enterprise == 0 && format == 1003
39
+ # @records << EM::SFlow::ExtendedGatewayData.new(record_data)
40
+ elsif enterprise == 0 && format == 1004
41
+ # @records << EM::SFlow::ExtendedUserData.new(record_data)
42
+ elsif enterprise == 0 && format == 1005
43
+ # @records << EM::SFlow::ExtendedUrlData.new(record_data)
44
+ elsif enterprise == 0 && format == 1006
45
+ # @records << EM::SFlow::ExtendedMplsData.new(record_data)
46
+ elsif enterprise == 0 && format == 1007
47
+ # @records << EM::SFlow::ExtendedNatData.new(record_data)
48
+ elsif enterprise == 0 && format == 1008
49
+ # @records << EM::SFlow::ExtendedMplsTunnel.new(record_data)
50
+ elsif enterprise == 0 && format == 1009
51
+ # @records << EM::SFlow::ExtendedMplsVc.new(record_data)
52
+ elsif enterprise == 0 && format == 1010
53
+ # @records << EM::SFlow::ExtendedMplsFec.new(record_data)
54
+ elsif enterprise == 0 && format == 1011
55
+ # @records << EM::SFlow::ExtendedMplsLvpFec.new(record_data)
56
+ elsif enterprise == 0 && format == 1012
57
+ # @records << EM::SFlow::ExtendedVlanTunnel.new(record_data)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,26 @@
1
+ class EventMachine::SFlow::GenericInterfaceCounters
2
+ attr_accessor :if_index, :if_type, :if_speed, :if_direction, :if_admin_status, :if_oper_status, :if_in_octets, :if_in_ucast_pkts, :if_in_mcast_pkts, :if_in_bcast_pkts, :if_in_discards, :if_in_errors, :if_in_unknown_protocols, :if_out_octets, :if_out_ucast_pkts, :if_out_mcast_pkts, :if_out_bcast_pkts, :if_out_discards, :if_out_errors, :if_promiscuous
3
+
4
+ def initialize data
5
+ data.extend EventMachine::SFlow::BinaryString
6
+
7
+ @if_index, @if_type, if_speed_a, if_speed_b, @if_direction, if_status = data.unpack("NNNNNN")
8
+ @if_speed = (if_speed_a << 32) + if_speed_b
9
+ @if_admin_status = (if_status >> 31) && 1
10
+ @if_oper_status = (if_status >> 30) && 1
11
+
12
+ data.advance(24)
13
+
14
+ if_in_octets_a, if_in_octets_b, @if_in_ucast_pkts, @if_in_mcast_pkts, @if_in_bcast_pkts, @if_in_discards, @if_in_errors, @if_in_unknown_protocols = data.unpack("NNNNNNNN")
15
+ @if_in_octets = (if_in_octets_a << 32) + if_in_octets_b
16
+
17
+ data.advance(32)
18
+
19
+ if_out_octets_a, if_out_octets_b, @if_out_ucast_pkts, @if_out_mcast_pkts, @if_out_bcast_pkts, @if_out_discards, @if_out_errors = data.unpack("NNNNNNN")
20
+ @if_out_octets = (if_out_octets_a << 32) + if_out_octets_b
21
+
22
+ data.advance(28)
23
+
24
+ @if_promiscuous = data.unpack("N").first
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ class EventMachine::SFlow::RawPacketHeader
2
+ attr_accessor :protocol, :frame_length, :strip_count, :header
3
+
4
+ def initialize data
5
+ data.extend EventMachine::SFlow::BinaryString
6
+
7
+ @protocol, @frame_length, @strip_count, length = data.unpack("NNNN")
8
+ data.advance(16)
9
+
10
+ @header = data
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ module EventMachine
2
+ module SFlow
3
+ VERSION = "1.0.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-sflow
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Norman Elton
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: eventmachine
16
+ requirement: &70342806966360 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.12.10
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70342806966360
25
+ description: EventMachine-powered sFlow Collector
26
+ email:
27
+ - normelton@gmail.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files:
31
+ - README.rdoc
32
+ files:
33
+ - .gitignore
34
+ - Gemfile
35
+ - LICENSE
36
+ - README.rdoc
37
+ - Rakefile
38
+ - em-sflow.gemspec
39
+ - lib/em-sflow.rb
40
+ - lib/em-sflow/binary_string.rb
41
+ - lib/em-sflow/collector.rb
42
+ - lib/em-sflow/datagram_handler.rb
43
+ - lib/em-sflow/packet/counter_sample.rb
44
+ - lib/em-sflow/packet/datagram.rb
45
+ - lib/em-sflow/packet/ethernet_interface_counters.rb
46
+ - lib/em-sflow/packet/flow_sample.rb
47
+ - lib/em-sflow/packet/generic_interface_counters.rb
48
+ - lib/em-sflow/packet/raw_packet_header.rb
49
+ - lib/em-sflow/version.rb
50
+ homepage: http://github.com/normelton/em-sflow
51
+ licenses: []
52
+ post_install_message:
53
+ rdoc_options:
54
+ - --line-numbers
55
+ - --inline-source
56
+ - --title
57
+ - EM-SFLOW
58
+ - --main
59
+ - README.rdoc
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 1.8.5
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: EventMachine-powered sFlow Collector
80
+ test_files: []