diff --git a/pkg/nftables/metrics.go b/pkg/nftables/metrics.go index 8b7cbc01..24f98da7 100644 --- a/pkg/nftables/metrics.go +++ b/pkg/nftables/metrics.go @@ -4,93 +4,61 @@ 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) } - 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) } - 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 } @@ -98,7 +66,7 @@ func (c *nftContext) collectDropped(path string, hooks []string) (int, int, int) 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) } @@ -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) } @@ -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) } @@ -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)) diff --git a/pkg/nftables/nftables_context.go b/pkg/nftables/nftables_context.go index 4d45421b..73cd61be 100644 --- a/pkg/nftables/nftables_context.go +++ b/pkg/nftables/nftables_context.go @@ -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 @@ -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) @@ -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 @@ -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 }