Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2cb9dd5
add nftables input plugin
ajohnson-aurora Sep 8, 2025
4e330fd
Add Sudo and Binary options
ansoni Sep 8, 2025
d8405dc
Remove unused configuration example
ansoni Sep 8, 2025
8a9e9da
Update Readme with linter feedback
ajohnson-aurora Sep 18, 2025
e4b33a2
go fmt updates
ajohnson-aurora Sep 18, 2025
9775fe8
PR Feedback
ajohnson-aurora Sep 23, 2025
5fc2700
missed long line
ajohnson-aurora Sep 23, 2025
edd1bd5
Add notlinux compliation and Address some README lint issues
ajohnson-aurora Sep 23, 2025
841b7e4
fix `make check` errors
ajohnson-aurora Sep 23, 2025
0921ca1
PR Feedback
ajohnson-aurora Oct 6, 2025
a55e09d
Add back speculative Telegraf Version
ajohnson-aurora Oct 6, 2025
0fef89a
More PR Feedback
ajohnson-aurora Oct 7, 2025
5651dfc
PR Feedback
ajohnson-aurora Oct 13, 2025
a46b125
Linter fixes
ajohnson-aurora Oct 13, 2025
34fd401
fix macos linter
ajohnson-aurora Oct 13, 2025
8dd3b49
PR Feedback
ajohnson-aurora Nov 4, 2025
56a509e
Use implicit declaration of error
srebhan Nov 5, 2025
a318d13
Improve comment
srebhan Nov 13, 2025
5f42e15
Cleanup init
srebhan Nov 13, 2025
ed20c94
Cleanup formatting in sample.conf
srebhan Nov 13, 2025
7fbca44
Cleanup variable naming and parameter order
srebhan Nov 13, 2025
f66f82e
Translate unit-test into testcase-based tests
srebhan Nov 13, 2025
15740e4
Remove debug output
srebhan Nov 13, 2025
41926d7
Merge functions
srebhan Nov 13, 2025
24ef6d9
Improve error reporting
srebhan Nov 13, 2025
4314b90
Add test-case for non-existant table
srebhan Nov 13, 2025
0da365f
Update plugins/inputs/nftables/README.md
skartikey Nov 18, 2025
e50e2a1
Update plugins/inputs/nftables/README.md
skartikey Nov 18, 2025
2095cbf
fix(inputs.nftables): Fix typo in test case directory name
skartikey Nov 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
PR Feedback
  • Loading branch information
ajohnson-aurora committed Sep 23, 2025
commit 9775fe8c0fc81180083b9d688289171b62caa25a
72 changes: 38 additions & 34 deletions plugins/inputs/nftables/nftables.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
//go:generate ../../../tools/readme_config_includer/generator
//go:build linux

package nftables

import (
"encoding/json"
"errors"
"fmt"
"os/exec"

"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/inputs"
"os/exec"
)

const measurement = "nftables"
Comment thread
srebhan marked this conversation as resolved.
Outdated
Expand All @@ -17,43 +21,43 @@ type Nftables struct {
Tables []string
Comment thread
skartikey marked this conversation as resolved.
Outdated
}

