From 7716d96810498f18bc2a0bffd946bc1a2d7235be Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Tue, 3 Jan 2017 18:37:56 +0000 Subject: [PATCH 1/5] Report persistent connections in states other than ESTABLISHED This aligns the `/proc` connection tracking (persistent connections) with conntrack (short-lived connections). --- probe/endpoint/procspy/procnet.go | 12 +++--------- probe/endpoint/procspy/spy.go | 4 ---- probe/endpoint/procspy/spy_linux.go | 2 +- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/probe/endpoint/procspy/procnet.go b/probe/endpoint/procspy/procnet.go index 3eabd2838..ece88a6a6 100644 --- a/probe/endpoint/procspy/procnet.go +++ b/probe/endpoint/procspy/procnet.go @@ -9,17 +9,15 @@ import ( type ProcNet struct { b []byte c Connection - wantedState uint bytesLocal, bytesRemote [16]byte seen map[uint64]struct{} } // NewProcNet gives a new ProcNet parser. -func NewProcNet(b []byte, wantedState uint) *ProcNet { +func NewProcNet(b []byte) *ProcNet { return &ProcNet{ b: b, c: Connection{}, - wantedState: wantedState, seen: map[uint64]struct{}{}, } } @@ -40,16 +38,12 @@ again: } var ( - local, remote, state, inode []byte + local, remote, inode []byte ) _, b = nextField(b) // 'sl' column local, b = nextField(b) remote, b = nextField(b) - state, b = nextField(b) - if parseHex(state) != p.wantedState { - p.b = nextLine(b) - goto again - } + _, b = nextField(b) // 'st' column _, b = nextField(b) // 'tx_queue' column _, b = nextField(b) // 'rx_queue' column _, b = nextField(b) // 'tr' column diff --git a/probe/endpoint/procspy/spy.go b/probe/endpoint/procspy/spy.go index d59de62b4..b506d587d 100644 --- a/probe/endpoint/procspy/spy.go +++ b/probe/endpoint/procspy/spy.go @@ -7,10 +7,6 @@ import ( "net" ) -const ( - tcpEstablished = 1 // according to /include/net/tcp_states.h -) - // Connection is a (TCP) connection. The Proc struct might not be filled in. type Connection struct { Transport string diff --git a/probe/endpoint/procspy/spy_linux.go b/probe/endpoint/procspy/spy_linux.go index 61f189881..6966f852f 100644 --- a/probe/endpoint/procspy/spy_linux.go +++ b/probe/endpoint/procspy/spy_linux.go @@ -61,7 +61,7 @@ func (s *linuxScanner) Connections(processes bool) (ConnIter, error) { } return &pnConnIter{ - pn: NewProcNet(buf.Bytes(), tcpEstablished), + pn: NewProcNet(buf.Bytes()), buf: buf, procs: procs, }, nil From a8b4e65b5cea19bb2860c723f0c4bb341534f0bf Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Tue, 3 Jan 2017 22:55:28 +0000 Subject: [PATCH 2/5] Make linter happy --- probe/endpoint/procspy/procnet.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/probe/endpoint/procspy/procnet.go b/probe/endpoint/procspy/procnet.go index ece88a6a6..910ba6706 100644 --- a/probe/endpoint/procspy/procnet.go +++ b/probe/endpoint/procspy/procnet.go @@ -16,9 +16,9 @@ type ProcNet struct { // NewProcNet gives a new ProcNet parser. func NewProcNet(b []byte) *ProcNet { return &ProcNet{ - b: b, - c: Connection{}, - seen: map[uint64]struct{}{}, + b: b, + c: Connection{}, + seen: map[uint64]struct{}{}, } } From 99a7dc3b9a6e101163b4f81fef3e8e95b73bf9c2 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Tue, 3 Jan 2017 23:32:40 +0000 Subject: [PATCH 3/5] Fix tests --- probe/endpoint/procspy/procnet_internal_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/probe/endpoint/procspy/procnet_internal_test.go b/probe/endpoint/procspy/procnet_internal_test.go index 1dd7d7f45..6b34ad858 100644 --- a/probe/endpoint/procspy/procnet_internal_test.go +++ b/probe/endpoint/procspy/procnet_internal_test.go @@ -13,7 +13,7 @@ func TestProcNet(t *testing.T) { 2: 0100007F:0019 00000000:0000 01 00000000:00000000 00:00000000 00000000 0 0 10550 1 ffff8800a729b780 100 0 0 10 0 3: A12CF62E:E4D7 57FC1EC0:01BB 01 00000000:00000000 02:000006FA 00000000 1000 0 639474 2 ffff88007e75a740 48 4 26 10 -1 ` - p := NewProcNet([]byte(testString), tcpEstablished) + p := NewProcNet([]byte(testString)) expected := []Connection{ { LocalAddress: net.IP([]byte{0, 0, 0, 0}), @@ -64,7 +64,7 @@ func TestTransport6(t *testing.T) { 8: 4500032000BE692B8AE31EBD919D9D10:D61C 5014002A080805400000000015100000:01BB 01 00000000:00000000 02:00000045 00000000 1000 0 36856710 2 ffff88010b796080 22 4 30 8 7 ` - p := NewProcNet([]byte(testString), tcpEstablished) + p := NewProcNet([]byte(testString)) expected := []Connection{ { // state: 10, @@ -114,7 +114,7 @@ func TestTransportNonsense(t *testing.T) { 0: 00000000:A6C0 00000000:0000 01 000000 broken line ` - p := NewProcNet([]byte(testString), tcpEstablished) + p := NewProcNet([]byte(testString)) expected := []Connection{ { LocalAddress: net.IP([]byte{0, 0, 0, 0}), @@ -142,7 +142,7 @@ func TestProcNetFiltersDuplicates(t *testing.T) { 0: 00000000:A6C0 00000000:0000 01 00000000:00000000 00:00000000 00000000 105 0 5107 1 ffff8800a6aaf040 100 0 0 10 0 1: 00000000:A6C0 00000000:0000 01 00000000:00000000 00:00000000 00000000 105 0 5107 1 ffff8800a6aaf040 100 0 0 10 0 ` - p := NewProcNet([]byte(testString), tcpEstablished) + p := NewProcNet([]byte(testString)) expected := Connection{ LocalAddress: net.IP([]byte{0, 0, 0, 0}), LocalPort: 0xa6c0, From 89a0ab6799895a3d187aea3ab624aabe6c541e2e Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Wed, 4 Jan 2017 00:12:47 +0000 Subject: [PATCH 4/5] Fix test data and improve /proc/net/tcp header parsing The header checking code was unsafe because: 1. It was accessing the byteslice at [2] without ensuring a length >= 3 2. It was assuming that the indentation of the 'sl' header is always 2 (which seems to be the case in recent kernels https://github.com/torvalds/linux/blob/8f18e4d03ed8fa5e4a300c94550533bd8ce4ff9a/net/ipv4/tcp_ipv4.c#L2304 and https://github.com/torvalds/linux/blob/8f18e4d03ed8fa5e4a300c94550533bd8ce4ff9a/net/ipv6/tcp_ipv6.c#L1831 ) but it's more robust to simply trim the byteslice. --- probe/endpoint/procspy/procnet.go | 15 +++++++++------ probe/endpoint/procspy/procnet_internal_test.go | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/probe/endpoint/procspy/procnet.go b/probe/endpoint/procspy/procnet.go index 910ba6706..1946d5f6a 100644 --- a/probe/endpoint/procspy/procnet.go +++ b/probe/endpoint/procspy/procnet.go @@ -5,6 +5,9 @@ import ( "net" ) +// Used to check whether we are parsing a header line +var slHeader = []byte("sl") + // ProcNet is an iterator to parse /proc/net/tcp{,6} files. type ProcNet struct { b []byte @@ -31,16 +34,16 @@ again: } b := p.b - if p.b[2] == 's' { + var ( + sl, local, remote, inode []byte + ) + + sl, b = nextField(b) // 'sl' column + if bytes.Equal(sl, slHeader) { // Skip header p.b = nextLine(b) goto again } - - var ( - local, remote, inode []byte - ) - _, b = nextField(b) // 'sl' column local, b = nextField(b) remote, b = nextField(b) _, b = nextField(b) // 'st' column diff --git a/probe/endpoint/procspy/procnet_internal_test.go b/probe/endpoint/procspy/procnet_internal_test.go index 6b34ad858..876282678 100644 --- a/probe/endpoint/procspy/procnet_internal_test.go +++ b/probe/endpoint/procspy/procnet_internal_test.go @@ -59,7 +59,7 @@ func TestProcNet(t *testing.T) { func TestTransport6(t *testing.T) { // Abridged copy of my /proc/net/tcp6 - testString := ` sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout Inode + testString := ` sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout Inode 0: 00000000000000000000000000000000:19C8 00000000000000000000000000000000:0000 01 00000000:00000000 00:00000000 00000000 0 0 23661201 1 ffff880103fb4800 100 0 0 10 -1 8: 4500032000BE692B8AE31EBD919D9D10:D61C 5014002A080805400000000015100000:01BB 01 00000000:00000000 02:00000045 00000000 1000 0 36856710 2 ffff88010b796080 22 4 30 8 7 ` From 2be26e2be421463d34d8cd68bd11aa09738711c6 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Tue, 10 Jan 2017 15:35:32 +0000 Subject: [PATCH 5/5] Limit connections to established and half-closed --- probe/endpoint/procspy/procnet.go | 11 +++++++++-- probe/endpoint/procspy/spy.go | 8 ++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/probe/endpoint/procspy/procnet.go b/probe/endpoint/procspy/procnet.go index 1946d5f6a..3073ad2c3 100644 --- a/probe/endpoint/procspy/procnet.go +++ b/probe/endpoint/procspy/procnet.go @@ -35,7 +35,7 @@ again: b := p.b var ( - sl, local, remote, inode []byte + sl, local, remote, state, inode []byte ) sl, b = nextField(b) // 'sl' column @@ -46,7 +46,14 @@ again: } local, b = nextField(b) remote, b = nextField(b) - _, b = nextField(b) // 'st' column + state, b = nextField(b) + switch parseHex(state) { + // Only process established or half-closed connections + case tcpEstablished, tcpFinWait1, tcpFinWait2, tcpCloseWait: + default: + p.b = nextLine(b) + goto again + } _, b = nextField(b) // 'tx_queue' column _, b = nextField(b) // 'rx_queue' column _, b = nextField(b) // 'tr' column diff --git a/probe/endpoint/procspy/spy.go b/probe/endpoint/procspy/spy.go index b506d587d..7620e208a 100644 --- a/probe/endpoint/procspy/spy.go +++ b/probe/endpoint/procspy/spy.go @@ -7,6 +7,14 @@ import ( "net" ) +const ( + // according to /include/net/tcp_states.h + tcpEstablished = 1 + tcpFinWait1 = 4 + tcpFinWait2 = 5 + tcpCloseWait = 8 +) + // Connection is a (TCP) connection. The Proc struct might not be filled in. type Connection struct { Transport string