Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 32 additions & 4 deletions pkg/ipsetcmd/ipset.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,25 +225,53 @@ func (i *IPSet) Len() int {
return 0
}

for _, line := range strings.Split(string(out), "\n") {
return parseIPSetLen(string(out))
}

// parseIPSetLen extracts the number of entries from the output of `ipset list <set>`.
//
// Most ipset versions print a "Number of entries:" header line, which we use when
// present. Some kernels (set protocol v6, e.g. on embedded routers) omit that line
// entirely, so we fall back to counting the member lines listed after "Members:".
func parseIPSetLen(out string) int {
lines := strings.Split(out, "\n")

for _, line := range lines {
if !strings.Contains(strings.ToLower(line), "number of entries:") {
continue
}

fields := strings.Split(line, ":")
fields := strings.SplitN(line, ":", 2)
if len(fields) != 2 {
continue
}

count, err := strconv.Atoi(strings.TrimSpace(fields[1]))
if err != nil {
return 0
continue
}

return count
}

return 0
inMembers := false
count := 0

for _, line := range lines {
if !inMembers {
if strings.TrimSpace(line) == "Members:" {
inMembers = true
}

continue
}

if strings.TrimSpace(line) != "" {
count++
}
}

return count
}

// Helpers.
Expand Down
68 changes: 68 additions & 0 deletions pkg/ipsetcmd/ipset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package ipsetcmd

import "testing"

func TestParseIPSetLen(t *testing.T) {
tests := []struct {
name string
out string
want int
}{
{
name: "modern ipset with Number of entries header",
out: `Name: crowdsec-blacklists-0
Type: hash:net
Revision: 7
Header: family inet hashsize 8192 maxelem 131072 timeout 300
Size in memory: 769856
References: 1
Number of entries: 3
Members:
1.2.3.4 timeout 290
5.6.7.8 timeout 100
9.10.11.12 timeout 50
`,
want: 3,
},
{
name: "protocol v6 kernel without Number of entries header",
out: `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
91.92.42.120 timeout 10034
20.64.105.88 timeout 6299
`,
want: 3,
},
{
name: "empty set without header",
out: `Name: crowdsec-blacklists-1
Type: hash:net
Revision: 6
Header: family inet hashsize 1024 maxelem 131072 timeout 300
Size in memory: 4192
References: 1
Members:
`,
want: 0,
},
{
name: "garbage output",
out: "some error happened\n",
want: 0,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if got := parseIPSetLen(tc.out); got != tc.want {
t.Errorf("parseIPSetLen() = %d, want %d", got, tc.want)
}
})
}
}
Loading