Skip to content

fix(ipset): count members when "Number of entries" is missing from ipset list#489

Merged
blotus merged 1 commit into
crowdsecurity:mainfrom
siamskoi:fix/ipset-len-without-number-of-entries
Jun 2, 2026
Merged

fix(ipset): count members when "Number of entries" is missing from ipset list#489
blotus merged 1 commit into
crowdsecurity:mainfrom
siamskoi:fix/ipset-len-without-number-of-entries

Conversation

@siamskoi

@siamskoi siamskoi commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

What

IPSet.Len() returns the correct entry count on kernels whose ipset list
output omits the Number of entries: header line, fixing the
fw_bouncer_banned_ips metric reporting 0 on those systems.

Why / root cause

IPSet.Len() parsed only the Number of entries: line of ipset list:

  for _, line := range strings.Split(string(out), "\n") {
      if !strings.Contains(strings.ToLower(line), "number of entries:") {
          continue
      }   
      ...
  }   
  return 0

That line is only printed by the newer set protocol. On kernels that expose
set protocol v6 (common on embedded systems — e.g. Keenetic routers
running Entware), ipset list produces no such line:

Name: crowdsec-blacklists-0
Type: hash:net
Revision: 6
Header: family inet hashsize 8192 maxelem 131072 timeout 300
Size in memory: 760832
References: 1
Members:
5.11.143.72 timeout 590431
...

Len() never matches and falls through to return 0. Because the
fw_bouncer_banned_ips gauge is set directly from set.Len()
(pkg/iptables/metrics.go), the metric reports 0 even when the ipsets
hold tens of thousands of entries. The fw_bouncer_dropped_* and
lapi_requests_* metrics are parsed differently and were unaffected.

What changed

  • Extracted the parsing into a pure parseIPSetLen() helper.
  • Kept the fast path using Number of entries: when present.
  • Added a fallback that counts the member lines after the Members:
    header when that line is absent.

Testing

  • go test ./pkg/ipsetcmd/ — new table test TestParseIPSetLen covers a
    modern (Number of entries:) output, a protocol-v6 output without that
    line, an empty set, and garbage input.
  • Built for linux/arm64 and deployed on a Keenetic router (set protocol
    v6): fw_bouncer_banned_ips now matches the real ipset sizes
    (e.g. 19255 for the CAPI set) instead of 0.

Notes

No behaviour change on kernels that already emit Number of entries: — the
fast path is unchanged; the fallback only triggers when the line is absent.

@siamskoi siamskoi changed the title fix(ipset): count members when "Number of entries" is absent fix(ipset): count members when "Number of entries" is missing from ipset list Jun 2, 2026
IPSet.Len() parsed only the "Number of entries:" line of `ipset list`
output. Kernels exposing the set protocol v6 (e.g. embedded routers such
as Keenetic) do not emit that line, so Len() silently fell back to 0.
This made the fw_bouncer_banned_ips gauge report 0 even when the ipsets
held tens of thousands of entries.

Add a fallback that counts the member lines after the "Members:" header
when the "Number of entries:" line is missing. The parsing is extracted
into a pure parseIPSetLen() helper, covered by a test exercising both
output formats (with and without the header), an empty set and garbage.
@siamskoi siamskoi force-pushed the fix/ipset-len-without-number-of-entries branch from b3c7ba8 to fe79e6c Compare June 2, 2026 10:22
@blotus blotus merged commit d4cba40 into crowdsecurity:main Jun 2, 2026
5 checks passed
@siamskoi siamskoi deleted the fix/ipset-len-without-number-of-entries branch June 3, 2026 10:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants