// +build linux package iptables import ( "errors" "reflect" "testing" "github.com/influxdata/telegraf/testutil" ) func TestIptables_Gather(t *testing.T) { tests := []struct { table string chains []string values []string tags []map[string]string fields [][]map[string]interface{} err error }{ { // 1 - no configured table => no results values: []string{ `Chain INPUT (policy ACCEPT 58 packets, 5096 bytes) pkts bytes target prot opt in out source destination 57 4520 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 `}, }, { // 2 - no configured chains => no results table: "filter", values: []string{ `Chain INPUT (policy ACCEPT 58 packets, 5096 bytes) pkts bytes target prot opt in out source destination 57 4520 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 `}, }, { // 3 - pkts and bytes are gathered as integers table: "filter", chains: []string{"INPUT"}, values: []string{ `Chain INPUT (policy ACCEPT 58 packets, 5096 bytes) pkts bytes target prot opt in out source destination 57 4520 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* foobar */ `}, tags: []map[string]string{{"table": "filter", "chain": "INPUT", "target": "RETURN", "ruleid": "foobar"}}, fields: [][]map[string]interface{}{ {map[string]interface{}{"pkts": uint64(57), "bytes": uint64(4520)}}, }, }, { // 4 - missing fields header => no results table: "filter", chains: []string{"INPUT"}, values: []string{`Chain INPUT (policy ACCEPT 58 packets, 5096 bytes)`}, }, { // 5 - invalid chain header => error table: "filter", chains: []string{"INPUT"}, values: []string{ `INPUT (policy ACCEPT 58 packets, 5096 bytes) pkts bytes target prot opt in out source destination 57 4520 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 `}, err: errParse, }, { // 6 - invalid fields header => error table: "filter", chains: []string{"INPUT"}, values: []string{ `Chain INPUT (policy ACCEPT 58 packets, 5096 bytes) 57 4520 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 `}, err: errParse, }, { // 7 - invalid integer value => best effort, no error table: "filter", chains: []string{"INPUT"}, values: []string{ `Chain INPUT (policy ACCEPT 58 packets, 5096 bytes) pkts bytes target prot opt in out source destination K 4520 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 `}, }, { // 8 - Multiple rows, multiple chains => no error table: "filter", chains: []string{"INPUT", "FORWARD"}, values: []string{ `Chain INPUT (policy ACCEPT 58 packets, 5096 bytes) pkts bytes target prot opt in out source destination 100 4520 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 200 4520 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* foo */ `, `Chain FORWARD (policy ACCEPT 58 packets, 5096 bytes) pkts bytes target prot opt in out source destination 300 4520 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* bar */ 400 4520 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 500 4520 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* foobar */ `, }, tags: []map[string]string{ {"table": "filter", "chain": "INPUT", "target": "RETURN", "ruleid": "foo"}, {"table": "filter", "chain": "FORWARD", "target": "RETURN", "ruleid": "bar"}, {"table": "filter", "chain": "FORWARD", "target": "RETURN", "ruleid": "foobar"}, }, fields: [][]map[string]interface{}{ {map[string]interface{}{"pkts": uint64(200), "bytes": uint64(4520)}}, {map[string]interface{}{"pkts": uint64(300), "bytes": uint64(4520)}}, {map[string]interface{}{"pkts": uint64(500), "bytes": uint64(4520)}}, }, }, { // 9 - comments are used as ruleid if any table: "filter", chains: []string{"INPUT"}, values: []string{ `Chain INPUT (policy ACCEPT 58 packets, 5096 bytes) pkts bytes target prot opt in out source destination 57 4520 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 /* foobar */ 100 4520 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 `}, tags: []map[string]string{ {"table": "filter", "chain": "INPUT", "target": "RETURN", "ruleid": "foobar"}, }, fields: [][]map[string]interface{}{ {map[string]interface{}{"pkts": uint64(57), "bytes": uint64(4520)}}, }, }, { // 10 - allow trailing text table: "mangle", chains: []string{"SHAPER"}, values: []string{ `Chain SHAPER (policy ACCEPT 58 packets, 5096 bytes) pkts bytes target prot opt in out source destination 0 0 ACCEPT all -- * * 1.3.5.7 0.0.0.0/0 /* test */ 0 0 CLASSIFY all -- * * 1.3.5.7 0.0.0.0/0 /* test2 */ CLASSIFY set 1:4 `}, tags: []map[string]string{ {"table": "mangle", "chain": "SHAPER", "target": "ACCEPT", "ruleid": "test"}, {"table": "mangle", "chain": "SHAPER", "target": "CLASSIFY", "ruleid": "test2"}, }, fields: [][]map[string]interface{}{ {map[string]interface{}{"pkts": uint64(0), "bytes": uint64(0)}}, {map[string]interface{}{"pkts": uint64(0), "bytes": uint64(0)}}, }, }, { // 11 - invalid pkts/bytes table: "mangle", chains: []string{"SHAPER"}, values: []string{ `Chain SHAPER (policy ACCEPT 58 packets, 5096 bytes) pkts bytes target prot opt in out source destination a a ACCEPT all -- * * 1.3.5.7 0.0.0.0/0 /* test */ a a CLASSIFY all -- * * 1.3.5.7 0.0.0.0/0 /* test2 */ CLASSIFY set 1:4 `}, tags: []map[string]string{}, fields: [][]map[string]interface{}{}, }, { // 11 - all target and ports table: "all_recv", chains: []string{"accountfwd"}, values: []string{ `Chain accountfwd (1 references) pkts bytes target prot opt in out source destination 123 456 all -- eth0 * 0.0.0.0/0 0.0.0.0/0 /* all_recv */ `}, tags: []map[string]string{ {"table": "all_recv", "chain": "accountfwd", "target": "all", "ruleid": "all_recv"}, }, fields: [][]map[string]interface{}{ {map[string]interface{}{"pkts": uint64(123), "bytes": uint64(456)}}, }, }, } for i, tt := range tests { t.Run(tt.table, func(t *testing.T) { i++ ipt := &Iptables{ Table: tt.table, Chains: tt.chains, lister: func(table, chain string) (string, error) { if len(tt.values) > 0 { v := tt.values[0] tt.values = tt.values[1:] return v, nil } return "", nil }, } acc := new(testutil.Accumulator) err := acc.GatherError(ipt.Gather) if !reflect.DeepEqual(tt.err, err) { t.Errorf("%d: expected error '%#v' got '%#v'", i, tt.err, err) } if tt.table == "" { n := acc.NFields() if n != 0 { t.Errorf("%d: expected 0 fields if empty table got %d", i, n) } return } if len(tt.chains) == 0 { n := acc.NFields() if n != 0 { t.Errorf("%d: expected 0 fields if empty chains got %d", i, n) } return } if len(tt.tags) == 0 { n := acc.NFields() if n != 0 { t.Errorf("%d: expected 0 values got %d", i, n) } return } n := 0 for j, tags := range tt.tags { for k, fields := range tt.fields[j] { if len(acc.Metrics) < n+1 { t.Errorf("%d: expected at least %d values got %d", i, n+1, len(acc.Metrics)) break } m := acc.Metrics[n] if !reflect.DeepEqual(m.Measurement, measurement) { t.Errorf("%d %d %d: expected measurement '%#v' got '%#v'\n", i, j, k, measurement, m.Measurement) } if !reflect.DeepEqual(m.Tags, tags) { t.Errorf("%d %d %d: expected tags\n%#v got\n%#v\n", i, j, k, tags, m.Tags) } if !reflect.DeepEqual(m.Fields, fields) { t.Errorf("%d %d %d: expected fields\n%#v got\n%#v\n", i, j, k, fields, m.Fields) } n++ } } }) } } func TestIptables_Gather_listerError(t *testing.T) { errFoo := errors.New("error foobar") ipt := &Iptables{ Table: "nat", Chains: []string{"foo", "bar"}, lister: func(table, chain string) (string, error) { return "", errFoo }, } acc := new(testutil.Accumulator) err := acc.GatherError(ipt.Gather) if !reflect.DeepEqual(err, errFoo) { t.Errorf("Expected error %#v got\n%#v\n", errFoo, err) } }