var NftableConfig = `
var nftableConfig = `
Comment thread
skartikey marked this conversation as resolved.
Outdated
## Configuration for nftables
tables = [ "filter" ]
`

type Nftable struct {
Metainfo *Metainfo
Rules []*Rule
Metainfo *Metainfo
Rules []*Rule
JSONSchemaVersion int `json:"json_schema_version"`
Comment thread
srebhan marked this conversation as resolved.
Outdated
}

// The nftable JSON output is designed in a generic way so
// just handle it via some custom unmarshalling to keep our
// interface clean
// UnmarshalJSON handles custom parsing of the nftable output which is
// designed in a generic that is not compatible with the generic parser.
Comment thread
srebhan marked this conversation as resolved.
Outdated
func (nftable *Nftable) UnmarshalJSON(b []byte) error {
var aTable map[string][]map[string]json.RawMessage
if err := json.Unmarshal(b, &aTable); err != nil {
return fmt.Errorf("Unable to Unmarshal: %s", b)
var atable map[string][]map[string]json.RawMessage
Comment thread
skartikey marked this conversation as resolved.
Outdated
if err := json.Unmarshal(b, &atable); err != nil {
return fmt.Errorf("unable to unmarshal: %s", b)
}
// []map[string]interface
nfthings := aTable["nftables"]
nfthings := atable["nftables"]
for _, nfthing := range nfthings {
hasKey := func(key string) bool { _, ok := nfthing[key]; return ok }
switch {
case hasKey("metainfo"):
var mi Metainfo
if err := json.Unmarshal(nfthing["metainfo"], &mi); err == nil {
nftable.Metainfo = &mi
} else {
return fmt.Errorf("Unable to parse Metadata: %v", err)
err := json.Unmarshal(nfthing["metainfo"], &mi)
if err != nil {
return fmt.Errorf("unable to parse metadata: %w", err)
}
nftable.Metainfo = &mi
case hasKey("rule"):
var r Rule
if err := json.Unmarshal(nfthing["rule"], &r); err == nil {
nftable.Rules = append(nftable.Rules, &r)
} else {
return fmt.Errorf("Unable to parse Rule: %v", err)
err := json.Unmarshal(nfthing["rule"], &r)
if err != nil {
return fmt.Errorf("unable to parse rule: %w", err)
}
nftable.Rules = append(nftable.Rules, &r)
default:
// something we are not parsing
}
Expand All @@ -64,7 +68,7 @@ func (nftable *Nftable) UnmarshalJSON(b []byte) error {
type Metainfo struct {
Version string `json:"version"`
ReleaseName string `json:"release_name"`
JsonSchemaVersion int `json:"json_schema_version"`
JSONSchemaVersion int `json:"json_schema_version"`
}

type Rule struct {
Comment thread
skartikey marked this conversation as resolved.
Outdated
Expand All @@ -75,30 +79,34 @@ type Rule struct {
Counter *Counter
}

// UnmarshalJSON handles properly extracing the counter expression from
Comment thread
skartikey marked this conversation as resolved.
Outdated
// the Exprs array input
func (rule *Rule) UnmarshalJSON(b []byte) error {
var raw struct {
Family string `json:"family"`
Table string `json:"table"`
Chain string `json:"chain"`
Comment string `json:comment"`
Comment string `json:"comment"`
Exprs []map[string]json.RawMessage `json:"expr"`
}
if err := json.Unmarshal(b, &raw); err != nil {
return fmt.Errorf("Unable to Unmarshal: %s", b)
return fmt.Errorf("unable to unmarshal: %s", b)
}
rule.Family = raw.Family
rule.Table = raw.Table
rule.Chain = raw.Chain
rule.Comment = raw.Comment

