diff --git a/.gitignore b/.gitignore index 194eab89..d2b87e8e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *~ #* _obj -*.tmp \ No newline at end of file +*.tmp +.idea diff --git a/README.rst b/README.rst index a813e73b..3aeb5345 100644 --- a/README.rst +++ b/README.rst @@ -126,6 +126,11 @@ Several methods have been added which are not present in psutil, but will provid - system wide stats on network protocols (i.e IP, TCP, UDP, etc.) - sourced from /proc/net/snmp +- iptables nf_conntrack (linux only) + + - system wide stats on netfilter conntrack module + - sourced from /proc/sys/net/netfilter/nf_conntrack_count + Some codes are ported from Ohai. many thanks. @@ -155,6 +160,7 @@ net_connections x x net_protocols x net_if_addrs net_if_stats +netfilter_conntrack x ================= ====== ======= ====== ======= Process class diff --git a/internal/common/common.go b/internal/common/common.go index aeadda6b..3449cdd0 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -141,6 +141,33 @@ func ByteToString(orig []byte) string { return string(orig[l:n]) } +// ReadInts reads contents from single line file and returns them as []int32. +func ReadInts(filename string) ([]int64, error) { + f, err := os.Open(filename) + if err != nil { + return []int64{}, err + } + defer f.Close() + + var ret []int64 + + r := bufio.NewReader(f) + + // The int files that this is concerned with should only be one liners. + line, err := r.ReadString('\n') + if err != nil { + return []int64{}, err + } + + i, err := strconv.ParseInt(strings.Trim(line, "\n"), 10, 32) + if err != nil { + return []int64{}, err + } + ret = append(ret, i) + + return ret, nil +} + // Parse to int32 without error func mustParseInt32(val string) int32 { vv, _ := strconv.ParseInt(val, 10, 32) diff --git a/net/net.go b/net/net.go index 61f9abf0..66d5991b 100644 --- a/net/net.go +++ b/net/net.go @@ -64,6 +64,11 @@ type NetInterfaceStat struct { Addrs []NetInterfaceAddr `json:"addrs"` } +type NetFilterStat struct { + ConnTrackCount int64 `json:"conntrack_count"` + ConnTrackMax int64 `json:"conntrack_max"` +} + var constMap = map[string]int{ "TCP": syscall.SOCK_STREAM, "UDP": syscall.SOCK_DGRAM, diff --git a/net/net_linux.go b/net/net_linux.go index a091ee5c..ef06814d 100644 --- a/net/net_linux.go +++ b/net/net_linux.go @@ -160,3 +160,31 @@ func NetProtoCounters(protocols []string) ([]NetProtoCountersStat, error) { } return stats, nil } + +// NetFilterCounters returns iptables conntrack statistics +// the currently in use conntrack count and the max. +// If the file does not exist or is invalid it will return nil. +func NetFilterCounters() ([]NetFilterStat, error) { + countfile := common.HostProc("sys/net/netfilter/nf_conntrack_count") + maxfile := common.HostProc("sys/net/netfilter/nf_conntrack_max") + + count, err := common.ReadInts(countfile) + + if err != nil { + return nil, err + } + stats := make([]NetFilterStat, 0, 1) + + max, err := common.ReadInts(maxfile) + if err != nil { + return nil, err + } + + payload := NetFilterStat{ + ConnTrackCount: count[0], + ConnTrackMax: max[0], + } + + stats = append(stats, payload) + return stats, nil +} diff --git a/net/net_test.go b/net/net_test.go index 187c320d..0bac45fe 100644 --- a/net/net_test.go +++ b/net/net_test.go @@ -196,3 +196,23 @@ func TestNetConnections(t *testing.T) { } } + +func TestNetFilterCounters(t *testing.T) { + if ci := os.Getenv("CI"); ci != "" { // skip if test on drone.io + return + } + + v, err := NetFilterCounters() + if err != nil { + t.Errorf("could not get NetConnections: %v", err) + } + if len(v) == 0 { + t.Errorf("could not get NetConnections: %v", v) + } + for _, vv := range v { + if vv.ConnTrackMax == 0 { + t.Errorf("nf_conntrack_max needs to be greater than zero: %v", vv) + } + } + +}