Merge pull request #685 from cmattoon/conntrack-stat
Adds ConntrackStats (/proc/net/stat/nf_conntrack)
This commit is contained in:
commit
5335e3fd50
|
@ -213,6 +213,12 @@ func ReadInts(filename string) ([]int64, error) {
|
|||
return ret, nil
|
||||
}
|
||||
|
||||
// Parse Hex to uint32 without error
|
||||
func HexToUint32(hex string) uint32 {
|
||||
vv, _ := strconv.ParseUint(hex, 16, 32)
|
||||
return uint32(vv)
|
||||
}
|
||||
|
||||
// Parse to int32 without error
|
||||
func mustParseInt32(val string) int32 {
|
||||
vv, _ := strconv.ParseInt(val, 10, 32)
|
||||
|
|
|
@ -51,6 +51,12 @@ func TestByteToString(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestHexToUint32(t *testing.T) {
|
||||
if HexToUint32("FFFFFFFF") != 4294967295 {
|
||||
t.Error("Could not convert")
|
||||
}
|
||||
}
|
||||
|
||||
func TestmustParseInt32(t *testing.T) {
|
||||
ret := mustParseInt32("11111")
|
||||
if ret != int32(11111) {
|
||||
|
|
95
net/net.go
95
net/net.go
|
@ -70,6 +70,96 @@ type FilterStat struct {
|
|||
ConnTrackMax int64 `json:"conntrackMax"`
|
||||
}
|
||||
|
||||
// ConntrackStat has conntrack summary info
|
||||
type ConntrackStat struct {
|
||||
Entries uint32 `json:"entries"` // Number of entries in the conntrack table
|
||||
Searched uint32 `json:"searched"` // Number of conntrack table lookups performed
|
||||
Found uint32 `json:"found"` // Number of searched entries which were successful
|
||||
New uint32 `json:"new"` // Number of entries added which were not expected before
|
||||
Invalid uint32 `json:"invalid"` // Number of packets seen which can not be tracked
|
||||
Ignore uint32 `json:"ignore"` // Packets seen which are already connected to an entry
|
||||
Delete uint32 `json:"delete"` // Number of entries which were removed
|
||||
DeleteList uint32 `json:"delete_list"` // Number of entries which were put to dying list
|
||||
Insert uint32 `json:"insert"` // Number of entries inserted into the list
|
||||
InsertFailed uint32 `json:"insert_failed"` // # insertion attempted but failed (same entry exists)
|
||||
Drop uint32 `json:"drop"` // Number of packets dropped due to conntrack failure.
|
||||
EarlyDrop uint32 `json:"early_drop"` // Dropped entries to make room for new ones, if maxsize reached
|
||||
IcmpError uint32 `json:"icmp_error"` // Subset of invalid. Packets that can't be tracked d/t error
|
||||
ExpectNew uint32 `json:"expect_new"` // Entries added after an expectation was already present
|
||||
ExpectCreate uint32 `json:"expect_create"` // Expectations added
|
||||
ExpectDelete uint32 `json:"expect_delete"` // Expectations deleted
|
||||
SearchRestart uint32 `json:"search_restart"` // Conntrack table lookups restarted due to hashtable resizes
|
||||
}
|
||||
|
||||
func NewConntrackStat(e uint32, s uint32, f uint32, n uint32, inv uint32, ign uint32, del uint32, dlst uint32, ins uint32, insfail uint32, drop uint32, edrop uint32, ie uint32, en uint32, ec uint32, ed uint32, sr uint32) *ConntrackStat {
|
||||
return &ConntrackStat{
|
||||
Entries: e,
|
||||
Searched: s,
|
||||
Found: f,
|
||||
New: n,
|
||||
Invalid: inv,
|
||||
Ignore: ign,
|
||||
Delete: del,
|
||||
DeleteList: dlst,
|
||||
Insert: ins,
|
||||
InsertFailed: insfail,
|
||||
Drop: drop,
|
||||
EarlyDrop: edrop,
|
||||
IcmpError: ie,
|
||||
ExpectNew: en,
|
||||
ExpectCreate: ec,
|
||||
ExpectDelete: ed,
|
||||
SearchRestart: sr,
|
||||
}
|
||||
}
|
||||
|
||||
type ConntrackStatList struct {
|
||||
items []*ConntrackStat
|
||||
}
|
||||
|
||||
func NewConntrackStatList() *ConntrackStatList {
|
||||
return &ConntrackStatList{
|
||||
items: []*ConntrackStat{},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ConntrackStatList) Append(c *ConntrackStat) {
|
||||
l.items = append(l.items, c)
|
||||
}
|
||||
|
||||
func (l *ConntrackStatList) Items() []ConntrackStat {
|
||||
items := make([]ConntrackStat, len(l.items), len(l.items))
|
||||
for i, el := range l.items {
|
||||
items[i] = *el
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// Summary returns a single-element list with totals from all list items.
|
||||
func (l *ConntrackStatList) Summary() []ConntrackStat {
|
||||
summary := NewConntrackStat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
for _, cs := range l.items {
|
||||
summary.Entries += cs.Entries
|
||||
summary.Searched += cs.Searched
|
||||
summary.Found += cs.Found
|
||||
summary.New += cs.New
|
||||
summary.Invalid += cs.Invalid
|
||||
summary.Ignore += cs.Ignore
|
||||
summary.Delete += cs.Delete
|
||||
summary.DeleteList += cs.DeleteList
|
||||
summary.Insert += cs.Insert
|
||||
summary.InsertFailed += cs.InsertFailed
|
||||
summary.Drop += cs.Drop
|
||||
summary.EarlyDrop += cs.EarlyDrop
|
||||
summary.IcmpError += cs.IcmpError
|
||||
summary.ExpectNew += cs.ExpectNew
|
||||
summary.ExpectCreate += cs.ExpectCreate
|
||||
summary.ExpectDelete += cs.ExpectDelete
|
||||
summary.SearchRestart += cs.SearchRestart
|
||||
}
|
||||
return []ConntrackStat{*summary}
|
||||
}
|
||||
|
||||
var constMap = map[string]int{
|
||||
"unix": syscall.AF_UNIX,
|
||||
"TCP": syscall.SOCK_STREAM,
|
||||
|
@ -108,6 +198,11 @@ func (n InterfaceAddr) String() string {
|
|||
return string(s)
|
||||
}
|
||||
|
||||
func (n ConntrackStat) String() string {
|
||||
s, _ := json.Marshal(n)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func Interfaces() ([]InterfaceStat, error) {
|
||||
return InterfacesWithContext(context.Background())
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
@ -271,6 +272,14 @@ func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
|
|||
return nil, errors.New("NetFilterCounters not implemented for darwin")
|
||||
}
|
||||
|
||||
func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
|
||||
return ConntrackStatsWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
// NetProtoCounters returns network statistics for the entire system
|
||||
// If protocols is empty then all protocols are returned, otherwise
|
||||
// just the protocols in the list are returned.
|
||||
|
|
|
@ -24,6 +24,14 @@ func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
|
|||
return []FilterStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
|
||||
return ConntrackStatsWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
|
||||
return ProtoCountersWithContext(context.Background(), protocols)
|
||||
}
|
||||
|
|
|
@ -112,6 +112,14 @@ func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
|
|||
return nil, errors.New("NetFilterCounters not implemented for freebsd")
|
||||
}
|
||||
|
||||
func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
|
||||
return ConntrackStatsWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
|
||||
return nil, errors.New("ConntrackStats not implemented for freebsd")
|
||||
}
|
||||
|
||||
// NetProtoCounters returns network statistics for the entire system
|
||||
// If protocols is empty then all protocols are returned, otherwise
|
||||
// just the protocols in the list are returned.
|
||||
|
|
|
@ -18,6 +18,26 @@ import (
|
|||
"github.com/shirou/gopsutil/internal/common"
|
||||
)
|
||||
|
||||
const ( // Conntrack Column numbers
|
||||
CT_ENTRIES = iota
|
||||
CT_SEARCHED
|
||||
CT_FOUND
|
||||
CT_NEW
|
||||
CT_INVALID
|
||||
CT_IGNORE
|
||||
CT_DELETE
|
||||
CT_DELETE_LIST
|
||||
CT_INSERT
|
||||
CT_INSERT_FAILED
|
||||
CT_DROP
|
||||
CT_EARLY_DROP
|
||||
CT_ICMP_ERROR
|
||||
CT_EXPECT_NEW
|
||||
CT_EXPECT_CREATE
|
||||
CT_EXPECT_DELETE
|
||||
CT_SEARCH_RESTART
|
||||
)
|
||||
|
||||
// NetIOCounters returnes network I/O statistics for every network
|
||||
// interface installed on the system. If pernic argument is false,
|
||||
// return only sum of all information (which name is 'all'). If true,
|
||||
|
@ -232,6 +252,58 @@ func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
|
|||
return stats, nil
|
||||
}
|
||||
|
||||
// ConntrackStats returns more detailed info about the conntrack table
|
||||
func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
|
||||
return ConntrackStatsWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
// ConntrackStatsWithContext returns more detailed info about the conntrack table
|
||||
func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
|
||||
return conntrackStatsFromFile(common.HostProc("net/stat/nf_conntrack"), percpu)
|
||||
}
|
||||
|
||||
// conntrackStatsFromFile returns more detailed info about the conntrack table
|
||||
// from `filename`
|
||||
// If 'percpu' is false, the result will contain exactly one item with totals/summary
|
||||
func conntrackStatsFromFile(filename string, percpu bool) ([]ConntrackStat, error) {
|
||||
lines, err := common.ReadLines(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
statlist := NewConntrackStatList()
|
||||
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) == 17 && fields[0] != "entries" {
|
||||
statlist.Append(NewConntrackStat(
|
||||
common.HexToUint32(fields[CT_ENTRIES]),
|
||||
common.HexToUint32(fields[CT_SEARCHED]),
|
||||
common.HexToUint32(fields[CT_FOUND]),
|
||||
common.HexToUint32(fields[CT_NEW]),
|
||||
common.HexToUint32(fields[CT_INVALID]),
|
||||
common.HexToUint32(fields[CT_IGNORE]),
|
||||
common.HexToUint32(fields[CT_DELETE]),
|
||||
common.HexToUint32(fields[CT_DELETE_LIST]),
|
||||
common.HexToUint32(fields[CT_INSERT]),
|
||||
common.HexToUint32(fields[CT_INSERT_FAILED]),
|
||||
common.HexToUint32(fields[CT_DROP]),
|
||||
common.HexToUint32(fields[CT_EARLY_DROP]),
|
||||
common.HexToUint32(fields[CT_ICMP_ERROR]),
|
||||
common.HexToUint32(fields[CT_EXPECT_NEW]),
|
||||
common.HexToUint32(fields[CT_EXPECT_CREATE]),
|
||||
common.HexToUint32(fields[CT_EXPECT_DELETE]),
|
||||
common.HexToUint32(fields[CT_SEARCH_RESTART]),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
if percpu {
|
||||
return statlist.Items(), nil
|
||||
}
|
||||
return statlist.Summary(), nil
|
||||
}
|
||||
|
||||
// http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h
|
||||
var TCPStatuses = map[string]string{
|
||||
"01": "ESTABLISHED",
|
||||
|
|
|
@ -161,3 +161,142 @@ func TestReverse(t *testing.T) {
|
|||
src := []byte{0x01, 0x02, 0x03}
|
||||
assert.Equal(t, []byte{0x03, 0x02, 0x01}, Reverse(src))
|
||||
}
|
||||
|
||||
func TestConntrackStatFileParsing(t *testing.T) {
|
||||
tmpfile, err := ioutil.TempFile("", "proc_net_stat_conntrack")
|
||||
defer os.Remove(tmpfile.Name())
|
||||
assert.Nil(t, err, "Temporary file creation failed: ", err)
|
||||
|
||||
data := []byte(`
|
||||
entries searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error expect_new expect_create expect_delete search_restart
|
||||
0000007b 00000000 00000000 00000000 000b115a 00000084 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 0000004a
|
||||
0000007b 00000000 00000000 00000000 0007eee5 00000068 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000035
|
||||
0000007b 00000000 00000000 00000000 0090346b 00000057 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000025
|
||||
0000007b 00000000 00000000 00000000 0005920f 00000069 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000064
|
||||
0000007b 00000000 00000000 00000000 000331ff 00000059 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 0000003b
|
||||
0000007b 00000000 00000000 00000000 000314ea 00000066 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000054
|
||||
0000007b 00000000 00000000 00000000 0002b270 00000055 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 0000003d
|
||||
0000007b 00000000 00000000 00000000 0002f67d 00000057 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000042
|
||||
`)
|
||||
|
||||
// Expected results
|
||||
slist := NewConntrackStatList()
|
||||
|
||||
slist.Append(&ConntrackStat{
|
||||
Entries: 123,
|
||||
Searched: 0,
|
||||
Found: 0,
|
||||
New: 0,
|
||||
Invalid: 725338,
|
||||
Ignore: 132,
|
||||
Delete: 0,
|
||||
DeleteList: 0,
|
||||
Insert: 0,
|
||||
InsertFailed: 0,
|
||||
Drop: 0,
|
||||
EarlyDrop: 0,
|
||||
IcmpError: 0,
|
||||
ExpectNew: 0,
|
||||
ExpectCreate: 0,
|
||||
ExpectDelete: 0,
|
||||
SearchRestart: 74,
|
||||
})
|
||||
slist.Append(&ConntrackStat{123, 0, 0, 0, 519909, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53})
|
||||
|
||||
slist.Append(&ConntrackStat{123, 0, 0, 0, 9450603, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37})
|
||||
slist.Append(&ConntrackStat{123, 0, 0, 0, 365071, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100})
|
||||
|
||||
slist.Append(&ConntrackStat{123, 0, 0, 0, 209407, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59})
|
||||
slist.Append(&ConntrackStat{123, 0, 0, 0, 201962, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84})
|
||||
|
||||
slist.Append(&ConntrackStat{123, 0, 0, 0, 176752, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61})
|
||||
slist.Append(&ConntrackStat{123, 0, 0, 0, 194173, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66})
|
||||
|
||||
// Write data to tempfile
|
||||
_, err = tmpfile.Write(data)
|
||||
assert.Nil(t, err, "Temporary file writing failed: ", err)
|
||||
|
||||
// Function under test
|
||||
stats, err := conntrackStatsFromFile(tmpfile.Name(), true)
|
||||
assert.Equal(t, 8, len(stats), "Expected 8 results")
|
||||
|
||||
summary := &ConntrackStat{}
|
||||
for i, exp := range slist.Items() {
|
||||
st := stats[i]
|
||||
|
||||
assert.Equal(t, exp.Entries, st.Entries)
|
||||
summary.Entries += st.Entries
|
||||
|
||||
assert.Equal(t, exp.Searched, st.Searched)
|
||||
summary.Searched += st.Searched
|
||||
|
||||
assert.Equal(t, exp.Found, st.Found)
|
||||
summary.Found += st.Found
|
||||
|
||||
assert.Equal(t, exp.New, st.New)
|
||||
summary.New += st.New
|
||||
|
||||
assert.Equal(t, exp.Invalid, st.Invalid)
|
||||
summary.Invalid += st.Invalid
|
||||
|
||||
assert.Equal(t, exp.Ignore, st.Ignore)
|
||||
summary.Ignore += st.Ignore
|
||||
|
||||
assert.Equal(t, exp.Delete, st.Delete)
|
||||
summary.Delete += st.Delete
|
||||
|
||||
assert.Equal(t, exp.DeleteList, st.DeleteList)
|
||||
summary.DeleteList += st.DeleteList
|
||||
|
||||
assert.Equal(t, exp.Insert, st.Insert)
|
||||
summary.Insert += st.Insert
|
||||
|
||||
assert.Equal(t, exp.InsertFailed, st.InsertFailed)
|
||||
summary.InsertFailed += st.InsertFailed
|
||||
|
||||
assert.Equal(t, exp.Drop, st.Drop)
|
||||
summary.Drop += st.Drop
|
||||
|
||||
assert.Equal(t, exp.EarlyDrop, st.EarlyDrop)
|
||||
summary.EarlyDrop += st.EarlyDrop
|
||||
|
||||
assert.Equal(t, exp.IcmpError, st.IcmpError)
|
||||
summary.IcmpError += st.IcmpError
|
||||
|
||||
assert.Equal(t, exp.ExpectNew, st.ExpectNew)
|
||||
summary.ExpectNew += st.ExpectNew
|
||||
|
||||
assert.Equal(t, exp.ExpectCreate, st.ExpectCreate)
|
||||
summary.ExpectCreate += st.ExpectCreate
|
||||
|
||||
assert.Equal(t, exp.ExpectDelete, st.ExpectDelete)
|
||||
summary.ExpectDelete += st.ExpectDelete
|
||||
|
||||
assert.Equal(t, exp.SearchRestart, st.SearchRestart)
|
||||
summary.SearchRestart += st.SearchRestart
|
||||
}
|
||||
|
||||
// Test summary grouping
|
||||
totals, err := conntrackStatsFromFile(tmpfile.Name(), false)
|
||||
for i, st := range totals {
|
||||
assert.Equal(t, summary.Entries, st.Entries)
|
||||
assert.Equal(t, summary.Searched, st.Searched)
|
||||
assert.Equal(t, summary.Found, st.Found)
|
||||
assert.Equal(t, summary.New, st.New)
|
||||
assert.Equal(t, summary.Invalid, st.Invalid)
|
||||
assert.Equal(t, summary.Ignore, st.Ignore)
|
||||
assert.Equal(t, summary.Delete, st.Delete)
|
||||
assert.Equal(t, summary.DeleteList, st.DeleteList)
|
||||
assert.Equal(t, summary.Insert, st.Insert)
|
||||
assert.Equal(t, summary.InsertFailed, st.InsertFailed)
|
||||
assert.Equal(t, summary.Drop, st.Drop)
|
||||
assert.Equal(t, summary.EarlyDrop, st.EarlyDrop)
|
||||
assert.Equal(t, summary.IcmpError, st.IcmpError)
|
||||
assert.Equal(t, summary.ExpectNew, st.ExpectNew)
|
||||
assert.Equal(t, summary.ExpectCreate, st.ExpectCreate)
|
||||
assert.Equal(t, summary.ExpectDelete, st.ExpectDelete)
|
||||
assert.Equal(t, summary.SearchRestart, st.SearchRestart)
|
||||
|
||||
assert.Equal(t, 0, i) // Should only have one element
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,6 +156,14 @@ func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
|
|||
return nil, errors.New("NetFilterCounters not implemented for openbsd")
|
||||
}
|
||||
|
||||
func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
|
||||
return ConntrackStatsWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
// NetProtoCounters returns network statistics for the entire system
|
||||
// If protocols is empty then all protocols are returned, otherwise
|
||||
// just the protocols in the list are returned.
|
||||
|
|
|
@ -206,6 +206,15 @@ func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
|
|||
return nil, errors.New("NetFilterCounters not implemented for windows")
|
||||
}
|
||||
|
||||
func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
|
||||
return ConntrackStatsWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
|
||||
// NetProtoCounters returns network statistics for the entire system
|
||||
// If protocols is empty then all protocols are returned, otherwise
|
||||
// just the protocols in the list are returned.
|
||||
|
|
Loading…
Reference in New Issue