Skip to content

Commit

Permalink
Support old conntrack stats
Browse files Browse the repository at this point in the history
Linux < 2.6.35 was missing the `search_restart` field in nf_conntrack.
* Refactor stat parsing to simplify code.
* Support 16-field nf_conntrack entries.
* Support additional fields.

Fixes: #499

Signed-off-by: SuperQ <superq@gmail.com>
  • Loading branch information
SuperQ authored and discordianfish committed Mar 21, 2023
1 parent f930a52 commit dd6bb71
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 62 deletions.
15 changes: 15 additions & 0 deletions internal/util/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,21 @@ func ParsePInt64s(ss []string) ([]*int64, error) {
return us, nil
}

// Parses a uint64 from given hex in string.
func ParseHexUint64s(ss []string) ([]*uint64, error) {
us := make([]*uint64, 0, len(ss))
for _, s := range ss {
u, err := strconv.ParseUint(s, 16, 64)
if err != nil {
return nil, err
}

us = append(us, &u)
}

return us, nil
}

// ReadUintFromFile reads a file and attempts to parse a uint64 from it.
func ReadUintFromFile(path string) (uint64, error) {
data, err := os.ReadFile(path)
Expand Down
88 changes: 26 additions & 62 deletions net_conntrackstat.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"bytes"
"fmt"
"io"
"strconv"
"strings"

"github.com/prometheus/procfs/internal/util"
Expand All @@ -28,9 +27,13 @@ import (
// and contains netfilter conntrack statistics at one CPU core.
type ConntrackStatEntry struct {
Entries uint64
Searched uint64
Found uint64
New uint64
Invalid uint64
Ignore uint64
Delete uint64
DeleteList uint64
Insert uint64
InsertFailed uint64
Drop uint64
Expand Down Expand Up @@ -81,73 +84,34 @@ func parseConntrackStat(r io.Reader) ([]ConntrackStatEntry, error) {

// Parses a ConntrackStatEntry from given array of fields.
func parseConntrackStatEntry(fields []string) (*ConntrackStatEntry, error) {
if len(fields) != 17 {
return nil, fmt.Errorf("invalid conntrackstat entry, missing fields")
}
entry := &ConntrackStatEntry{}

entries, err := parseConntrackStatField(fields[0])
if err != nil {
return nil, err
}
entry.Entries = entries

found, err := parseConntrackStatField(fields[2])
if err != nil {
return nil, err
}
entry.Found = found

invalid, err := parseConntrackStatField(fields[4])
if err != nil {
return nil, err
}
entry.Invalid = invalid

ignore, err := parseConntrackStatField(fields[5])
if err != nil {
return nil, err
}
entry.Ignore = ignore

insert, err := parseConntrackStatField(fields[8])
entries, err := util.ParseHexUint64s(fields)
if err != nil {
return nil, err
return nil, fmt.Errorf("invalid conntrackstat entry, couldn't parse fields: %s", err)
}
entry.Insert = insert

insertFailed, err := parseConntrackStatField(fields[9])
if err != nil {
return nil, err
numEntries := len(entries)
if numEntries < 16 || numEntries > 17 {
return nil, fmt.Errorf("invalid conntrackstat entry, invalid number of fields: %d", numEntries)
}
entry.InsertFailed = insertFailed

drop, err := parseConntrackStatField(fields[10])
if err != nil {
return nil, err
stats := &ConntrackStatEntry{
Entries: *entries[0],
Searched: *entries[1],
Found: *entries[2],
New: *entries[3],
Invalid: *entries[4],
Ignore: *entries[5],
Delete: *entries[6],
DeleteList: *entries[7],
Insert: *entries[8],
InsertFailed: *entries[9],
Drop: *entries[10],
EarlyDrop: *entries[11],
}
entry.Drop = drop

earlyDrop, err := parseConntrackStatField(fields[11])
if err != nil {
return nil, err
// Ignore missing search_restart on Linux < 2.6.35.
if numEntries == 17 {
stats.SearchRestart = *entries[16]
}
entry.EarlyDrop = earlyDrop

searchRestart, err := parseConntrackStatField(fields[16])
if err != nil {
return nil, err
}
entry.SearchRestart = searchRestart

return entry, nil
}

// Parses a uint64 from given hex in string.
func parseConntrackStatField(field string) (uint64, error) {
val, err := strconv.ParseUint(field, 16, 64)
if err != nil {
return 0, fmt.Errorf("couldn't parse %q field: %w", field, err)
}
return val, err
return stats, nil
}
33 changes: 33 additions & 0 deletions net_conntrackstat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,36 @@ func TestParseConntrackStat(t *testing.T) {
t.Errorf("want %v, have %v", want, have)
}
}

func TestParseOldConntrackStat(t *testing.T) {
var nfConntrackStat = []byte(`entries searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error expect_new expect_create expect_delete
0000002b 0003159f 02e6786a 00142562 0001bf93 00e1a051 00142537 000b8fe0 000b900b 00000000 00000000 00000000 0001b46a 00000000 00000000 00000000
`)
r := bytes.NewReader(nfConntrackStat)

have, err := parseConntrackStat(r)
if err != nil {
t.Fatal(err)
}

want := []ConntrackStatEntry{
ConntrackStatEntry{
Entries: 43,
Searched: 202143,
Found: 48658538,
New: 1320290,
Invalid: 114579,
Ignore: 14786641,
Delete: 1320247,
DeleteList: 757728,
Insert: 757771,
InsertFailed: 0,
Drop: 0,
EarlyDrop: 0,
SearchRestart: 0,
},
}
if !reflect.DeepEqual(want, have) {
t.Errorf("want %v, have %v", want, have)
}
}

0 comments on commit dd6bb71

Please sign in to comment.