fluent-plugin-sflow 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.gitmodules +3 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +35 -0
- data/Rakefile +12 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/example/fluentd.conf +9 -0
- data/fluent-plugin-sflow.gemspec +42 -0
- data/lib/fluent/plugin/in_sflow.rb +45 -0
- data/lib/sflow/Gemfile +8 -0
- data/lib/sflow/Gemfile.lock +38 -0
- data/lib/sflow/LICENSE.txt +22 -0
- data/lib/sflow/README.md +67 -0
- data/lib/sflow/Rakefile +12 -0
- data/lib/sflow/bin/bundler +16 -0
- data/lib/sflow/bin/rake +16 -0
- data/lib/sflow/bin/sflow.rb +7 -0
- data/lib/sflow/etc/config.yaml +10 -0
- data/lib/sflow/lib/sflow.rb +10 -0
- data/lib/sflow/lib/sflow/collector.rb +69 -0
- data/lib/sflow/lib/sflow/config.rb +15 -0
- data/lib/sflow/lib/sflow/models/binary_models.rb +176 -0
- data/lib/sflow/lib/sflow/models/ipv4header.rb +69 -0
- data/lib/sflow/lib/sflow/models/protocol.rb +47 -0
- data/lib/sflow/lib/sflow/models/tcpheader.rb +82 -0
- data/lib/sflow/lib/sflow/models/udpheader.rb +36 -0
- data/lib/sflow/lib/sflow/parsers/parsers.rb +68 -0
- data/lib/sflow/lib/sflow/snmp/iface_names.rb +40 -0
- data/lib/sflow/lib/sflow/storage/storage.rb +34 -0
- data/lib/sflow/lib/sflow/version.rb +3 -0
- data/lib/sflow/misc/kibana-schema.json +1364 -0
- data/lib/sflow/misc/screen1.png +0 -0
- data/lib/sflow/sflow.gemspec +23 -0
- data/lib/sflow/test/lib/sflow/version_test.rb +8 -0
- data/lib/sflow/test/test_helper.rb +4 -0
- metadata +180 -0
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
dir = File.expand_path(File.join(File.dirname(__FILE__), 'sflow'))
|
6
|
+
['config','models/ipv4header', 'models/tcpheader', 'models/udpheader', 'models/protocol', 'models/binary_models','parsers/parsers','storage/storage', 'collector','snmp/iface_names'].each do |req|
|
7
|
+
require File.join(dir, req)
|
8
|
+
end
|
9
|
+
|
10
|
+
Process.daemon(true) if $daemonize == true
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class SflowCollector
|
2
|
+
module Collector
|
3
|
+
Thread.abort_on_exception=true
|
4
|
+
require 'socket'
|
5
|
+
def post_init
|
6
|
+
puts "Server listening."
|
7
|
+
end
|
8
|
+
|
9
|
+
def receive_data(data)
|
10
|
+
operation = proc do
|
11
|
+
begin
|
12
|
+
if data != nil
|
13
|
+
sflow = SflowParser.parse_packet(data)
|
14
|
+
end
|
15
|
+
rescue Exception => e
|
16
|
+
puts Time.now
|
17
|
+
puts sflow.inspect
|
18
|
+
puts e.message
|
19
|
+
puts e.backtrace
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
callback = proc do |sflow|
|
24
|
+
begin
|
25
|
+
if sflow != nil
|
26
|
+
SflowStorage.send_udpjson(sflow)
|
27
|
+
end
|
28
|
+
rescue Exception => e
|
29
|
+
puts Time.now
|
30
|
+
puts sflow.inspect if sflow != nil
|
31
|
+
puts e.message
|
32
|
+
puts e.backtrace
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
EM.defer(operation,callback)
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.start_collector(bind_ip = '0.0.0.0', bind_port = 6343)
|
42
|
+
begin
|
43
|
+
config = SflowConfig.new
|
44
|
+
if config.logstash_host and config.logstash_port
|
45
|
+
puts "Connecting to Logstash: #{config.logstash_host}:#{config.logstash_port}"
|
46
|
+
$logstash = UDPSocket.new
|
47
|
+
$logstash.connect(config.logstash_host, config.logstash_port)
|
48
|
+
else
|
49
|
+
puts "no host:port given"
|
50
|
+
exit 1
|
51
|
+
end
|
52
|
+
$switch_hash = config.switch_hash
|
53
|
+
if config.switch_hash != nil
|
54
|
+
$switchportnames = SNMPwalk.new(config.switch_hash.each_key)
|
55
|
+
end
|
56
|
+
EventMachine::run do
|
57
|
+
EventMachine::open_datagram_socket(bind_ip, bind_port, Collector)
|
58
|
+
end
|
59
|
+
rescue Exception => e
|
60
|
+
puts Time.now
|
61
|
+
puts e.message
|
62
|
+
puts e.backtrace
|
63
|
+
raise "unable to start sflow collector"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class SflowConfig
|
2
|
+
attr_reader :switch_hash
|
3
|
+
attr_reader :logstash_host
|
4
|
+
attr_reader :logstash_port
|
5
|
+
attr_reader :daemonize
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
config = YAML.load_file("etc/config.yaml")
|
9
|
+
@switch_hash = config['switch']
|
10
|
+
@logstash_host = config['logstash_host']
|
11
|
+
@logstash_port = config['logstash_port']
|
12
|
+
@daemonize = config['daemonize']
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,176 @@
|
|
1
|
+
class Header < BinData::Record
|
2
|
+
endian :big
|
3
|
+
uint32 :version
|
4
|
+
uint32 :address_type
|
5
|
+
uint32 :agent_address
|
6
|
+
uint32 :sub_agent_id
|
7
|
+
uint32 :seq_number
|
8
|
+
uint32 :sys_uptime
|
9
|
+
uint32 :num_samples
|
10
|
+
array :flow_samples, :initial_length => :num_samples do
|
11
|
+
uint16 :enterprise_std
|
12
|
+
uint16 :sflow_sample_type
|
13
|
+
uint32 :sample_length
|
14
|
+
string :sample_data, :length => :sample_length
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Sflow5sampleheader1 < BinData::Record
|
19
|
+
endian :big
|
20
|
+
uint32 :seq_number
|
21
|
+
uint32 :source_id_type
|
22
|
+
uint32 :sampling_rate
|
23
|
+
uint32 :sample_pool
|
24
|
+
uint32 :dropped_packets
|
25
|
+
uint32 :i_iface_value
|
26
|
+
uint32 :o_iface_value
|
27
|
+
uint32 :num_records
|
28
|
+
array :records, :initial_length => :num_records do
|
29
|
+
uint16 :enterprise
|
30
|
+
uint16 :format
|
31
|
+
uint32 :flow_length
|
32
|
+
string :record_data, :length => :flow_length
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
class Sflow5sampleheader3 < BinData::Record
|
38
|
+
endian :big
|
39
|
+
uint32 :seq_number
|
40
|
+
uint32 :source_id_type
|
41
|
+
uint32 :source_id_index
|
42
|
+
uint32 :sampling_rate
|
43
|
+
uint32 :sample_pool
|
44
|
+
uint32 :dropped_packets
|
45
|
+
uint32 :i_iface_format
|
46
|
+
uint32 :i_iface_value
|
47
|
+
uint32 :o_iface_format
|
48
|
+
uint32 :o_iface_value
|
49
|
+
uint32 :num_records
|
50
|
+
array :records, :initial_length => :num_records do
|
51
|
+
uint16 :enterprise
|
52
|
+
uint16 :format
|
53
|
+
uint32 :flow_length
|
54
|
+
string :record_data, :length => :flow_length
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
class Sflow5counterheader4 < BinData::Record
|
61
|
+
endian :big
|
62
|
+
uint32 :seq_number
|
63
|
+
uint32 :source_id_type
|
64
|
+
uint32 :source_id_index
|
65
|
+
uint32 :num_records
|
66
|
+
array :records, :initial_length => :num_records do
|
67
|
+
uint16 :enterprise
|
68
|
+
uint16 :format
|
69
|
+
uint32 :record_length
|
70
|
+
string :record_data, :length => :record_length
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class Sflow5counterheader2 < BinData::Record
|
75
|
+
endian :big
|
76
|
+
uint32 :seq_number
|
77
|
+
uint32 :source_id_type
|
78
|
+
uint32 :num_records
|
79
|
+
array :records, :initial_length => :num_records do
|
80
|
+
uint16 :enterprise
|
81
|
+
uint16 :format
|
82
|
+
uint32 :record_length
|
83
|
+
string :record_data, :length => :record_length
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
class Sflow5rawpacket < BinData::Record
|
89
|
+
endian :big
|
90
|
+
uint32 :header_protocol
|
91
|
+
uint32 :frame_length
|
92
|
+
uint32 :payload
|
93
|
+
uint32 :xy
|
94
|
+
array :rawpacket_data, :read_until => :eof do
|
95
|
+
string :data, :length => 1
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class Sflow5extswitch < BinData::Record
|
100
|
+
endian :big
|
101
|
+
uint32 :src_vlan
|
102
|
+
uint32 :src_priority
|
103
|
+
uint32 :dst_vlan
|
104
|
+
uint32 :dst_priority
|
105
|
+
end
|
106
|
+
|
107
|
+
class Sflow5genericcounter < BinData::Record
|
108
|
+
endian :big
|
109
|
+
uint32 :int_index
|
110
|
+
uint32 :int_type
|
111
|
+
uint64 :int_speed
|
112
|
+
uint32 :int_direction
|
113
|
+
uint16 :int_admin_status
|
114
|
+
uint16 :int_oper_status
|
115
|
+
uint64 :input_octets
|
116
|
+
uint32 :input_packets
|
117
|
+
uint32 :input_packets_multi
|
118
|
+
uint32 :input_packets_broad
|
119
|
+
uint32 :input_packets_discard
|
120
|
+
uint32 :input_packets_error
|
121
|
+
uint32 :unknown_proto
|
122
|
+
uint64 :output_octets
|
123
|
+
uint32 :output_packets
|
124
|
+
uint32 :output_packets_multi
|
125
|
+
uint32 :output_packets_broad
|
126
|
+
uint32 :output_packets_discard
|
127
|
+
uint32 :output_packets_error
|
128
|
+
uint32 :prom_mode
|
129
|
+
end
|
130
|
+
|
131
|
+
class Sflow5ethcounter < BinData::Record
|
132
|
+
endian :big
|
133
|
+
uint32 :alignment_errors
|
134
|
+
uint32 :fcs_errors
|
135
|
+
uint32 :single_collision_frames
|
136
|
+
uint32 :multi_collision_frames
|
137
|
+
uint32 :sqe_test_errors
|
138
|
+
uint32 :deffered_transmission
|
139
|
+
uint32 :late_collision
|
140
|
+
uint32 :excessive_collision
|
141
|
+
uint32 :internal_mac_transmit_errors
|
142
|
+
uint32 :carrier_sense_errors
|
143
|
+
uint32 :frame_too_long
|
144
|
+
uint32 :internal_mac_receive_errors
|
145
|
+
uint32 :symbol_errors
|
146
|
+
end
|
147
|
+
|
148
|
+
class Sflow5rawpacketheaderEthernet < BinData::Record
|
149
|
+
endian :big
|
150
|
+
string :eth_src, :length => 6
|
151
|
+
string :eth_dst, :length => 6
|
152
|
+
uint16 :eth_type
|
153
|
+
array :ethernetdata, :read_until => :eof do
|
154
|
+
string :data, :length => 1
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
class Sflow5rawpacketdata < BinData::Record
|
159
|
+
endian :big
|
160
|
+
string :eth, :length => 14
|
161
|
+
string :vlan_tag, :length => 2
|
162
|
+
string :vlan_tag_p, :length => 2
|
163
|
+
string :vlana, :length => 2
|
164
|
+
string :vlanb, :length => 2
|
165
|
+
string :ip_packet, :length => 40
|
166
|
+
end
|
167
|
+
|
168
|
+
class Sflow5rawpacketdataVLAN < BinData::Record
|
169
|
+
endian :big
|
170
|
+
uint16 :prio
|
171
|
+
uint16 :type
|
172
|
+
array :vlandata, :read_until => :eof do
|
173
|
+
string :data, :length => 1
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require_relative 'protocol'
|
3
|
+
|
4
|
+
# TODO: チェックサムを確認する
|
5
|
+
class IPv4Header
|
6
|
+
|
7
|
+
attr_reader :version,:header_length,:packet_length,:identification,:frag_dont,:frag_more,:frag_offset,:ttl,:protocol,:checksum,:sndr_addr,:dest_addr,
|
8
|
+
:data_length
|
9
|
+
|
10
|
+
def initialize(packet,offset=0)
|
11
|
+
@packet = packet.force_encoding("ASCII-8BIT")
|
12
|
+
@offset = offset
|
13
|
+
header = packet.unpack("x#{offset}n10")
|
14
|
+
@version = header[0] >> 12
|
15
|
+
@header_length = ((header[0] >> 8) & 0x0f)*4
|
16
|
+
@packet_length = header[1]
|
17
|
+
@identification = header[2]
|
18
|
+
@frag_dont = (header[3] >> 14) & 0x01 != 0
|
19
|
+
@frag_more = (header[3] >> 13) & 0x01 != 0
|
20
|
+
@frag_offset = header[3] & 0x1fff
|
21
|
+
@ttl = header[4] >> 8
|
22
|
+
@protocol = header[4] & 0x00ff
|
23
|
+
@checksum = header[5]
|
24
|
+
@sndr_addr = ip_to_s(packet[12..15])
|
25
|
+
@dest_addr = ip_to_s(packet[16..19])
|
26
|
+
@data_length = @packet_length - @header_length
|
27
|
+
|
28
|
+
@virtual_header = packet[12..19] + [0,6,@data_length].pack("CCn")
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
def upper
|
33
|
+
upper_header = Protocol.to_class(@protocol)
|
34
|
+
offset = @offset+@header_length
|
35
|
+
upper_header.new(@packet,offset,@data_length,self)
|
36
|
+
end
|
37
|
+
|
38
|
+
def data
|
39
|
+
start = @offset+@header_length
|
40
|
+
@packet[start..start+@data_length]
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_virtual_header
|
44
|
+
@virtual_header
|
45
|
+
end
|
46
|
+
|
47
|
+
def ip_to_s(ip)
|
48
|
+
ip = ip.unpack("n2")
|
49
|
+
sprintf("%d.%d.%d.%d",ip[0]>>8,ip[0]&0x00ff,ip[1]>>8,ip[1]&0x00ff)
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
"IPv4 Header\n" <<
|
54
|
+
" Version : #{@version}\n" <<
|
55
|
+
" Header Length : #{@header_length}\n" <<
|
56
|
+
" Packet Length : #{@packet_length}\n" <<
|
57
|
+
" Identification : #{@identification}\n" <<
|
58
|
+
" Don't fragment : #{@frag_dont}\n" <<
|
59
|
+
" More fragments : #{@frag_more}\n" <<
|
60
|
+
" Fragment Offset : #{@frag_offset}\n" <<
|
61
|
+
" TTL : #{@ttl}\n" <<
|
62
|
+
" Protocol : #{Protocol.to_s(@protocol)}\n" <<
|
63
|
+
" Header Checksum : #{@checksum}\n" <<
|
64
|
+
" Sender Address : #{@sndr_addr}\n" <<
|
65
|
+
" Destination Address: #{@dest_addr}\n" <<
|
66
|
+
" (Data Length) : #{@data_length}"
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative 'ipv4header'
|
2
|
+
require_relative 'udpheader'
|
3
|
+
require_relative 'tcpheader'
|
4
|
+
|
5
|
+
class Protocol
|
6
|
+
ICMP = 0x01
|
7
|
+
IGMP = 0x02
|
8
|
+
TCP = 0x06
|
9
|
+
UDP = 0x11
|
10
|
+
IPv6 = 0x29
|
11
|
+
|
12
|
+
def self.to_class protocol
|
13
|
+
case protocol
|
14
|
+
when Protocol::ICMP
|
15
|
+
raise "ICMP is not supported"
|
16
|
+
when Protocol::IGMP
|
17
|
+
raise "IGMP is not supported"
|
18
|
+
when Protocol::TCP
|
19
|
+
TCPHeader
|
20
|
+
when Protocol::UDP
|
21
|
+
UDPHeader
|
22
|
+
when Protocol::IPv6
|
23
|
+
raise "IPv6 is not supported"
|
24
|
+
else
|
25
|
+
raise "Protocol:"+sprintf("0x%2X",protocol)+" is not supported"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.to_s protocol
|
30
|
+
case protocol
|
31
|
+
when Protocol::ICMP
|
32
|
+
"ICMP"
|
33
|
+
when Protocol::IGMP
|
34
|
+
"IGMP"
|
35
|
+
when Protocol::TCP
|
36
|
+
"TCP"
|
37
|
+
when Protocol::UDP
|
38
|
+
"UDP"
|
39
|
+
when Protocol::IPv6
|
40
|
+
"IPv6"
|
41
|
+
else
|
42
|
+
sprintf("0x%2X",protocol)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
@@ -0,0 +1,82 @@
|
|
1
|
+
class TCPHeader
|
2
|
+
|
3
|
+
attr_reader :sndr_port,:dest_port,:seq_num,:ack_num,:header_length,
|
4
|
+
:urg,:ack,:psh,:rst,:syn,:fin,:win_size,:checksum,:emgcy_ptr,
|
5
|
+
:packet_length,:data_length,:lower
|
6
|
+
|
7
|
+
def initialize(packet,offset=0,length=nil,lower=nil)
|
8
|
+
@packet = packet.force_encoding("ASCII-8BIT")
|
9
|
+
@offset = offset
|
10
|
+
@length = length || packet.bytesize-offset
|
11
|
+
header = packet.unpack("x#{offset}n2N2n4")
|
12
|
+
@sndr_port = header[0]
|
13
|
+
@dest_port = header[1]
|
14
|
+
@seq_num = header[2]
|
15
|
+
@ack_num = header[3]
|
16
|
+
@header_length = (header[4]>>12)*4
|
17
|
+
@urg = (header[4] & 0b100000) != 0
|
18
|
+
@ack = (header[4] & 0b010000) != 0
|
19
|
+
@psh = (header[4] & 0b001000) != 0
|
20
|
+
@rst = (header[4] & 0b000100) != 0
|
21
|
+
@syn = (header[4] & 0b000010) != 0
|
22
|
+
@fin = (header[4] & 0b000001) != 0
|
23
|
+
@win_size = header[5]
|
24
|
+
@checksum = header[6]
|
25
|
+
@emgcy_ptr = header[7]
|
26
|
+
|
27
|
+
@packet_length = @length
|
28
|
+
@data_length = @packet_length-@header_length
|
29
|
+
|
30
|
+
@lower = lower
|
31
|
+
|
32
|
+
# check checksum
|
33
|
+
calc_cs = false
|
34
|
+
if calc_cs
|
35
|
+
tmp = @packet[@offset..@offset+@length]
|
36
|
+
if (tmp.length % 2) != 0
|
37
|
+
tmp += "\0"
|
38
|
+
end
|
39
|
+
data = @lower.get_virtual_header + tmp
|
40
|
+
sum = 0
|
41
|
+
list = data.unpack("n*")
|
42
|
+
list.each do |d|
|
43
|
+
sum += d
|
44
|
+
end
|
45
|
+
sum = (sum & 0xffff) + (sum >> 16)
|
46
|
+
sum = (sum & 0xffff) + (sum >> 16)
|
47
|
+
raise if sum != 65535
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
def data
|
56
|
+
if(@data_length>0)
|
57
|
+
@packet[@offset+@header_length..@offset+@length]
|
58
|
+
else
|
59
|
+
""
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_s
|
64
|
+
"TCP Header\n" <<
|
65
|
+
" Sender Port : #{@sndr_port}\n" <<
|
66
|
+
" Destination Port: #{@dest_port}\n" <<
|
67
|
+
" Sequence Number : #{@seq_num}\n" <<
|
68
|
+
" ACK Number : #{@ack_num}\n" <<
|
69
|
+
" Header Length : #{@header_length}\n" <<
|
70
|
+
" URG : #{@urg}\n" <<
|
71
|
+
" ACK : #{@ack}\n" <<
|
72
|
+
" PSH : #{@psh}\n" <<
|
73
|
+
" RST : #{@rst}\n" <<
|
74
|
+
" SYN : #{@syn}\n" <<
|
75
|
+
" FIN : #{@fin}\n" <<
|
76
|
+
" Window Size : #{@win_size}\n" <<
|
77
|
+
" Checksum : #{@checksum}\n" <<
|
78
|
+
" Emergency Ptr : #{@emgcy_ptr}\n" <<
|
79
|
+
" (Packet Length) : #{@packet_length}\n" <<
|
80
|
+
" (Data Length) : #{@data_length}"
|
81
|
+
end
|
82
|
+
end
|