// iterate rule expressions looking for the single counter
for _, expr := range raw.Exprs {
hasKey := func(key string) bool { _, ok := expr[key]; return ok }
switch {
case hasKey("counter"):
if hasKey("counter") {
rule.Counter = &Counter{}
if err := json.Unmarshal(expr["counter"], rule.Counter); err != nil {
return fmt.Errorf("Unable to parse Metadata: %v", err)
return fmt.Errorf("unable to parse metadata: %w", err)
Comment thread
skartikey marked this conversation as resolved.
Outdated
}
// we can return early since we are not looking for anything else
Comment thread
skartikey marked this conversation as resolved.
Outdated
return nil
}
}
return nil
Expand All @@ -109,17 +117,13 @@ type Counter struct {
Bytes int64 `json:"bytes"`
}

func (nft *Nftables) SampleConfig() string {
return NftableConfig
}

func (nft *Nftables) Description() string {
return "Gather chain data from an nftable table"
func (*Nftables) SampleConfig() string {
return nftableConfig
}

func (nft *Nftables) Gather(acc telegraf.Accumulator) error {
if len(nft.Tables) == 0 {
return errors.New("Invalid Configuration. Expected a `Tables` entry with list of nftables to monitor")
return errors.New("invalid configuration - expected a `Tables` entry with list of nftables to monitor")
Comment thread
skartikey marked this conversation as resolved.
Outdated
}
for _, table := range nft.Tables {
err := nft.getTableData(table, acc)
Expand Down Expand Up @@ -152,7 +156,7 @@ func (nft *Nftables) getTableData(tableName string, acc telegraf.Accumulator) er
c := exec.Command(name, args...)
out, err := c.Output()
if err != nil {
return errors.New(fmt.Sprintf("Error Executing %s, error: %s", c, err))
return fmt.Errorf("error executing %s, error: %w", c, err)
Comment thread
skartikey marked this conversation as resolved.
Outdated
}
return parseNftableOutput(out, acc)
}
Expand All @@ -161,7 +165,7 @@ func parseNftableOutput(out []byte, acc telegraf.Accumulator) error {
var nftable Nftable
err := json.Unmarshal(out, &nftable)
if err != nil {
return errors.New(fmt.Sprintf("Error Parsing: %s, Error: %v", out, err))
return fmt.Errorf("error parsing: %s, Error: %w", out, err)
}
for _, rule := range nftable.Rules {
// Rule must have a Counter and a Comment
Expand Down
34 changes: 16 additions & 18 deletions plugins/inputs/nftables/nftables_test.go
Comment thread
srebhan marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package nftables

import (
"errors"
"reflect"
"testing"

"github.com/influxdata/telegraf/testutil"
)

var singleton_table = `{
var singletontable = `{
Comment thread
skartikey marked this conversation as resolved.
Outdated
"nftables": [
{
"metainfo": {
Expand Down Expand Up @@ -153,7 +151,7 @@ var singleton_table = `{
}`

func TestParseNftableBadRule(t *testing.T) {
bad_rules := []string{
badrules := []string{
`{ "nftables": [
{
"rule": "I am a weird rule"
Expand All @@ -170,23 +168,23 @@ func TestParseNftableBadRule(t *testing.T) {
"rule": []
},
}`}
bad_errors := []string{
"Error Parsing: { \"nftables\": [\n {\n \"rule\": \"I am a weird rule\"\n }\n ]\n}, Error: Unable to parse Rule: Unable to Unmarshal: \"I am a weird rule\"",
"Error Parsing: { \"nftables\": [\n {\n \"rule\": {}\n },\n}, Error: invalid character '}' looking for beginning of value",
"Error Parsing: { \"nftables\": [\n {\n \"rule\": []\n },\n}, Error: invalid character '}' looking for beginning of value",
baderrors := []string{
"error parsing: { \"nftables\": [\n {\n \"rule\": \"I am a weird rule\"\n }\n ]\n}, Error: unable to parse rule: unable to unmarshal: \"I am a weird rule\"",
"error parsing: { \"nftables\": [\n {\n \"rule\": {}\n },\n}, Error: invalid character '}' looking for beginning of value",
"error parsing: { \"nftables\": [\n {\n \"rule\": []\n },\n}, Error: invalid character '}' looking for beginning of value",
}
for i, v := range bad_rules {
for i, v := range badrules {
acc := new(testutil.Accumulator)
err := parseNftableOutput([]byte(v), acc)
if err.Error() != bad_errors[i] {
t.Errorf("Expected Error %#v, but got %#v", bad_errors[i], err.Error())
if err.Error() != baderrors[i] {
t.Errorf("Expected Error %#v, but got %#v", baderrors[i], err.Error())
}
}
}

func TestParseNftableOutput(t *testing.T) {
acc := new(testutil.Accumulator)
Comment thread
srebhan marked this conversation as resolved.
Outdated
err := parseNftableOutput([]byte(singleton_table), acc)
err := parseNftableOutput([]byte(singletontable), acc)
if err != nil {
t.Errorf("No Error Expected: %#v", err)
}
Expand All @@ -206,20 +204,20 @@ func TestParseNftableOutput(t *testing.T) {
}

func TestParseNftableBadOutput(t *testing.T) {
errFoo := errors.New("Error Parsing: I am not JSON, Error: invalid character 'I' looking for beginning of value")
expected := "error parsing: I am not JSON, Error: invalid character 'I' looking for beginning of value"
acc := new(testutil.Accumulator)
err := parseNftableOutput([]byte("I am not JSON"), acc)
if !reflect.DeepEqual(err, errFoo) {
t.Errorf("Expected error %#v got\n%#v\n", errFoo, err)
if err.Error() != expected {
t.Errorf("Expected error %#v got\n%#v\n", expected, err)
}
}

func TestNftableBadConfig(t *testing.T) {
errFoo := errors.New("Invalid Configuration. Expected a `Tables` entry with list of nftables to monitor")
expected := "invalid configuration - expected a `Tables` entry with list of nftables to monitor"
ft := Nftables{}
acc := new(testutil.Accumulator)
err := acc.GatherError(ft.Gather)
if !reflect.DeepEqual(err, errFoo) {
t.Errorf("Expected error %#v got\n%#v\n", errFoo, err)
if err.Error() != expected {
t.Errorf("Expected error %#v got\n%#v\n", expected, err)
}
}
Loading