From 9cab0349aed66093e0c4dfa78e75d02d21ca2505 Mon Sep 17 00:00:00 2001 From: dbardbar Date: Sun, 8 Jan 2023 12:22:34 +0200 Subject: [PATCH 1/4] Add option to skip over unknown elements in nf9 and ipfix --- docs/config.md | 2 + ipfix/decoder.go | 75 ++++++++++++++++++++------------------ ipfix/decoder_test.go | 18 ++++----- ipfix/memcache_test.go | 2 +- netflow/v9/decoder.go | 43 +++++++++++++--------- netflow/v9/decoder_test.go | 2 +- vflow/ipfix.go | 2 +- vflow/netflow_v9.go | 2 +- vflow/options.go | 6 +++ 9 files changed, 86 insertions(+), 66 deletions(-) diff --git a/docs/config.md b/docs/config.md index 6d98c836..a2d71b0e 100644 --- a/docs/config.md +++ b/docs/config.md @@ -33,6 +33,7 @@ The vFlow configuration contains the following keys |ipfix-mirror-workers | 5 | IPFIX replicator concurrent packet generator | |ipfix-tpl-cache-file | /tmp/vflow.templates | IPFIX templates cache file | |ipfix-rpc-enabled | true | enable/disable IPFIX RPC | +|ipfix-skip-unknown | false | enable/disable skipping of unknown elements | |sflow-enabled | true | enable/disable sFlow decoders | |sflow-port | 6343 | server sFlow UDP port | |sflow-workers | 200 | sFlow concurrent decoders | @@ -50,6 +51,7 @@ The vFlow configuration contains the following keys |netflow9-topic | vflow.netflow9 | netflow v9 message queue topic name | |netflow9-udp-size | 1500 | maximum netflow v9 UDP packet size | |netflow9-tpl-cache-file | /tmp/netflow9.templates | netflow v9 templates cache file | +|netflow9-skip-unknown | false | enable/disable skipping of unknown elements | |dynamic-workers | true | enable/disable dynamic workers feature | |stats-enabled | true | enable/disable web stats listener | |stats-format | prometheus | set prometheus or restful format | diff --git a/ipfix/decoder.go b/ipfix/decoder.go index 15e16bcb..6fb7c79c 100644 --- a/ipfix/decoder.go +++ b/ipfix/decoder.go @@ -34,8 +34,9 @@ import ( // Decoder represents IPFIX payload and remote address type Decoder struct { - raddr net.IP - reader *reader.Reader + raddr net.IP + reader *reader.Reader + skipUnknownElements bool } // MessageHeader represents IPFIX message header @@ -97,8 +98,8 @@ type nonfatalError struct { var rpcChan = make(chan RPCRequest, 1) // NewDecoder constructs a decoder -func NewDecoder(raddr net.IP, b []byte) *Decoder { - return &Decoder{raddr, reader.NewReader(b)} +func NewDecoder(raddr net.IP, b []byte, skipUnknownElements bool) *Decoder { + return &Decoder{raddr, reader.NewReader(b), skipUnknownElements} } // Decode decodes the IPFIX raw data @@ -479,7 +480,7 @@ func (tr *TemplateRecord) unmarshalOpts(r *reader.Reader) error { return nil } -func (d *Decoder) getDataLength(fieldSpecifierLen uint16, t FieldType) (uint16, error) { +func (d *Decoder) getDataLength(fieldSpecifierLen uint16) (uint16, error) { var ( err error readLength uint16 @@ -487,7 +488,7 @@ func (d *Decoder) getDataLength(fieldSpecifierLen uint16, t FieldType) (uint16, r := d.reader - if (t == String || t == OctetArray) && (fieldSpecifierLen == 65535) { + if fieldSpecifierLen == 65535 { var len8 uint8 if len8, err = r.Uint8(); err != nil { return 0, err @@ -516,17 +517,35 @@ func (d *Decoder) decodeData(tr TemplateRecord) ([]DecodedField, error) { r := d.reader for i := 0; i < len(tr.ScopeFieldSpecifiers); i++ { + if readLength, err = d.getDataLength(tr.ScopeFieldSpecifiers[i].Length); err != nil { + return nil, err + } + + if b, err = r.Read(int(readLength)); err != nil { + return nil, err + } + m, ok := InfoModel[ElementKey{ tr.ScopeFieldSpecifiers[i].EnterpriseNo, tr.ScopeFieldSpecifiers[i].ElementID, }] - if !ok { - return nil, nonfatalError{fmt.Errorf("IPFIX element key (%d) not exist (scope)", - tr.ScopeFieldSpecifiers[i].ElementID)} + if ok { + fields = append(fields, DecodedField{ + ID: m.FieldID, + Value: Interpret(&b, m.Type), + EnterpriseNo: tr.ScopeFieldSpecifiers[i].EnterpriseNo, + }) + } else { + if !d.skipUnknownElements { + return nil, nonfatalError{fmt.Errorf("IPFIX element key (%d) not exist (scope)", + tr.ScopeFieldSpecifiers[i].ElementID)} + } } + } - if readLength, err = d.getDataLength(tr.ScopeFieldSpecifiers[i].Length, m.Type); err != nil { + for i := 0; i < len(tr.FieldSpecifiers); i++ { + if readLength, err = d.getDataLength(tr.FieldSpecifiers[i].Length); err != nil { return nil, err } @@ -534,37 +553,23 @@ func (d *Decoder) decodeData(tr TemplateRecord) ([]DecodedField, error) { return nil, err } - fields = append(fields, DecodedField{ - ID: m.FieldID, - Value: Interpret(&b, m.Type), - EnterpriseNo: tr.ScopeFieldSpecifiers[i].EnterpriseNo, - }) - } - - for i := 0; i < len(tr.FieldSpecifiers); i++ { m, ok := InfoModel[ElementKey{ tr.FieldSpecifiers[i].EnterpriseNo, tr.FieldSpecifiers[i].ElementID, }] - if !ok { - return nil, nonfatalError{fmt.Errorf("IPFIX element key (%d) not exist", - tr.FieldSpecifiers[i].ElementID)} - } - - if readLength, err = d.getDataLength(tr.FieldSpecifiers[i].Length, m.Type); err != nil { - return nil, err - } - - if b, err = r.Read(int(readLength)); err != nil { - return nil, err + if ok { + fields = append(fields, DecodedField{ + ID: m.FieldID, + Value: Interpret(&b, m.Type), + EnterpriseNo: tr.FieldSpecifiers[i].EnterpriseNo, + }) + } else { + if !d.skipUnknownElements { + return nil, nonfatalError{fmt.Errorf("IPFIX element key (%d) not exist", + tr.FieldSpecifiers[i].ElementID)} + } } - - fields = append(fields, DecodedField{ - ID: m.FieldID, - Value: Interpret(&b, m.Type), - EnterpriseNo: tr.FieldSpecifiers[i].EnterpriseNo, - }) } if len(fields) == 0 { diff --git a/ipfix/decoder_test.go b/ipfix/decoder_test.go index 4e56e1ca..8d63535d 100644 --- a/ipfix/decoder_test.go +++ b/ipfix/decoder_test.go @@ -134,7 +134,7 @@ func TestDecodeNoData(t *testing.T) { ip := net.ParseIP("127.0.0.1") mCache := GetCache("cache.file") body := []byte{} - d := NewDecoder(ip, body) + d := NewDecoder(ip, body, false) if _, err := d.Decode(mCache); err == nil { t.Error("expected err but nothing") } @@ -143,7 +143,7 @@ func TestDecodeNoData(t *testing.T) { func TestDecodeTemplate(t *testing.T) { ip := net.ParseIP("127.0.0.1") mCache := GetCache("cache.file") - d := NewDecoder(ip, tpl) + d := NewDecoder(ip, tpl, false) _, err := d.Decode(mCache) if err != nil { t.Error("unexpected error happened:", err) @@ -153,7 +153,7 @@ func TestDecodeTemplate(t *testing.T) { func TestDecodeOptsTemplate(t *testing.T) { ip := net.ParseIP("127.0.0.1") mCache := GetCache("cache.file") - d := NewDecoder(ip, optsTpl) + d := NewDecoder(ip, optsTpl, false) _, err := d.Decode(mCache) if err != nil { t.Error("unexpected error happened:", err) @@ -164,7 +164,7 @@ func BenchmarkDecodeTemplate(b *testing.B) { ip := net.ParseIP("127.0.0.1") mCache := GetCache("cache.file") for i := 0; i < b.N; i++ { - d := NewDecoder(ip, tpl) + d := NewDecoder(ip, tpl, false) d.Decode(mCache) } } @@ -173,7 +173,7 @@ func BenchmarkDecodeOptsTemplate(b *testing.B) { ip := net.ParseIP("127.0.0.1") mCache := GetCache("cache.file") for i := 0; i < b.N; i++ { - d := NewDecoder(ip, optsTpl) + d := NewDecoder(ip, optsTpl, false) d.Decode(mCache) } } @@ -181,7 +181,7 @@ func BenchmarkDecodeOptsTemplate(b *testing.B) { func TestMultiMessage(t *testing.T) { ip := net.ParseIP("127.0.0.1") mCache := GetCache("cache.file") - d := NewDecoder(ip, multiMessage) + d := NewDecoder(ip, multiMessage, false) r, err := d.Decode(mCache) if err != nil { t.Error("unexpected error happened:", err) @@ -199,7 +199,7 @@ func TestMultiMessage(t *testing.T) { func TestUnknownDatasetsMessage(t *testing.T) { ip := net.ParseIP("127.0.0.1") mCache := GetCache("cache.file") - d := NewDecoder(ip, unknownDatasetMessage) + d := NewDecoder(ip, unknownDatasetMessage, false) r, err := d.Decode(mCache) if l := len(r.DataSets); l != 0 { t.Error("Did not expect any result datasets, but got", l) @@ -218,13 +218,13 @@ func TestDecodeDataTpl(t *testing.T) { ip := net.ParseIP("127.0.0.1") templates := GetCache("") - d := NewDecoder(ip, tpl) + d := NewDecoder(ip, tpl, false) _, err := d.Decode(templates) if err != nil { t.Error(err) } - d = NewDecoder(ip, payload) + d = NewDecoder(ip, payload, false) _, err = d.Decode(templates) if err != nil { t.Error(err) diff --git a/ipfix/memcache_test.go b/ipfix/memcache_test.go index ac3327d9..40a4f1ee 100644 --- a/ipfix/memcache_test.go +++ b/ipfix/memcache_test.go @@ -31,7 +31,7 @@ import ( func TestMemCacheRetrieve(t *testing.T) { ip := net.ParseIP("127.0.0.1") mCache := GetCache("cache.file") - d := NewDecoder(ip, tpl) + d := NewDecoder(ip, tpl, false) d.Decode(mCache) v, ok := mCache.retrieve(256, ip) if !ok { diff --git a/netflow/v9/decoder.go b/netflow/v9/decoder.go index 4023d8f3..44b4228c 100644 --- a/netflow/v9/decoder.go +++ b/netflow/v9/decoder.go @@ -82,8 +82,9 @@ type DecodedField struct { // Decoder represents Netflow payload and remote address type Decoder struct { - raddr net.IP - reader *reader.Reader + raddr net.IP + reader *reader.Reader + skipUnknownElements bool } // Message represents Netflow decoded data @@ -335,15 +336,18 @@ func (d *Decoder) decodeData(tr TemplateRecord) ([]DecodedField, error) { tr.ScopeFieldSpecifiers[i].ElementID, }] - if !ok { - return nil, nonfatalError(fmt.Errorf("Netflow element key (%d) not exist (scope)", - tr.ScopeFieldSpecifiers[i].ElementID)) + if ok { + fields = append(fields, DecodedField{ + ID: m.FieldID, + Value: ipfix.Interpret(&b, m.Type), + }) + } else { + if !d.skipUnknownElements { + return nil, nonfatalError(fmt.Errorf("Netflow element key (%d) not exist (scope)", + tr.ScopeFieldSpecifiers[i].ElementID)) + } } - fields = append(fields, DecodedField{ - ID: m.FieldID, - Value: ipfix.Interpret(&b, m.Type), - }) } for i := 0; i < len(tr.FieldSpecifiers); i++ { @@ -357,23 +361,26 @@ func (d *Decoder) decodeData(tr TemplateRecord) ([]DecodedField, error) { tr.FieldSpecifiers[i].ElementID, }] - if !ok { - return nil, nonfatalError(fmt.Errorf("Netflow element key (%d) not exist", - tr.FieldSpecifiers[i].ElementID)) + if ok { + fields = append(fields, DecodedField{ + ID: m.FieldID, + Value: ipfix.Interpret(&b, m.Type), + }) + } else { + if !d.skipUnknownElements { + return nil, nonfatalError(fmt.Errorf("Netflow element key (%d) not exist", + tr.FieldSpecifiers[i].ElementID)) + } } - fields = append(fields, DecodedField{ - ID: m.FieldID, - Value: ipfix.Interpret(&b, m.Type), - }) } return fields, nil } // NewDecoder constructs a decoder -func NewDecoder(raddr net.IP, b []byte) *Decoder { - return &Decoder{raddr, reader.NewReader(b)} +func NewDecoder(raddr net.IP, b []byte, skipUnknownElements bool) *Decoder { + return &Decoder{raddr, reader.NewReader(b), skipUnknownElements} } // Decode decodes the flow records diff --git a/netflow/v9/decoder_test.go b/netflow/v9/decoder_test.go index 11c87def..ad4b496b 100644 --- a/netflow/v9/decoder_test.go +++ b/netflow/v9/decoder_test.go @@ -31,7 +31,7 @@ func TestDecodeNoData(t *testing.T) { ip := net.ParseIP("127.0.0.1") mCache := GetCache("cache.file") body := []byte{} - d := NewDecoder(ip, body) + d := NewDecoder(ip, body, false) if _, err := d.Decode(mCache); err == nil { t.Error("expected err but nothing") } diff --git a/vflow/ipfix.go b/vflow/ipfix.go index a5e49a2b..80648222 100644 --- a/vflow/ipfix.go +++ b/vflow/ipfix.go @@ -231,7 +231,7 @@ LOOP: } } - d := ipfix.NewDecoder(msg.raddr.IP, msg.body) + d := ipfix.NewDecoder(msg.raddr.IP, msg.body, opts.IPFIXSkipUnknown) if decodedMsg, err = d.Decode(mCache); err != nil { logger.Println(err) // in case ipfix message header couldn't decode diff --git a/vflow/netflow_v9.go b/vflow/netflow_v9.go index 41950395..0c46fa96 100644 --- a/vflow/netflow_v9.go +++ b/vflow/netflow_v9.go @@ -204,7 +204,7 @@ LOOP: msg.raddr, len(msg.body)) } - d := netflow9.NewDecoder(msg.raddr.IP, msg.body) + d := netflow9.NewDecoder(msg.raddr.IP, msg.body, opts.NetflowV9SkipUnknown) if decodedMsg, err = d.Decode(mCacheNF9); err != nil { logger.Println(err) if decodedMsg == nil { diff --git a/vflow/options.go b/vflow/options.go index 1c738e98..60b20edd 100644 --- a/vflow/options.go +++ b/vflow/options.go @@ -86,6 +86,7 @@ type Options struct { IPFIXMirrorPort int `yaml:"ipfix-mirror-port"` IPFIXMirrorWorkers int `yaml:"ipfix-mirror-workers"` IPFIXTplCacheFile string `yaml:"ipfix-tpl-cache-file"` + IPFIXSkipUnknown bool `yaml:"ipfix-skip-unknown"` // Netflow V5 NetflowV5Enabled bool `yaml:"netflow5-enabled"` @@ -103,6 +104,7 @@ type Options struct { NetflowV9Workers int `yaml:"netflow9-workers"` NetflowV9Topic string `yaml:"netflow9-topic"` NetflowV9TplCacheFile string `yaml:"netflow9-tpl-cache-file"` + NetflowV9SkipUnknown bool `yaml:"netflow9-skip-unknown"` // producer ProducerEnabled bool `yaml:"producer-enabled"` @@ -170,6 +172,7 @@ func NewOptions() *Options { IPFIXMirrorPort: 4172, IPFIXMirrorWorkers: 5, IPFIXTplCacheFile: "/tmp/vflow.templates", + IPFIXSkipUnknown: false, NetflowV5Enabled: true, NetflowV5Port: 9996, @@ -183,6 +186,7 @@ func NewOptions() *Options { NetflowV9Workers: 200, NetflowV9Topic: "vflow.netflow9", NetflowV9TplCacheFile: "/tmp/netflowv9.templates", + NetflowV9SkipUnknown: false, ProducerEnabled: true, MQName: "kafka", @@ -345,6 +349,7 @@ func (opts *Options) flagSet() { flag.StringVar(&opts.IPFIXMirrorAddr, "ipfix-mirror-addr", opts.IPFIXMirrorAddr, "IPFIX mirror destination address") flag.IntVar(&opts.IPFIXMirrorPort, "ipfix-mirror-port", opts.IPFIXMirrorPort, "IPFIX mirror destination port number") flag.IntVar(&opts.IPFIXMirrorWorkers, "ipfix-mirror-workers", opts.IPFIXMirrorWorkers, "IPFIX mirror workers number") + flag.BoolVar(&opts.IPFIXSkipUnknown, "ipfix-skip-unknown", opts.IPFIXSkipUnknown, "enabled/disable ignoring unknown fields") // netflow version 5 flag.BoolVar(&opts.NetflowV5Enabled, "netflow5-enabled", opts.NetflowV5Enabled, "enable/disable netflow version 5 listener") @@ -362,6 +367,7 @@ func (opts *Options) flagSet() { flag.IntVar(&opts.NetflowV9Workers, "netflow9-workers", opts.NetflowV9Workers, "Netflow version 9 workers number") flag.StringVar(&opts.NetflowV9Topic, "netflow9-topic", opts.NetflowV9Topic, "Netflow version 9 topic name") flag.StringVar(&opts.NetflowV9TplCacheFile, "netflow9-tpl-cache-file", opts.NetflowV9TplCacheFile, "Netflow version 9 template cache file") + flag.BoolVar(&opts.NetflowV9SkipUnknown, "netflow9-skip-unknown", opts.NetflowV9SkipUnknown, "enabled/disable ignoring unknown fields") // producer options flag.BoolVar(&opts.ProducerEnabled, "producer-enabled", opts.ProducerEnabled, "enable/disable producer message queue") From 9ca25092f10b0f0e056848d699689c4102b0e6ed Mon Sep 17 00:00:00 2001 From: dbardbar Date: Sun, 8 Jan 2023 14:29:48 +0200 Subject: [PATCH 2/4] Add unit tests for unknown element --- ipfix/decoder_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/ipfix/decoder_test.go b/ipfix/decoder_test.go index 8d63535d..dce39bb6 100644 --- a/ipfix/decoder_test.go +++ b/ipfix/decoder_test.go @@ -230,3 +230,43 @@ func TestDecodeDataTpl(t *testing.T) { t.Error(err) } } + +func TestUnknownElement(t *testing.T) { + // Single dataset, id 61166, 2 elements, element id 1, and element id 222, enterprise 7 + var template = []byte{ + 0x00, 0x0a, 0x00, 0x20, 0x63, 0x75, 0x58, 0xb1, 0x19, 0x10, 0x70, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x14, 0xee, 0xee, 0x00, 0x02, 0x00, 0x01, 0x00, 0x08, 0x80, 0xde, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x07} + + var payload = []byte{ + 0x00, 0x0a, 0x00, 0x20, 0x63, 0x75, 0x58, 0xb1, 0x19, 0x10, 0x70, 0x04, 0x00, 0x00, 0x00, 0x00, + 0xee, 0xee, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22} + + ip := net.ParseIP("127.0.0.1") + templates := GetCache("") + d := NewDecoder(ip, template, false) + _, err := d.Decode(templates) + if err != nil { + t.Error(err) + } + + // Parse data with unknown element + d = NewDecoder(ip, payload, false) + m, err := d.Decode(templates) + if err == nil { + t.Error("Expected error due to unknown element, but got nil") + } + if len(m.DataSets) != 0 { + t.Error("Did not expect any result datasets, but got", m.DataSets) + } + + // Now parse again, skip unknown element + d = NewDecoder(ip, payload, true) + m, err = d.Decode(templates) + if err != nil { + t.Error(err) + } + if len(m.DataSets) != 1 { + t.Error("Expected 1 dataset, but got", m.DataSets) + } +} From bf56e516c83c0564131f964aa9efae800af66c6e Mon Sep 17 00:00:00 2001 From: dbardbar Date: Sun, 8 Jan 2023 14:52:58 +0200 Subject: [PATCH 3/4] Add unit tests for unknown element --- netflow/v9/decoder_test.go | 58 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/netflow/v9/decoder_test.go b/netflow/v9/decoder_test.go index ad4b496b..5494fe87 100644 --- a/netflow/v9/decoder_test.go +++ b/netflow/v9/decoder_test.go @@ -36,3 +36,61 @@ func TestDecodeNoData(t *testing.T) { t.Error("expected err but nothing") } } + +func TestUnknownElement(t *testing.T) { + var template = []byte{ + 0x00, 0x09, 0x00, 0x01, // v9, set count = 1 + 0x00, 0x00, 0x00, 0x01, // uptime + 0x63, 0x99, 0xe9, 0x21, // timestamp + 0x00, 0x00, 0xff, 0x01, // sequence + 0x00, 0x00, 0x00, 0x01, // source id + 0x00, 0x00, // template set + 0x00, 0x10, // length + 0xee, 0xee, // template id + 0x00, 0x02, // field count + 0x00, 0x01, // element id 1 + 0x00, 0x08, // length 8 + 0xde, 0xad, // element id 57005 + 0x00, 0x04, // length 4 + } + + var payload = []byte{ + 0x00, 0x09, 0x00, 0x01, // v9, set count = 1 + 0x00, 0x00, 0x00, 0x02, // uptime + 0x63, 0x99, 0xe9, 0x22, // timestamp + 0x00, 0x00, 0xff, 0x02, // sequence + 0x00, 0x00, 0x00, 0x01, // source id + 0xee, 0xee, // template id + 0x00, 0x10, // length + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, // element #1 data + 0x00, 0x22, 0x22, 0x22, // element #2 data + } + + ip := net.ParseIP("127.0.0.1") + mCache := GetCache("cache.file") + d := NewDecoder(ip, template, false) + _, err := d.Decode(mCache) + if err != nil { + t.Error(err) + } + + // Parse data with unknown element + d = NewDecoder(ip, payload, false) + m, err := d.Decode(mCache) + if err == nil { + t.Error("Expected error due to unknown element, but got nil") + } + if len(m.DataSets) != 0 { + t.Error("Did not expect any result datasets, but got", m.DataSets) + } + + // Now parse again, skip unknown element + d = NewDecoder(ip, payload, true) + m, err = d.Decode(mCache) + if err != nil { + t.Error(err) + } + if len(m.DataSets) != 1 { + t.Error("Expected 1 dataset, but got", m.DataSets) + } +} From 9cdc05fb76e2683bdbb2c596fb183540ef558ab8 Mon Sep 17 00:00:00 2001 From: dbardbar Date: Sun, 8 Jan 2023 14:55:25 +0200 Subject: [PATCH 4/4] Add unit tests for unknown element --- vflow/options.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/vflow/options.go b/vflow/options.go index 60b20edd..02c9e60b 100644 --- a/vflow/options.go +++ b/vflow/options.go @@ -34,8 +34,6 @@ import ( "runtime" "strconv" "strings" - - "gopkg.in/yaml.v2" ) var ( @@ -349,7 +347,7 @@ func (opts *Options) flagSet() { flag.StringVar(&opts.IPFIXMirrorAddr, "ipfix-mirror-addr", opts.IPFIXMirrorAddr, "IPFIX mirror destination address") flag.IntVar(&opts.IPFIXMirrorPort, "ipfix-mirror-port", opts.IPFIXMirrorPort, "IPFIX mirror destination port number") flag.IntVar(&opts.IPFIXMirrorWorkers, "ipfix-mirror-workers", opts.IPFIXMirrorWorkers, "IPFIX mirror workers number") - flag.BoolVar(&opts.IPFIXSkipUnknown, "ipfix-skip-unknown", opts.IPFIXSkipUnknown, "enabled/disable ignoring unknown fields") + flag.BoolVar(&opts.IPFIXSkipUnknown, "ipfix-skip-unknown", opts.IPFIXSkipUnknown, "enabled/disable ignore of unknown fields") // netflow version 5 flag.BoolVar(&opts.NetflowV5Enabled, "netflow5-enabled", opts.NetflowV5Enabled, "enable/disable netflow version 5 listener") @@ -367,7 +365,7 @@ func (opts *Options) flagSet() { flag.IntVar(&opts.NetflowV9Workers, "netflow9-workers", opts.NetflowV9Workers, "Netflow version 9 workers number") flag.StringVar(&opts.NetflowV9Topic, "netflow9-topic", opts.NetflowV9Topic, "Netflow version 9 topic name") flag.StringVar(&opts.NetflowV9TplCacheFile, "netflow9-tpl-cache-file", opts.NetflowV9TplCacheFile, "Netflow version 9 template cache file") - flag.BoolVar(&opts.NetflowV9SkipUnknown, "netflow9-skip-unknown", opts.NetflowV9SkipUnknown, "enabled/disable ignoring unknown fields") + flag.BoolVar(&opts.NetflowV9SkipUnknown, "netflow9-skip-unknown", opts.NetflowV9SkipUnknown, "enabled/disable ignore of unknown fields") // producer options flag.BoolVar(&opts.ProducerEnabled, "producer-enabled", opts.ProducerEnabled, "enable/disable producer message queue")