Support /proc/net/dev, /proc/net/route, /proc/net/snmp

Support for /proc/net/dev, /proc/net/route, and /proc/net/snmp [1]. This should enable the use of tools such as route[2].

[1] https://tools.ietf.org/html/rfc2013
[2] http://man7.org/linux/man-pages/man8/route.8.html

PiperOrigin-RevId: 276380008
diff --git a/tcpip/tcpip.go b/tcpip/tcpip.go
index 5dbfa42..3a8e56e 100644
--- a/tcpip/tcpip.go
+++ b/tcpip/tcpip.go
@@ -673,6 +673,11 @@
 	s.IncrementBy(1)
 }
 
+// Decrement minuses one to the counter.
+func (s *StatCounter) Decrement() {
+	s.IncrementBy(^uint64(0))
+}
+
 // Value returns the current value of the counter.
 func (s *StatCounter) Value() uint64 {
 	return atomic.LoadUint64(&s.count)
@@ -881,6 +886,15 @@
 	// successfully via Listen.
 	PassiveConnectionOpenings *StatCounter
 
+	// CurrentEstablished is the number of TCP connections for which the
+	// current state is either ESTABLISHED or CLOSE-WAIT.
+	CurrentEstablished *StatCounter
+
+	// EstablishedResets is the number of times TCP connections have made
+	// a direct transition to the CLOSED state from either the
+	// ESTABLISHED state or the CLOSE-WAIT state.
+	EstablishedResets *StatCounter
+
 	// ListenOverflowSynDrop is the number of times the listen queue overflowed
 	// and a SYN was dropped.
 	ListenOverflowSynDrop *StatCounter
diff --git a/tcpip/transport/tcp/accept.go b/tcpip/transport/tcp/accept.go
index c287325..72d711b 100644
--- a/tcpip/transport/tcp/accept.go
+++ b/tcpip/transport/tcp/accept.go
@@ -297,6 +297,7 @@
 		return nil, err
 	}
 	ep.mu.Lock()
+	ep.stack.Stats().TCP.CurrentEstablished.Increment()
 	ep.state = StateEstablished
 	ep.mu.Unlock()
 
@@ -519,6 +520,7 @@
 		n.tsOffset = 0
 
 		// Switch state to connected.
+		n.stack.Stats().TCP.CurrentEstablished.Increment()
 		n.state = StateEstablished
 
 		// Do the delivery in a separate goroutine so
diff --git a/tcpip/transport/tcp/connect.go b/tcpip/transport/tcp/connect.go
index d4625dd..1933f75 100644
--- a/tcpip/transport/tcp/connect.go
+++ b/tcpip/transport/tcp/connect.go
@@ -805,6 +805,10 @@
 func (e *endpoint) resetConnectionLocked(err *tcpip.Error) {
 	// Only send a reset if the connection is being aborted for a reason
 	// other than receiving a reset.
+	if e.state == StateEstablished || e.state == StateCloseWait {
+		e.stack.Stats().TCP.EstablishedResets.Increment()
+		e.stack.Stats().TCP.CurrentEstablished.Decrement()
+	}
 	e.state = StateError
 	e.HardError = err
 	if err != tcpip.ErrConnectionReset {
@@ -975,6 +979,8 @@
 			e.lastErrorMu.Unlock()
 
 			e.mu.Lock()
+			e.stack.Stats().TCP.EstablishedResets.Increment()
+			e.stack.Stats().TCP.CurrentEstablished.Decrement()
 			e.state = StateError
 			e.HardError = err
 
@@ -1005,7 +1011,10 @@
 
 	// Tell waiters that the endpoint is connected and writable.
 	e.mu.Lock()
-	e.state = StateEstablished
+	if e.state != StateEstablished {
+		e.stack.Stats().TCP.CurrentEstablished.Increment()
+		e.state = StateEstablished
+	}
 	drained := e.drainDone != nil
 	e.mu.Unlock()
 	if drained {
@@ -1166,6 +1175,8 @@
 	// Mark endpoint as closed.
 	e.mu.Lock()
 	if e.state != StateError {
+		e.stack.Stats().TCP.EstablishedResets.Increment()
+		e.stack.Stats().TCP.CurrentEstablished.Decrement()
 		e.state = StateClose
 	}
 	// Lock released below.
diff --git a/tcpip/transport/tcp/endpoint.go b/tcpip/transport/tcp/endpoint.go
index 3a4ece4..e667201 100644
--- a/tcpip/transport/tcp/endpoint.go
+++ b/tcpip/transport/tcp/endpoint.go
@@ -1729,6 +1729,7 @@
 		e.segmentQueue.mu.Unlock()
 		e.snd.updateMaxPayloadSize(int(e.route.MTU()), 0)
 		e.state = StateEstablished
+		e.stack.Stats().TCP.CurrentEstablished.Increment()
 	}
 
 	if run {
diff --git a/tcpip/transport/tcp/snd.go b/tcpip/transport/tcp/snd.go
index fa616b9..d4bad63 100644
--- a/tcpip/transport/tcp/snd.go
+++ b/tcpip/transport/tcp/snd.go
@@ -674,6 +674,7 @@
 		default:
 			s.ep.state = StateFinWait1
 		}
+		s.ep.stack.Stats().TCP.CurrentEstablished.Decrement()
 		s.ep.mu.Unlock()
 	} else {
 		// We're sending a non-FIN segment.