Skip to content

Commit

Permalink
use nft pkg to get metrics about rules
Browse files Browse the repository at this point in the history
  • Loading branch information
blotus committed Apr 22, 2024
1 parent 4f86e59 commit e484e80
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 84 deletions.
128 changes: 44 additions & 84 deletions pkg/nftables/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,101 +4,69 @@
package nftables

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

log "github.com/sirupsen/logrus"

"github.com/crowdsecurity/cs-firewall-bouncer/pkg/metrics"
)

type Counter struct {
Nftables []struct {
Rule struct {
Expr []struct {
Counter *struct {
Packets int `json:"packets"`
Bytes int `json:"bytes"`
} `json:"counter,omitempty"`
} `json:"expr"`
} `json:"rule,omitempty"`
} `json:"nftables"`
}

type Set struct {
Nftables []struct {
Set struct {
Elem []struct {
Elem struct{} `json:"elem"`
} `json:"elem"`
} `json:"set,omitempty"`
} `json:"nftables"`
}

func (c *nftContext) collectDroppedPackets(path string, chain string) (int, int, error) {
cmd := exec.Command(path, "-j", "list", "chain", c.ipFamily(), c.tableName, chain)
"github.com/google/nftables/expr"

out, err := cmd.CombinedOutput()
if err != nil {
return 0, 0, fmt.Errorf("while running %s: %w", cmd.String(), err)
}

parsedOut := Counter{}
if err := json.Unmarshal(out, &parsedOut); err != nil {
return 0, 0, err
}
log "github.com/sirupsen/logrus"
)

for _, r := range parsedOut.Nftables {
for _, expr := range r.Rule.Expr {
if expr.Counter != nil {
return expr.Counter.Packets, expr.Counter.Bytes, nil
func (c *nftContext) collectDroppedPackets(chain string) (int, int, error) {
droppedPackets := 0
droppedBytes := 0
//setName := ""
for chainName, chain := range c.chains {
rules, err := c.conn.GetRules(c.table, chain)
if err != nil {
log.Errorf("can't get rules for ip4 chain %s: %s", chainName, err)
continue
}
for _, rule := range rules {
for _, xpr := range rule.Exprs {
switch obj := xpr.(type) {
case *expr.Counter:
log.Infof("rule %d (%s): packets %d, bytes %d", rule.Position, rule.Table.Name, obj.Packets, obj.Bytes)
droppedPackets += int(obj.Packets)
droppedBytes += int(obj.Bytes)
case *expr.Lookup:
log.Infof("rule %d (%s): lookup %s", rule.Position, rule.Table.Name, obj.SetName)
//setName = obj.SetName
}
}
}
}

return 0, 0, nil
return droppedPackets, droppedBytes, nil
}

func (c *nftContext) ipFamily() string {
if c.version == "v4" {
return "ip"
}

return "ip6"
}
func (c *nftContext) collectActiveBannedIPs() (int, error) {
//Find the size of the set we have created
set, err := c.conn.GetSetByName(c.table, c.set.Name)

func (c *nftContext) collectActiveBannedIPs(path string) (int, error) {
cmd := exec.Command(path, "-j", "list", "set", c.ipFamily(), c.tableName, c.blacklists)

out, err := cmd.CombinedOutput()
if err != nil {
return 0, fmt.Errorf("while running %s: %w", cmd.String(), err)
return 0, fmt.Errorf("can't get set %s: %s", c.set.Name, err)

Check failure on line 49 in pkg/nftables/metrics.go

View workflow job for this annotation

GitHub Actions / golangci-lint + codeql

non-wrapping format verb for fmt.Errorf. Use `%w` to format errors (errorlint)
}

set := Set{}
if err := json.Unmarshal(out, &set); err != nil {
return 0, err
}
setContent, err := c.conn.GetSetElements(set)

ret := 0
for _, r := range set.Nftables {
ret += len(r.Set.Elem)
if err != nil {
return 0, fmt.Errorf("can't get set elements for %s: %s", c.set.Name, err)

Check failure on line 55 in pkg/nftables/metrics.go

View workflow job for this annotation

GitHub Actions / golangci-lint + codeql

non-wrapping format verb for fmt.Errorf. Use `%w` to format errors (errorlint)
}

return ret, nil
return len(setContent), nil
}

func (c *nftContext) collectDropped(path string, hooks []string) (int, int, int) {
func (c *nftContext) collectDropped(hooks []string) (int, int, int) {
if c.conn == nil {
return 0, 0, 0
}

var droppedPackets, droppedBytes, banned int

if c.setOnly {
pkt, byt, err := c.collectDroppedPackets(path, c.chainName)
pkt, byt, err := c.collectDroppedPackets(c.chainName)
if err != nil {
log.Errorf("can't collect dropped packets for ip%s from nft: %s", c.version, err)
}
Expand All @@ -107,7 +75,7 @@ func (c *nftContext) collectDropped(path string, hooks []string) (int, int, int)
droppedBytes += byt
} else {
for _, hook := range hooks {
pkt, byt, err := c.collectDroppedPackets(path, c.chainName+"-"+hook)
pkt, byt, err := c.collectDroppedPackets(c.chainName + "-" + hook)
if err != nil {
log.Errorf("can't collect dropped packets for ip%s from nft: %s", c.version, err)
}
Expand All @@ -116,7 +84,7 @@ func (c *nftContext) collectDropped(path string, hooks []string) (int, int, int)
}
}

banned, err := c.collectActiveBannedIPs(path)
banned, err := c.collectActiveBannedIPs()
if err != nil {
log.Errorf("can't collect total banned IPs for ip%s from nft: %s", c.version, err)
}
Expand All @@ -125,25 +93,17 @@ func (c *nftContext) collectDropped(path string, hooks []string) (int, int, int)
}

func (n *nft) CollectMetrics() {
path, err := exec.LookPath("nft")
if err != nil {
log.Error("can't monitor dropped packets: ", err)
return
}

cmd := exec.Command(path, "-j", "list", "tables")

_, err = cmd.CombinedOutput()
if err != nil {
log.Warningf("nft -j is not supported (requires 0.9.7), nftables metrics are disabled")
return
}

t := time.NewTicker(metrics.MetricCollectionInterval)

for range t.C {
ip4DroppedPackets, ip4DroppedBytes, bannedIP4 := n.v4.collectDropped(path, n.Hooks)
ip6DroppedPackets, ip6DroppedBytes, bannedIP6 := n.v6.collectDropped(path, n.Hooks)
startTime := time.Now()
ip4DroppedPackets, ip4DroppedBytes, bannedIP4 := n.v4.collectDropped(n.Hooks)
ip6DroppedPackets, ip6DroppedBytes, bannedIP6 := n.v6.collectDropped(n.Hooks)

log.Debugf("metrics collection took %s", time.Since(startTime))
log.Debugf("ip4: dropped packets: %d, dropped bytes: %d, banned IPs: %d", ip4DroppedPackets, ip4DroppedBytes, bannedIP4)
log.Debugf("ip6: dropped packets: %d, dropped bytes: %d, banned IPs: %d", ip6DroppedPackets, ip6DroppedBytes, bannedIP6)

metrics.TotalDroppedPackets.Set(float64(ip4DroppedPackets + ip6DroppedPackets))
metrics.TotalDroppedBytes.Set(float64(ip6DroppedBytes + ip4DroppedBytes))
Expand Down
11 changes: 11 additions & 0 deletions pkg/nftables/nftables_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var HookNameToHookID = map[string]nftables.ChainHook{
}

type nftContext struct {
chains map[string]*nftables.Chain
conn *nftables.Conn
set *nftables.Set
table *nftables.Table
Expand Down Expand Up @@ -196,6 +197,8 @@ func (c *nftContext) initOwnTable(hooks []string, denyLog bool, denyLogPrefix st
Priority: &priority,
})

c.chains[hook] = chain

log.Debugf("nftables: ip%s chain '%s' created", c.version, chain.Name)

r, err := c.createRule(chain, set, denyLog, denyLogPrefix, denyAction)
Expand All @@ -220,6 +223,10 @@ func (c *nftContext) init(hooks []string, denyLog bool, denyLogPrefix string, de
return nil
}

if c.chains == nil {
c.chains = make(map[string]*nftables.Chain)
}

log.Debugf("nftables: ip%s init starting", c.version)

var err error
Expand All @@ -236,6 +243,10 @@ func (c *nftContext) init(hooks []string, denyLog bool, denyLogPrefix string, de
"For example, use 'crowdsec-set' instead of 'crowdsec-blacklists'", err)
}

//metricsCounter := nftables.CounterObj{}

//c.conn.AddObj()

return err
}

Expand Down

0 comments on commit e484e80

Please sign in to comment.