logstash-codec-netflow 2.1.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -19
- data/CONTRIBUTORS +0 -12
- data/Gemfile +2 -0
- data/lib/logstash/codecs/netflow.rb +27 -277
- data/lib/logstash/codecs/netflow/util.rb +6 -60
- data/logstash-codec-netflow.gemspec +3 -4
- data/spec/codecs/netflow_spec.rb +20 -265
- metadata +27 -29
- data/lib/logstash/codecs/netflow/iana2yaml.rb +0 -77
- data/lib/logstash/codecs/netflow/ipfix.yaml +0 -2333
- data/spec/codecs/ipfix.dat +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 95e7e7d16a47ec1ae4535979d9792b6051e6d36f
|
4
|
+
data.tar.gz: b101a6c29ebc0fa9d65ade94fb14160cd7a1df33
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 776e788d36ccdb7c30844b7f4b7925eb7c4fc5a5c40371b3330376bb00b659a4338a312160634813dfe4d6f14d98fbf4f9d83c9da3533ec954c8460b749a7e04
|
7
|
+
data.tar.gz: 135bdba032f43b7855c4bf63fcf3468b40eb8faaefbb70ef9ada0ff63e2561cc230dcc87671d7f92afac0d702b1b406cccf46192ac4f12a15a351c46bb897f80
|
data/CHANGELOG.md
CHANGED
@@ -1,30 +1,13 @@
|
|
1
|
-
##
|
2
|
-
|
3
|
-
- Small update due to breaking change in BinData gem (issue #41)
|
4
|
-
|
5
|
-
## 2.1.0
|
6
|
-
|
7
|
-
- Added IPFIX support
|
8
|
-
- Fixed exception if Netflow data contains MAC addresses (issue #26, issue #34)
|
9
|
-
- Fixed exceptions when receiving invalid Netflow v5 and v9 data (issue #17, issue 18)
|
10
|
-
- Fixed decoding Netflow templates from multiple (non-identical) exporters
|
11
|
-
- Add support for Cisco ASA fields
|
12
|
-
- Add support for Netflow 9 options template with scope fields
|
13
|
-
|
1
|
+
## 3.0.0
|
2
|
+
- Update the plugin to the version 2.0 of the plugin api, this change is required for Logstash 5.0 compatibility. See https://github.com/elastic/logstash/issues/5141
|
14
3
|
# 2.0.5
|
15
|
-
|
16
4
|
- Depend on logstash-core-plugin-api instead of logstash-core, removing the need to mass update plugins on major releases of logstash
|
17
|
-
|
18
5
|
# 2.0.4
|
19
|
-
|
20
6
|
- New dependency requirements for logstash-core for the 5.0 release
|
21
|
-
|
22
7
|
## 2.0.3
|
23
|
-
|
24
8
|
- Fixed JSON compare flaw in specs
|
25
9
|
|
26
10
|
## 2.0.0
|
27
|
-
|
28
11
|
- Plugins were updated to follow the new shutdown semantic, this mainly allows Logstash to instruct input plugins to terminate gracefully,
|
29
12
|
instead of using Thread.raise on the plugins' threads. Ref: https://github.com/elastic/logstash/pull/3895
|
30
13
|
- Dependency on logstash-core update to 2.0
|
data/CONTRIBUTORS
CHANGED
@@ -3,24 +3,12 @@ reports, or in general have helped logstash along its way.
|
|
3
3
|
|
4
4
|
Contributors:
|
5
5
|
* Aaron Mildenstein (untergeek)
|
6
|
-
* Adam Kaminski (thimslugga)
|
7
6
|
* Colin Surprenant (colinsurprenant)
|
8
7
|
* Jordan Sissel (jordansissel)
|
9
|
-
* Jorrit Folmer (jorritfolmer)
|
10
8
|
* Matt Dainty (bodgit)
|
11
|
-
* Paul Warren (pwarren)
|
12
9
|
* Pier-Hugues Pellerin (ph)
|
13
|
-
* Pulkit Agrawal (propulkit)
|
14
10
|
* Richard Pijnenburg (electrical)
|
15
|
-
* Salvador Ferrer (salva-ferrer)
|
16
|
-
* Will Rigby (wrigby)
|
17
|
-
* Rojuinex
|
18
11
|
* debadair
|
19
|
-
* hkshirish
|
20
|
-
* jstopinsek
|
21
|
-
|
22
|
-
Maintainer:
|
23
|
-
* Jorrit Folmer (jorritfolmer)
|
24
12
|
|
25
13
|
Note: If you've sent us patches, bug reports, or otherwise contributed to
|
26
14
|
Logstash, and you aren't on the list above and want to be, please let us know
|
data/Gemfile
CHANGED
@@ -3,59 +3,7 @@ require "logstash/filters/base"
|
|
3
3
|
require "logstash/namespace"
|
4
4
|
require "logstash/timestamp"
|
5
5
|
|
6
|
-
# The "netflow" codec is
|
7
|
-
#
|
8
|
-
# ==== Supported Netflow/IPFIX exporters
|
9
|
-
#
|
10
|
-
# The following Netflow/IPFIX exporters are known to work with the most recent version of the netflow codec:
|
11
|
-
#
|
12
|
-
# [cols="6,^2,^2,^2,12",options="header"]
|
13
|
-
# |===========================================================================================
|
14
|
-
# |Netflow exporter | v5 | v9 | IPFIX | Remarks
|
15
|
-
# |Softflowd | y | y | y | IPFIX supported in https://github.com/djmdjm/softflowd
|
16
|
-
# |nProbe | y | y | y |
|
17
|
-
# |ipt_NETFLOW | y | y | y |
|
18
|
-
# |Cisco ASA | | y | |
|
19
|
-
# |Cisco IOS 12.x | | y | |
|
20
|
-
# |fprobe | y | | |
|
21
|
-
# |===========================================================================================
|
22
|
-
#
|
23
|
-
# ==== Usage
|
24
|
-
#
|
25
|
-
# Example Logstash configuration:
|
26
|
-
#
|
27
|
-
# [source]
|
28
|
-
# -----------------------------
|
29
|
-
# input {
|
30
|
-
# udp {
|
31
|
-
# host => localhost
|
32
|
-
# port => 2055
|
33
|
-
# codec => netflow {
|
34
|
-
# versions => [5, 9]
|
35
|
-
# }
|
36
|
-
# type => netflow
|
37
|
-
# }
|
38
|
-
# udp {
|
39
|
-
# host => localhost
|
40
|
-
# port => 4739
|
41
|
-
# codec => netflow {
|
42
|
-
# versions => [10]
|
43
|
-
# target => ipfix
|
44
|
-
# }
|
45
|
-
# type => ipfix
|
46
|
-
# }
|
47
|
-
# tcp {
|
48
|
-
# host => localhost
|
49
|
-
# port => 4739
|
50
|
-
# codec => netflow {
|
51
|
-
# versions => [10]
|
52
|
-
# target => ipfix
|
53
|
-
# }
|
54
|
-
# type => ipfix
|
55
|
-
# }
|
56
|
-
# }
|
57
|
-
# -----------------------------
|
58
|
-
|
6
|
+
# The "netflow" codec is for decoding Netflow v5/v9 flows.
|
59
7
|
class LogStash::Codecs::Netflow < LogStash::Codecs::Base
|
60
8
|
config_name "netflow"
|
61
9
|
|
@@ -66,7 +14,7 @@ class LogStash::Codecs::Netflow < LogStash::Codecs::Base
|
|
66
14
|
config :target, :validate => :string, :default => "netflow"
|
67
15
|
|
68
16
|
# Specify which Netflow versions you will accept.
|
69
|
-
config :versions, :validate => :array, :default => [5, 9
|
17
|
+
config :versions, :validate => :array, :default => [5, 9]
|
70
18
|
|
71
19
|
# Override YAML file containing Netflow field definitions
|
72
20
|
#
|
@@ -83,25 +31,7 @@ class LogStash::Codecs::Netflow < LogStash::Codecs::Base
|
|
83
31
|
# - :skip
|
84
32
|
#
|
85
33
|
# See <https://github.com/logstash-plugins/logstash-codec-netflow/blob/master/lib/logstash/codecs/netflow/netflow.yaml> for the base set.
|
86
|
-
config :
|
87
|
-
|
88
|
-
# Override YAML file containing IPFIX field definitions
|
89
|
-
#
|
90
|
-
# Very similar to the Netflow version except there is a top level Private
|
91
|
-
# Enterprise Number (PEN) key added:
|
92
|
-
#
|
93
|
-
# ---
|
94
|
-
# pen:
|
95
|
-
# id:
|
96
|
-
# - :uintN or :ip4_addr or :ip6_addr or :mac_addr or :string
|
97
|
-
# - :name
|
98
|
-
# id:
|
99
|
-
# - :skip
|
100
|
-
#
|
101
|
-
# There is an implicit PEN 0 for the standard fields.
|
102
|
-
#
|
103
|
-
# See <https://github.com/logstash-plugins/logstash-codec-netflow/blob/master/lib/logstash/codecs/netflow/ipfix.yaml> for the base set.
|
104
|
-
config :ipfix_definitions, :validate => :path
|
34
|
+
config :definitions, :validate => :path
|
105
35
|
|
106
36
|
NETFLOW5_FIELDS = ['version', 'flow_seq_num', 'engine_type', 'engine_id', 'sampling_algorithm', 'sampling_interval', 'flow_records']
|
107
37
|
NETFLOW9_FIELDS = ['version', 'flow_seq_num']
|
@@ -112,7 +42,6 @@ class LogStash::Codecs::Netflow < LogStash::Codecs::Base
|
|
112
42
|
4 => :scope_netflow_cache,
|
113
43
|
5 => :scope_template,
|
114
44
|
}
|
115
|
-
IPFIX_FIELDS = ['version']
|
116
45
|
SWITCHED = /_switched$/
|
117
46
|
FLOWSET_ID = "flowset_id"
|
118
47
|
|
@@ -123,16 +52,26 @@ class LogStash::Codecs::Netflow < LogStash::Codecs::Base
|
|
123
52
|
|
124
53
|
def register
|
125
54
|
require "logstash/codecs/netflow/util"
|
126
|
-
@
|
127
|
-
@ipfix_templates = Vash.new()
|
55
|
+
@templates = Vash.new()
|
128
56
|
|
129
57
|
# Path to default Netflow v9 field definitions
|
130
58
|
filename = ::File.expand_path('netflow/netflow.yaml', ::File.dirname(__FILE__))
|
131
|
-
@netflow_fields = load_definitions(filename, @netflow_definitions)
|
132
59
|
|
133
|
-
|
134
|
-
|
135
|
-
|
60
|
+
begin
|
61
|
+
@fields = YAML.load_file(filename)
|
62
|
+
rescue Exception => e
|
63
|
+
raise "#{self.class.name}: Bad syntax in definitions file #{filename}"
|
64
|
+
end
|
65
|
+
|
66
|
+
# Allow the user to augment/override/rename the supported Netflow fields
|
67
|
+
if @definitions
|
68
|
+
raise "#{self.class.name}: definitions file #{@definitions} does not exists" unless File.exists?(@definitions)
|
69
|
+
begin
|
70
|
+
@fields.merge!(YAML.load_file(@definitions))
|
71
|
+
rescue Exception => e
|
72
|
+
raise "#{self.class.name}: Bad syntax in definitions file #{@definitions}"
|
73
|
+
end
|
74
|
+
end
|
136
75
|
end # def register
|
137
76
|
|
138
77
|
def decode(payload, metadata = nil, &block)
|
@@ -157,11 +96,6 @@ class LogStash::Codecs::Netflow < LogStash::Codecs::Base
|
|
157
96
|
decode_netflow9(flowset, record).each{|event| yield(event)}
|
158
97
|
end
|
159
98
|
end
|
160
|
-
elsif header.version == 10
|
161
|
-
flowset = IpfixPDU.read(payload)
|
162
|
-
flowset.records.each do |record|
|
163
|
-
decode_ipfix(flowset, record).each { |event| yield(event) }
|
164
|
-
end
|
165
99
|
else
|
166
100
|
@logger.warn("Unsupported Netflow version v#{header.version}")
|
167
101
|
end
|
@@ -217,7 +151,7 @@ class LogStash::Codecs::Netflow < LogStash::Codecs::Base
|
|
217
151
|
record.flowset_data.templates.each do |template|
|
218
152
|
catch (:field) do
|
219
153
|
fields = []
|
220
|
-
template.
|
154
|
+
template.fields.each do |field|
|
221
155
|
entry = netflow_field_for(field.field_type, field.field_length)
|
222
156
|
throw :field unless entry
|
223
157
|
fields += entry
|
@@ -229,9 +163,9 @@ class LogStash::Codecs::Netflow < LogStash::Codecs::Base
|
|
229
163
|
else
|
230
164
|
key = "#{flowset.source_id}|#{template.template_id}"
|
231
165
|
end
|
232
|
-
@
|
166
|
+
@templates[key, @cache_ttl] = BinData::Struct.new(:endian => :big, :fields => fields)
|
233
167
|
# Purge any expired templates
|
234
|
-
@
|
168
|
+
@templates.cleanup!
|
235
169
|
end
|
236
170
|
end
|
237
171
|
when 1
|
@@ -254,9 +188,9 @@ class LogStash::Codecs::Netflow < LogStash::Codecs::Base
|
|
254
188
|
else
|
255
189
|
key = "#{flowset.source_id}|#{template.template_id}"
|
256
190
|
end
|
257
|
-
@
|
191
|
+
@templates[key, @cache_ttl] = BinData::Struct.new(:endian => :big, :fields => fields)
|
258
192
|
# Purge any expired templates
|
259
|
-
@
|
193
|
+
@templates.cleanup!
|
260
194
|
end
|
261
195
|
end
|
262
196
|
when 256..65535
|
@@ -267,7 +201,7 @@ class LogStash::Codecs::Netflow < LogStash::Codecs::Base
|
|
267
201
|
else
|
268
202
|
key = "#{flowset.source_id}|#{record.flowset_id}"
|
269
203
|
end
|
270
|
-
template = @
|
204
|
+
template = @templates[key]
|
271
205
|
|
272
206
|
unless template
|
273
207
|
#@logger.warn("No matching template for flow id #{record.flowset_id} from #{event["source"]}")
|
@@ -324,164 +258,14 @@ class LogStash::Codecs::Netflow < LogStash::Codecs::Base
|
|
324
258
|
@logger.warn("Invalid netflow packet received (#{e})")
|
325
259
|
end
|
326
260
|
|
327
|
-
def decode_ipfix(flowset, record)
|
328
|
-
events = []
|
329
|
-
|
330
|
-
case record.flowset_id
|
331
|
-
when 2
|
332
|
-
# Template flowset
|
333
|
-
record.flowset_data.templates.each do |template|
|
334
|
-
catch (:field) do
|
335
|
-
fields = []
|
336
|
-
template.record_fields.each do |field|
|
337
|
-
field_type = field.field_type
|
338
|
-
field_length = field.field_length
|
339
|
-
enterprise_id = field.enterprise ? field.enterprise_id : 0
|
340
|
-
|
341
|
-
if field.field_length == 0xffff
|
342
|
-
# FIXME
|
343
|
-
@logger.warn("Cowardly refusing to deal with variable length encoded field", :type => field_type, :enterprise => enterprise_id)
|
344
|
-
throw :field
|
345
|
-
end
|
346
|
-
|
347
|
-
if enterprise_id == 0
|
348
|
-
case field_type
|
349
|
-
when 291, 292, 293
|
350
|
-
# FIXME
|
351
|
-
@logger.warn("Cowardly refusing to deal with complex data types", :type => field_type, :enterprise => enterprise_id)
|
352
|
-
throw :field
|
353
|
-
end
|
354
|
-
end
|
355
|
-
|
356
|
-
entry = ipfix_field_for(field_type, enterprise_id, field.field_length)
|
357
|
-
throw :field unless entry
|
358
|
-
fields += entry
|
359
|
-
end
|
360
|
-
# FIXME Source IP address required in key
|
361
|
-
key = "#{flowset.observation_domain_id}|#{template.template_id}"
|
362
|
-
@ipfix_templates[key, @cache_ttl] = BinData::Struct.new(:endian => :big, :fields => fields)
|
363
|
-
# Purge any expired templates
|
364
|
-
@ipfix_templates.cleanup!
|
365
|
-
end
|
366
|
-
end
|
367
|
-
when 3
|
368
|
-
# Options template flowset
|
369
|
-
record.flowset_data.templates.each do |template|
|
370
|
-
catch (:field) do
|
371
|
-
fields = []
|
372
|
-
(template.scope_fields.to_ary + template.option_fields.to_ary).each do |field|
|
373
|
-
field_type = field.field_type
|
374
|
-
field_length = field.field_length
|
375
|
-
enterprise_id = field.enterprise ? field.enterprise_id : 0
|
376
|
-
|
377
|
-
if field.field_length == 0xffff
|
378
|
-
# FIXME
|
379
|
-
@logger.warn("Cowardly refusing to deal with variable length encoded field", :type => field_type, :enterprise => enterprise_id)
|
380
|
-
throw :field
|
381
|
-
end
|
382
|
-
|
383
|
-
if enterprise_id == 0
|
384
|
-
case field_type
|
385
|
-
when 291, 292, 293
|
386
|
-
# FIXME
|
387
|
-
@logger.warn("Cowardly refusing to deal with complex data types", :type => field_type, :enterprise => enterprise_id)
|
388
|
-
throw :field
|
389
|
-
end
|
390
|
-
end
|
391
|
-
|
392
|
-
entry = ipfix_field_for(field_type, enterprise_id, field.field_length)
|
393
|
-
throw :field unless entry
|
394
|
-
fields += entry
|
395
|
-
end
|
396
|
-
# FIXME Source IP address required in key
|
397
|
-
key = "#{flowset.observation_domain_id}|#{template.template_id}"
|
398
|
-
@ipfix_templates[key, @cache_ttl] = BinData::Struct.new(:endian => :big, :fields => fields)
|
399
|
-
# Purge any expired templates
|
400
|
-
@ipfix_templates.cleanup!
|
401
|
-
end
|
402
|
-
end
|
403
|
-
when 256..65535
|
404
|
-
# Data flowset
|
405
|
-
key = "#{flowset.observation_domain_id}|#{record.flowset_id}"
|
406
|
-
template = @ipfix_templates[key]
|
407
|
-
|
408
|
-
unless template
|
409
|
-
@logger.warn("No matching template for flow id #{record.flowset_id}")
|
410
|
-
next
|
411
|
-
end
|
412
|
-
|
413
|
-
array = BinData::Array.new(:type => template, :read_until => :eof)
|
414
|
-
records = array.read(record.flowset_data)
|
415
|
-
|
416
|
-
records.each do |r|
|
417
|
-
event = {
|
418
|
-
LogStash::Event::TIMESTAMP => LogStash::Timestamp.at(flowset.unix_sec),
|
419
|
-
@target => {}
|
420
|
-
}
|
421
|
-
|
422
|
-
IPFIX_FIELDS.each do |f|
|
423
|
-
event[@target][f] = flowset[f].snapshot
|
424
|
-
end
|
425
|
-
|
426
|
-
r.each_pair do |k, v|
|
427
|
-
case k.to_s
|
428
|
-
when /^flow(?:Start|End)Seconds$/
|
429
|
-
event[@target][k.to_s] = LogStash::Timestamp.at(v.snapshot).to_iso8601
|
430
|
-
when /^flow(?:Start|End)(Milli|Micro|Nano)seconds$/
|
431
|
-
divisor =
|
432
|
-
case $1
|
433
|
-
when 'Milli'
|
434
|
-
1_000
|
435
|
-
when 'Micro'
|
436
|
-
1_000_000
|
437
|
-
when 'Nano'
|
438
|
-
1_000_000_000
|
439
|
-
end
|
440
|
-
event[@target][k.to_s] = LogStash::Timestamp.at(v.snapshot.to_f / divisor).to_iso8601
|
441
|
-
else
|
442
|
-
event[@target][k.to_s] = v.snapshot
|
443
|
-
end
|
444
|
-
end
|
445
|
-
|
446
|
-
events << LogStash::Event.new(event)
|
447
|
-
end
|
448
|
-
else
|
449
|
-
@logger.warn("Unsupported flowset id #{record.flowset_id}")
|
450
|
-
end
|
451
|
-
|
452
|
-
events
|
453
|
-
rescue BinData::ValidityError => e
|
454
|
-
@logger.warn("Invalid IPFIX packet received (#{e})")
|
455
|
-
end
|
456
|
-
|
457
|
-
def load_definitions(defaults, extra)
|
458
|
-
begin
|
459
|
-
fields = YAML.load_file(defaults)
|
460
|
-
rescue Exception => e
|
461
|
-
raise "#{self.class.name}: Bad syntax in definitions file #{defaults}"
|
462
|
-
end
|
463
|
-
|
464
|
-
# Allow the user to augment/override/rename the default fields
|
465
|
-
if extra
|
466
|
-
raise "#{self.class.name}: definitions file #{extra} does not exist" unless File.exists?(extra)
|
467
|
-
begin
|
468
|
-
fields.merge!(YAML.load_file(extra))
|
469
|
-
rescue Exception => e
|
470
|
-
raise "#{self.class.name}: Bad syntax in definitions file #{extra}"
|
471
|
-
end
|
472
|
-
end
|
473
|
-
|
474
|
-
fields
|
475
|
-
end
|
476
|
-
|
477
261
|
def uint_field(length, default)
|
478
262
|
# If length is 4, return :uint32, etc. and use default if length is 0
|
479
263
|
("uint" + (((length > 0) ? length : default) * 8).to_s).to_sym
|
480
264
|
end # def uint_field
|
481
265
|
|
482
266
|
def netflow_field_for(type, length)
|
483
|
-
if @
|
484
|
-
field = @
|
267
|
+
if @fields.include?(type)
|
268
|
+
field = @fields[type].clone
|
485
269
|
if field.is_a?(Array)
|
486
270
|
|
487
271
|
field[0] = uint_field(length, field[0]) if field[0].is_a?(Integer)
|
@@ -507,38 +291,4 @@ class LogStash::Codecs::Netflow < LogStash::Codecs::Base
|
|
507
291
|
nil
|
508
292
|
end
|
509
293
|
end # def netflow_field_for
|
510
|
-
|
511
|
-
def ipfix_field_for(type, enterprise, length)
|
512
|
-
if @ipfix_fields.include?(enterprise)
|
513
|
-
if @ipfix_fields[enterprise].include?(type)
|
514
|
-
field = @ipfix_fields[enterprise][type].clone
|
515
|
-
else
|
516
|
-
@logger.warn("Unsupported enterprise field", :type => type, :enterprise => enterprise, :length => length)
|
517
|
-
end
|
518
|
-
else
|
519
|
-
@logger.warn("Unsupported enterprise", :enterprise => enterprise)
|
520
|
-
end
|
521
|
-
|
522
|
-
return nil unless field
|
523
|
-
|
524
|
-
if field.is_a?(Array)
|
525
|
-
case field[0]
|
526
|
-
when :skip
|
527
|
-
field += [nil, {:length => length}]
|
528
|
-
when :string
|
529
|
-
field += [{:length => length, :trim_padding => true}]
|
530
|
-
when :uint64
|
531
|
-
field[0] = uint_field(length, 8)
|
532
|
-
when :uint32
|
533
|
-
field[0] = uint_field(length, 4)
|
534
|
-
when :uint16
|
535
|
-
field[0] = uint_field(length, 2)
|
536
|
-
end
|
537
|
-
|
538
|
-
@logger.debug("Definition complete", :field => field)
|
539
|
-
[field]
|
540
|
-
else
|
541
|
-
@logger.warn("Definition should be an array", :field => field)
|
542
|
-
end
|
543
|
-
end
|
544
294
|
end # class LogStash::Filters::Netflow
|