fluent-plugin-sflow 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|