Repository: prometheus/procfs
Pull request #435 (2022-02-14 14:20:44)

Introduce parsing for `/proc/<pid>/net/snmp`, `/proc/<pid>/net/snmp6` and `/proc/<pid>/net/netstat`.

SuperQ: Nice, do we have to worry about missing fields in various kernel versions? We like to be able to support very old kernels in this library, since Go will run on kernels as old as 2.6.23.
nikakis: @SuperQ I believe according to [this RFC](https://datatracker.ietf.org/doc/rfc1213/) it seems that we will be fine.
SuperQ: Any chance I can also get you to implement snmp6 parsing in this PR too?
nikakis: Sure i will implement snmp6 and netstat also.
Maybe i can also do the refactoring on the node_exporter side when this is done.
nikakis: @SuperQ I added also snmp6 and netstat. Have a look please!

Patch:
diff --git a/fixtures.ttar b/fixtures.ttar
index 0c4f6a3..0fe1e08 100644
--- a/fixtures.ttar
+++ b/fixtures.ttar
@@ -156,6 +156,125 @@ Inter-|   Receive                                                |  Transmit
   eth0:     438       5    0    0    0     0          0         0      648       8    0    0    0     0       0          0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/26231/net/snmp
+Lines: 12
+Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequests OutDiscards OutNoRoutes ReasmTimeout ReasmReqds ReasmOKs ReasmFails FragOKs FragFails FragCreates
+Ip: 2 64 594223 0 1 0 0 0 593186 547253 20 231 0 0 0 0 0 0 0
+Icmp: InMsgs InErrors InCsumErrors InDestUnreachs InTimeExcds InParmProbs InSrcQuenchs InRedirects InEchos InEchoReps InTimestamps InTimestampReps InAddrMasks InAddrMaskReps OutMsgs OutErrors OutDestUnreachs OutTimeExcds OutParmProbs OutSrcQuenchs OutRedirects OutEchos OutEchoReps OutTimestamps OutTimestampReps OutAddrMasks OutAddrMaskReps
+Icmp: 45 1 0 45 0 0 0 0 0 0 0 0 0 0 50 0 50 0 0 0 0 0 0 0 0 0 0
+IcmpMsg: InType3 OutType3
+IcmpMsg: 45 50
+Tcp: RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpens PassiveOpens AttemptFails EstabResets CurrEstab InSegs OutSegs RetransSegs InErrs OutRsts InCsumErrors
+Tcp: 1 200 120000 -1 1103 9 8 51 15 653161 594855 348 98 1038 0
+Udp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors IgnoredMulti
+Udp: 10179 50 0 9846 0 0 0 58
+UdpLite: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors IgnoredMulti
+UdpLite: 0 0 0 0 0 0 0 0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/26231/net/netstat
+Lines: 4
+TcpExt: SyncookiesSent SyncookiesRecv SyncookiesFailed EmbryonicRsts PruneCalled RcvPruned OfoPruned OutOfWindowIcmps LockDroppedIcmps ArpFilter TW TWRecycled TWKilled PAWSActive PAWSEstab DelayedACKs DelayedACKLocked DelayedACKLost ListenOverflows ListenDrops TCPHPHits TCPPureAcks TCPHPAcks TCPRenoRecovery TCPSackRecovery TCPSACKReneging TCPSACKReorder TCPRenoReorder TCPTSReorder TCPFullUndo TCPPartialUndo TCPDSACKUndo TCPLossUndo TCPLostRetransmit TCPRenoFailures TCPSackFailures TCPLossFailures TCPFastRetrans TCPSlowStartRetrans TCPTimeouts TCPLossProbes TCPLossProbeRecovery TCPRenoRecoveryFail TCPSackRecoveryFail TCPRcvCollapsed TCPDSACKOldSent TCPDSACKOfoSent TCPDSACKRecv TCPDSACKOfoRecv TCPAbortOnData TCPAbortOnClose TCPAbortOnMemory TCPAbortOnTimeout TCPAbortOnLinger TCPAbortFailed TCPMemoryPressures TCPMemoryPressuresChrono TCPSACKDiscard TCPDSACKIgnoredOld TCPDSACKIgnoredNoUndo TCPSpuriousRTOs TCPMD5NotFound TCPMD5Unexpected TCPMD5Failure TCPSackShifted TCPSackMerged TCPSackShiftFallback TCPBacklogDrop PFMemallocDrop TCPMinTTLDrop TCPDeferAcceptDrop IPReversePathFilter TCPTimeWaitOverflow TCPReqQFullDoCookies TCPReqQFullDrop TCPRetransFail TCPRcvCoalesce TCPOFOQueue TCPOFODrop TCPOFOMerge TCPChallengeACK TCPSYNChallenge TCPFastOpenActive TCPFastOpenActiveFail TCPFastOpenPassive TCPFastOpenPassiveFail TCPFastOpenListenOverflow TCPFastOpenCookieReqd TCPFastOpenBlackhole TCPSpuriousRtxHostQueues BusyPollRxPackets TCPAutoCorking TCPFromZeroWindowAdv TCPToZeroWindowAdv TCPWantZeroWindowAdv TCPSynRetrans TCPOrigDataSent TCPHystartTrainDetect TCPHystartTrainCwnd TCPHystartDelayDetect TCPHystartDelayCwnd TCPACKSkippedSynRecv TCPACKSkippedPAWS TCPACKSkippedSeq TCPACKSkippedFinWait2 TCPACKSkippedTimeWait TCPACKSkippedChallenge TCPWinProbe TCPKeepAlive TCPMTUPFail TCPMTUPSuccess TCPWqueueTooBig
+TcpExt: 0 0 0 1 0 0 0 0 0 0 83 0 0 0 3640 287 1 7460 0 0 134193 1335 829 0 4 0 1 0 0 0 0 1 19 0 0 0 0 3 0 32 100 4 0 0 0 7460 2421 49 1 62 6 0 23 0 7 0 0 0 0 19 2 0 0 0 0 0 6 0 0 0 0 3 0 0 0 0 92425 65515 0 2421 4 4 0 0 0 0 0 0 0 0 0 10 0 0 0 16 2221 0 0 2 45 0 0 3 0 0 0 0 456 0 0 0
+IpExt: InNoRoutes InTruncatedPkts InMcastPkts OutMcastPkts InBcastPkts OutBcastPkts InOctets OutOctets InMcastOctets OutMcastOctets InBcastOctets OutBcastOctets InCsumErrors InNoECTPkts InECT1Pkts InECT0Pkts InCEPkts ReasmOverlaps
+IpExt: 0 0 208 214 118 111 190585481 7512674 26093 25903 14546 13628 0 134215 0 0 0 0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/26231/net/snmp6
+Lines: 92
+Ip6InReceives                   	92166
+Ip6InHdrErrors                  	0
+Ip6InTooBigErrors               	0
+Ip6InNoRoutes                   	0
+Ip6InAddrErrors                 	0
+Ip6InUnknownProtos              	0
+Ip6InTruncatedPkts              	0
+Ip6InDiscards                   	0
+Ip6InDelivers                   	92053
+Ip6OutForwDatagrams             	0
+Ip6OutRequests                  	57502
+Ip6OutDiscards                  	0
+Ip6OutNoRoutes                  	169
+Ip6ReasmTimeout                 	0
+Ip6ReasmReqds                   	0
+Ip6ReasmOKs                     	0
+Ip6ReasmFails                   	0
+Ip6FragOKs                      	0
+Ip6FragFails                    	0
+Ip6FragCreates                  	0
+Ip6InMcastPkts                  	381
+Ip6OutMcastPkts                 	148
+Ip6InOctets                     	113479132
+Ip6OutOctets                    	9842685
+Ip6InMcastOctets                	65971
+Ip6OutMcastOctets               	19394
+Ip6InBcastOctets                	0
+Ip6OutBcastOctets               	0
+Ip6InNoECTPkts                  	92166
+Ip6InECT1Pkts                   	0
+Ip6InECT0Pkts                   	0
+Ip6InCEPkts                     	0
+Icmp6InMsgs                     	142
+Icmp6InErrors                   	0
+Icmp6OutMsgs                    	58
+Icmp6OutErrors                  	0
+Icmp6InCsumErrors               	0
+Icmp6InDestUnreachs             	2
+Icmp6InPktTooBigs               	0
+Icmp6InTimeExcds                	0
+Icmp6InParmProblems             	0
+Icmp6InEchos                    	0
+Icmp6InEchoReplies              	0
+Icmp6InGroupMembQueries         	0
+Icmp6InGroupMembResponses       	0
+Icmp6InGroupMembReductions      	0
+Icmp6InRouterSolicits           	0
+Icmp6InRouterAdvertisements     	111
+Icmp6InNeighborSolicits         	26
+Icmp6InNeighborAdvertisements   	1
+Icmp6InRedirects                	0
+Icmp6InMLDv2Reports             	2
+Icmp6OutDestUnreachs            	0
+Icmp6OutPktTooBigs              	0
+Icmp6OutTimeExcds               	0
+Icmp6OutParmProblems            	0
+Icmp6OutEchos                   	0
+Icmp6OutEchoReplies             	0
+Icmp6OutGroupMembQueries        	0
+Icmp6OutGroupMembResponses      	0
+Icmp6OutGroupMembReductions     	0
+Icmp6OutRouterSolicits          	2
+Icmp6OutRouterAdvertisements    	0
+Icmp6OutNeighborSolicits        	5
+Icmp6OutNeighborAdvertisements  	26
+Icmp6OutRedirects               	0
+Icmp6OutMLDv2Reports            	25
+Icmp6InType1                    	2
+Icmp6InType134                  	111
+Icmp6InType135                  	26
+Icmp6InType136                  	1
+Icmp6InType143                  	2
+Icmp6OutType133                 	2
+Icmp6OutType135                 	5
+Icmp6OutType136                 	26
+Icmp6OutType143                 	25
+Udp6InDatagrams                 	2016
+Udp6NoPorts                     	0
+Udp6InErrors                    	0
+Udp6OutDatagrams                	1546
+Udp6RcvbufErrors                	0
+Udp6SndbufErrors                	0
+Udp6InCsumErrors                	0
+Udp6IgnoredMulti                	12
+UdpLite6InDatagrams             	0
+UdpLite6NoPorts                 	0
+UdpLite6InErrors                	0
+UdpLite6OutDatagrams            	0
+UdpLite6RcvbufErrors            	0
+UdpLite6SndbufErrors            	0
+UdpLite6InCsumErrors            	0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Directory: fixtures/proc/26231/ns
 Mode: 755
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/proc_netstat.go b/proc_netstat.go
new file mode 100644
index 0000000..c974b9b
--- /dev/null
+++ b/proc_netstat.go
@@ -0,0 +1,440 @@
+// Copyright 2022 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"io"
+	"strconv"
+	"strings"
+
+	"github.com/prometheus/procfs/internal/util"
+)
+
+// ProcNetstat models the content of /proc/<pid>/net/netstat.
+type ProcNetstat struct {
+	// The process ID.
+	PID int
+	TcpExt
+	IpExt
+}
+
+type TcpExt struct {
+	SyncookiesSent            float64
+	SyncookiesRecv            float64
+	SyncookiesFailed          float64
+	EmbryonicRsts             float64
+	PruneCalled               float64
+	RcvPruned                 float64
+	OfoPruned                 float64
+	OutOfWindowIcmps          float64
+	LockDroppedIcmps          float64
+	ArpFilter                 float64
+	TW                        float64
+	TWRecycled                float64
+	TWKilled                  float64
+	PAWSActive                float64
+	PAWSEstab                 float64
+	DelayedACKs               float64
+	DelayedACKLocked          float64
+	DelayedACKLost            float64
+	ListenOverflows           float64
+	ListenDrops               float64
+	TCPHPHits                 float64
+	TCPPureAcks               float64
+	TCPHPAcks                 float64
+	TCPRenoRecovery           float64
+	TCPSackRecovery           float64
+	TCPSACKReneging           float64
+	TCPSACKReorder            float64
+	TCPRenoReorder            float64
+	TCPTSReorder              float64
+	TCPFullUndo               float64
+	TCPPartialUndo            float64
+	TCPDSACKUndo              float64
+	TCPLossUndo               float64
+	TCPLostRetransmit         float64
+	TCPRenoFailures           float64
+	TCPSackFailures           float64
+	TCPLossFailures           float64
+	TCPFastRetrans            float64
+	TCPSlowStartRetrans       float64
+	TCPTimeouts               float64
+	TCPLossProbes             float64
+	TCPLossProbeRecovery      float64
+	TCPRenoRecoveryFail       float64
+	TCPSackRecoveryFail       float64
+	TCPRcvCollapsed           float64
+	TCPDSACKOldSent           float64
+	TCPDSACKOfoSent           float64
+	TCPDSACKRecv              float64
+	TCPDSACKOfoRecv           float64
+	TCPAbortOnData            float64
+	TCPAbortOnClose           float64
+	TCPAbortOnMemory          float64
+	TCPAbortOnTimeout         float64
+	TCPAbortOnLinger          float64
+	TCPAbortFailed            float64
+	TCPMemoryPressures        float64
+	TCPMemoryPressuresChrono  float64
+	TCPSACKDiscard            float64
+	TCPDSACKIgnoredOld        float64
+	TCPDSACKIgnoredNoUndo     float64
+	TCPSpuriousRTOs           float64
+	TCPMD5NotFound            float64
+	TCPMD5Unexpected          float64
+	TCPMD5Failure             float64
+	TCPSackShifted            float64
+	TCPSackMerged             float64
+	TCPSackShiftFallback      float64
+	TCPBacklogDrop            float64
+	PFMemallocDrop            float64
+	TCPMinTTLDrop             float64
+	TCPDeferAcceptDrop        float64
+	IPReversePathFilter       float64
+	TCPTimeWaitOverflow       float64
+	TCPReqQFullDoCookies      float64
+	TCPReqQFullDrop           float64
+	TCPRetransFail            float64
+	TCPRcvCoalesce            float64
+	TCPOFOQueue               float64
+	TCPOFODrop                float64
+	TCPOFOMerge               float64
+	TCPChallengeACK           float64
+	TCPSYNChallenge           float64
+	TCPFastOpenActive         float64
+	TCPFastOpenActiveFail     float64
+	TCPFastOpenPassive        float64
+	TCPFastOpenPassiveFail    float64
+	TCPFastOpenListenOverflow float64
+	TCPFastOpenCookieReqd     float64
+	TCPFastOpenBlackhole      float64
+	TCPSpuriousRtxHostQueues  float64
+	BusyPollRxPackets         float64
+	TCPAutoCorking            float64
+	TCPFromZeroWindowAdv      float64
+	TCPToZeroWindowAdv        float64
+	TCPWantZeroWindowAdv      float64
+	TCPSynRetrans             float64
+	TCPOrigDataSent           float64
+	TCPHystartTrainDetect     float64
+	TCPHystartTrainCwnd       float64
+	TCPHystartDelayDetect     float64
+	TCPHystartDelayCwnd       float64
+	TCPACKSkippedSynRecv      float64
+	TCPACKSkippedPAWS         float64
+	TCPACKSkippedSeq          float64
+	TCPACKSkippedFinWait2     float64
+	TCPACKSkippedTimeWait     float64
+	TCPACKSkippedChallenge    float64
+	TCPWinProbe               float64
+	TCPKeepAlive              float64
+	TCPMTUPFail               float64
+	TCPMTUPSuccess            float64
+	TCPWqueueTooBig           float64
+}
+
+type IpExt struct {
+	InNoRoutes      float64
+	InTruncatedPkts float64
+	InMcastPkts     float64
+	OutMcastPkts    float64
+	InBcastPkts     float64
+	OutBcastPkts    float64
+	InOctets        float64
+	OutOctets       float64
+	InMcastOctets   float64
+	OutMcastOctets  float64
+	InBcastOctets   float64
+	OutBcastOctets  float64
+	InCsumErrors    float64
+	InNoECTPkts     float64
+	InECT1Pkts      float64
+	InECT0Pkts      float64
+	InCEPkts        float64
+	ReasmOverlaps   float64
+}
+
+func (p Proc) Netstat() (ProcNetstat, error) {
+	filename := p.path("net/netstat")
+	data, err := util.ReadFileNoStat(filename)
+	if err != nil {
+		return ProcNetstat{PID: p.PID}, err
+	}
+	procNetstat, err := parseNetstat(bytes.NewReader(data), filename)
+	procNetstat.PID = p.PID
+	return procNetstat, err
+}
+
+// parseNetstat parses the metrics from proc/<pid>/net/netstat file
+// and returns a ProcNetstat structure.
+func parseNetstat(r io.Reader, fileName string) (ProcNetstat, error) {
+	var (
+		scanner  = bufio.NewScanner(r)
+		procNetstat = ProcNetstat{}
+	)
+
+	for scanner.Scan() {
+		nameParts := strings.Split(scanner.Text(), " ")
+		scanner.Scan()
+		valueParts := strings.Split(scanner.Text(), " ")
+		// Remove trailing :.
+		protocol := strings.TrimSuffix(nameParts[0], ":")
+		if len(nameParts) != len(valueParts) {
+			return procNetstat, fmt.Errorf("mismatch field count mismatch in %s: %s",
+				fileName, protocol)
+		}
+		for i := 1; i < len(nameParts); i++ {
+			value, err := strconv.ParseFloat(valueParts[i], 64)
+			if err != nil {
+				return procNetstat, err
+			}
+			key := nameParts[i]
+
+			switch protocol {
+			case "TcpExt":
+				switch key {
+				case "SyncookiesSent":
+					procNetstat.TcpExt.SyncookiesSent = value
+				case "SyncookiesRecv":
+					procNetstat.TcpExt.SyncookiesRecv = value
+				case "SyncookiesFailed":
+					procNetstat.TcpExt.SyncookiesFailed = value
+				case "EmbryonicRsts":
+					procNetstat.TcpExt.EmbryonicRsts = value
+				case "PruneCalled":
+					procNetstat.TcpExt.PruneCalled = value
+				case "RcvPruned":
+					procNetstat.TcpExt.RcvPruned = value
+				case "OfoPruned":
+					procNetstat.TcpExt.OfoPruned = value
+				case "OutOfWindowIcmps":
+					procNetstat.TcpExt.OutOfWindowIcmps = value
+				case "LockDroppedIcmps":
+					procNetstat.TcpExt.LockDroppedIcmps = value
+				case "ArpFilter":
+					procNetstat.TcpExt.ArpFilter = value
+				case "TW":
+					procNetstat.TcpExt.TW = value
+				case "TWRecycled":
+					procNetstat.TcpExt.TWRecycled = value
+				case "TWKilled":
+					procNetstat.TcpExt.TWKilled = value
+				case "PAWSActive":
+					procNetstat.TcpExt.PAWSActive = value
+				case "PAWSEstab":
+					procNetstat.TcpExt.PAWSEstab = value
+				case "DelayedACKs":
+					procNetstat.TcpExt.DelayedACKs = value
+				case "DelayedACKLocked":
+					procNetstat.TcpExt.DelayedACKLocked = value
+				case "DelayedACKLost":
+					procNetstat.TcpExt.DelayedACKLost = value
+				case "ListenOverflows":
+					procNetstat.TcpExt.ListenOverflows = value
+				case "ListenDrops":
+					procNetstat.TcpExt.ListenDrops = value
+				case "TCPHPHits":
+					procNetstat.TcpExt.TCPHPHits = value
+				case "TCPPureAcks":
+					procNetstat.TcpExt.TCPPureAcks = value
+				case "TCPHPAcks":
+					procNetstat.TcpExt.TCPHPAcks = value
+				case "TCPRenoRecovery":
+					procNetstat.TcpExt.TCPRenoRecovery = value
+				case "TCPSackRecovery":
+					procNetstat.TcpExt.TCPSackRecovery = value
+				case "TCPSACKReneging":
+					procNetstat.TcpExt.TCPSACKReneging = value
+				case "TCPSACKReorder":
+					procNetstat.TcpExt.TCPSACKReorder = value
+				case "TCPRenoReorder":
+					procNetstat.TcpExt.TCPRenoReorder = value
+				case "TCPTSReorder":
+					procNetstat.TcpExt.TCPTSReorder = value
+				case "TCPFullUndo":
+					procNetstat.TcpExt.TCPFullUndo = value
+				case "TCPPartialUndo":
+					procNetstat.TcpExt.TCPPartialUndo = value
+				case "TCPDSACKUndo":
+					procNetstat.TcpExt.TCPDSACKUndo = value
+				case "TCPLossUndo":
+					procNetstat.TcpExt.TCPLossUndo = value
+				case "TCPLostRetransmit":
+					procNetstat.TcpExt.TCPLostRetransmit = value
+				case "TCPRenoFailures":
+					procNetstat.TcpExt.TCPRenoFailures = value
+				case "TCPSackFailures":
+					procNetstat.TcpExt.TCPSackFailures = value
+				case "TCPLossFailures":
+					procNetstat.TcpExt.TCPLossFailures = value
+				case "TCPFastRetrans":
+					procNetstat.TcpExt.TCPFastRetrans = value
+				case "TCPSlowStartRetrans":
+					procNetstat.TcpExt.TCPSlowStartRetrans = value
+				case "TCPTimeouts":
+					procNetstat.TcpExt.TCPTimeouts = value
+				case "TCPLossProbes":
+					procNetstat.TcpExt.TCPLossProbes = value
+				case "TCPLossProbeRecovery":
+					procNetstat.TcpExt.TCPLossProbeRecovery = value
+				case "TCPRenoRecoveryFail":
+					procNetstat.TcpExt.TCPRenoRecoveryFail = value
+				case "TCPSackRecoveryFail":
+					procNetstat.TcpExt.TCPSackRecoveryFail = value
+				case "TCPRcvCollapsed":
+					procNetstat.TcpExt.TCPRcvCollapsed = value
+				case "TCPDSACKOldSent":
+					procNetstat.TcpExt.TCPDSACKOldSent = value
+				case "TCPDSACKOfoSent":
+					procNetstat.TcpExt.TCPDSACKOfoSent = value
+				case "TCPDSACKRecv":
+					procNetstat.TcpExt.TCPDSACKRecv = value
+				case "TCPDSACKOfoRecv":
+					procNetstat.TcpExt.TCPDSACKOfoRecv = value
+				case "TCPAbortOnData":
+					procNetstat.TcpExt.TCPAbortOnData = value
+				case "TCPAbortOnClose":
+					procNetstat.TcpExt.TCPAbortOnClose = value
+				case "TCPDeferAcceptDrop":
+					procNetstat.TcpExt.TCPDeferAcceptDrop = value
+				case "IPReversePathFilter":
+					procNetstat.TcpExt.IPReversePathFilter = value
+				case "TCPTimeWaitOverflow":
+					procNetstat.TcpExt.TCPTimeWaitOverflow = value
+				case "TCPReqQFullDoCookies":
+					procNetstat.TcpExt.TCPReqQFullDoCookies = value
+				case "TCPReqQFullDrop":
+					procNetstat.TcpExt.TCPReqQFullDrop = value
+				case "TCPRetransFail":
+					procNetstat.TcpExt.TCPRetransFail = value
+				case "TCPRcvCoalesce":
+					procNetstat.TcpExt.TCPRcvCoalesce = value
+				case "TCPOFOQueue":
+					procNetstat.TcpExt.TCPOFOQueue = value
+				case "TCPOFODrop":
+					procNetstat.TcpExt.TCPOFODrop = value
+				case "TCPOFOMerge":
+					procNetstat.TcpExt.TCPOFOMerge = value
+				case "TCPChallengeACK":
+					procNetstat.TcpExt.TCPChallengeACK = value
+				case "TCPSYNChallenge":
+					procNetstat.TcpExt.TCPSYNChallenge = value
+				case "TCPFastOpenActive":
+					procNetstat.TcpExt.TCPFastOpenActive = value
+				case "TCPFastOpenActiveFail":
+					procNetstat.TcpExt.TCPFastOpenActiveFail = value
+				case "TCPFastOpenPassive":
+					procNetstat.TcpExt.TCPFastOpenPassive = value
+				case "TCPFastOpenPassiveFail":
+					procNetstat.TcpExt.TCPFastOpenPassiveFail = value
+				case "TCPFastOpenListenOverflow":
+					procNetstat.TcpExt.TCPFastOpenListenOverflow = value
+				case "TCPFastOpenCookieReqd":
+					procNetstat.TcpExt.TCPFastOpenCookieReqd = value
+				case "TCPFastOpenBlackhole":
+					procNetstat.TcpExt.TCPFastOpenBlackhole = value
+				case "TCPSpuriousRtxHostQueues":
+					procNetstat.TcpExt.TCPSpuriousRtxHostQueues = value
+				case "BusyPollRxPackets":
+					procNetstat.TcpExt.BusyPollRxPackets = value
+				case "TCPAutoCorking":
+					procNetstat.TcpExt.TCPAutoCorking = value
+				case "TCPFromZeroWindowAdv":
+					procNetstat.TcpExt.TCPFromZeroWindowAdv = value
+				case "TCPToZeroWindowAdv":
+					procNetstat.TcpExt.TCPToZeroWindowAdv = value
+				case "TCPWantZeroWindowAdv":
+					procNetstat.TcpExt.TCPWantZeroWindowAdv = value
+				case "TCPSynRetrans":
+					procNetstat.TcpExt.TCPSynRetrans = value
+				case "TCPOrigDataSent":
+					procNetstat.TcpExt.TCPOrigDataSent = value
+				case "TCPHystartTrainDetect":
+					procNetstat.TcpExt.TCPHystartTrainDetect = value
+				case "TCPHystartTrainCwnd":
+					procNetstat.TcpExt.TCPHystartTrainCwnd = value
+				case "TCPHystartDelayDetect":
+					procNetstat.TcpExt.TCPHystartDelayDetect = value
+				case "TCPHystartDelayCwnd":
+					procNetstat.TcpExt.TCPHystartDelayCwnd = value
+				case "TCPACKSkippedSynRecv":
+					procNetstat.TcpExt.TCPACKSkippedSynRecv = value
+				case "TCPACKSkippedPAWS":
+					procNetstat.TcpExt.TCPACKSkippedPAWS = value
+				case "TCPACKSkippedSeq":
+					procNetstat.TcpExt.TCPACKSkippedSeq = value
+				case "TCPACKSkippedFinWait2":
+					procNetstat.TcpExt.TCPACKSkippedFinWait2 = value
+				case "TCPACKSkippedTimeWait":
+					procNetstat.TcpExt.TCPACKSkippedTimeWait = value
+				case "TCPACKSkippedChallenge":
+					procNetstat.TcpExt.TCPACKSkippedChallenge = value
+				case "TCPWinProbe":
+					procNetstat.TcpExt.TCPWinProbe = value
+				case "TCPKeepAlive":
+					procNetstat.TcpExt.TCPKeepAlive = value
+				case "TCPMTUPFail":
+					procNetstat.TcpExt.TCPMTUPFail = value
+				case "TCPMTUPSuccess":
+					procNetstat.TcpExt.TCPMTUPSuccess = value
+				case "TCPWqueueTooBig":
+					procNetstat.TcpExt.TCPWqueueTooBig = value
+				}
+			case "IpExt":
+				switch key {
+				case "InNoRoutes":
+					procNetstat.IpExt.InNoRoutes = value
+				case "InTruncatedPkts":
+					procNetstat.IpExt.InTruncatedPkts = value
+				case "InMcastPkts":
+					procNetstat.IpExt.InMcastPkts = value
+				case "OutMcastPkts":
+					procNetstat.IpExt.OutMcastPkts = value
+				case "InBcastPkts":
+					procNetstat.IpExt.InBcastPkts = value
+				case "OutBcastPkts":
+					procNetstat.IpExt.OutBcastPkts = value
+				case "InOctets":
+					procNetstat.IpExt.InOctets = value
+				case "OutOctets":
+					procNetstat.IpExt.OutOctets = value
+				case "InMcastOctets":
+					procNetstat.IpExt.InMcastOctets = value
+				case "OutMcastOctets":
+					procNetstat.IpExt.OutMcastOctets = value
+				case "InBcastOctets":
+					procNetstat.IpExt.InBcastOctets = value
+				case "OutBcastOctets":
+					procNetstat.IpExt.OutBcastOctets = value
+				case "InCsumErrors":
+					procNetstat.IpExt.InCsumErrors = value
+				case "InNoECTPkts":
+					procNetstat.IpExt.InNoECTPkts = value
+				case "InECT1Pkts":
+					procNetstat.IpExt.InECT1Pkts = value
+				case "InECT0Pkts":
+					procNetstat.IpExt.InECT0Pkts = value
+				case "InCEPkts":
+					procNetstat.IpExt.InCEPkts = value
+				case "ReasmOverlaps":
+					procNetstat.IpExt.ReasmOverlaps = value
+				}
+			}
+		}
+	}
+	return procNetstat, scanner.Err()
+}
diff --git a/proc_snmp.go b/proc_snmp.go
new file mode 100644
index 0000000..85438cd
--- /dev/null
+++ b/proc_snmp.go
@@ -0,0 +1,353 @@
+// Copyright 2022 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"io"
+	"strconv"
+	"strings"
+
+	"github.com/prometheus/procfs/internal/util"
+)
+
+// ProcSnmp models the content of /proc/<pid>/net/snmp.
+type ProcSnmp struct {
+	// The process ID.
+	PID int
+	Ip
+	Icmp
+	IcmpMsg
+	Tcp
+	Udp
+	UdpLite
+}
+
+type Ip struct {
+	Forwarding      float64
+	DefaultTTL      float64
+	InReceives      float64
+	InHdrErrors     float64
+	InAddrErrors    float64
+	ForwDatagrams   float64
+	InUnknownProtos float64
+	InDiscards      float64
+	InDelivers      float64
+	OutRequests     float64
+	OutDiscards     float64
+	OutNoRoutes     float64
+	ReasmTimeout    float64
+	ReasmReqds      float64
+	ReasmOKs        float64
+	ReasmFails      float64
+	FragOKs         float64
+	FragFails       float64
+	FragCreates     float64
+}
+
+type Icmp struct {
+	InMsgs           float64
+	InErrors         float64
+	InCsumErrors     float64
+	InDestUnreachs   float64
+	InTimeExcds      float64
+	InParmProbs      float64
+	InSrcQuenchs     float64
+	InRedirects      float64
+	InEchos          float64
+	InEchoReps       float64
+	InTimestamps     float64
+	InTimestampReps  float64
+	InAddrMasks      float64
+	InAddrMaskReps   float64
+	OutMsgs          float64
+	OutErrors        float64
+	OutDestUnreachs  float64
+	OutTimeExcds     float64
+	OutParmProbs     float64
+	OutSrcQuenchs    float64
+	OutRedirects     float64
+	OutEchos         float64
+	OutEchoReps      float64
+	OutTimestamps    float64
+	OutTimestampReps float64
+	OutAddrMasks     float64
+	OutAddrMaskReps  float64
+}
+
+type IcmpMsg struct {
+	InType3  float64
+	OutType3 float64
+}
+
+type Tcp struct {
+	RtoAlgorithm float64
+	RtoMin       float64
+	RtoMax       float64
+	MaxConn      float64
+	ActiveOpens  float64
+	PassiveOpens float64
+	AttemptFails float64
+	EstabResets  float64
+	CurrEstab    float64
+	InSegs       float64
+	OutSegs      float64
+	RetransSegs  float64
+	InErrs       float64
+	OutRsts      float64
+	InCsumErrors float64
+}
+
+type Udp struct {
+	InDatagrams  float64
+	NoPorts      float64
+	InErrors     float64
+	OutDatagrams float64
+	RcvbufErrors float64
+	SndbufErrors float64
+	InCsumErrors float64
+	IgnoredMulti float64
+}
+
+type UdpLite struct {
+	InDatagrams  float64
+	NoPorts      float64
+	InErrors     float64
+	OutDatagrams float64
+	RcvbufErrors float64
+	SndbufErrors float64
+	InCsumErrors float64
+	IgnoredMulti float64
+}
+
+func (p Proc) Snmp() (ProcSnmp, error) {
+	filename := p.path("net/snmp")
+	data, err := util.ReadFileNoStat(filename)
+	if err != nil {
+		return ProcSnmp{PID: p.PID}, err
+	}
+	procSnmp, err := parseSnmp(bytes.NewReader(data), filename)
+	procSnmp.PID = p.PID
+	return procSnmp, err
+}
+
+// parseSnmp parses the metrics from proc/<pid>/net/snmp file
+// and returns a map contains those metrics (e.g. {"Ip": {"Forwarding": 2}}).
+func parseSnmp(r io.Reader, fileName string) (ProcSnmp, error) {
+	var (
+		scanner  = bufio.NewScanner(r)
+		procSnmp = ProcSnmp{}
+	)
+
+	for scanner.Scan() {
+		nameParts := strings.Split(scanner.Text(), " ")
+		scanner.Scan()
+		valueParts := strings.Split(scanner.Text(), " ")
+		// Remove trailing :.
+		protocol := strings.TrimSuffix(nameParts[0], ":")
+		if len(nameParts) != len(valueParts) {
+			return procSnmp, fmt.Errorf("mismatch field count mismatch in %s: %s",
+				fileName, protocol)
+		}
+		for i := 1; i < len(nameParts); i++ {
+			value, err := strconv.ParseFloat(valueParts[i], 64)
+			if err != nil {
+				return procSnmp, err
+			}
+			key := nameParts[i]
+
+			switch protocol {
+			case "Ip":
+				switch key {
+				case "Forwarding":
+					procSnmp.Ip.Forwarding = value
+				case "DefaultTTL":
+					procSnmp.Ip.DefaultTTL = value
+				case "InReceives":
+					procSnmp.Ip.InReceives = value
+				case "InHdrErrors":
+					procSnmp.Ip.InHdrErrors = value
+				case "InAddrErrors":
+					procSnmp.Ip.InAddrErrors = value
+				case "ForwDatagrams":
+					procSnmp.Ip.ForwDatagrams = value
+				case "InUnknownProtos":
+					procSnmp.Ip.InUnknownProtos = value
+				case "InDiscards":
+					procSnmp.Ip.InDiscards = value
+				case "InDelivers":
+					procSnmp.Ip.InDelivers = value
+				case "OutRequests":
+					procSnmp.Ip.OutRequests = value
+				case "OutDiscards":
+					procSnmp.Ip.OutDiscards = value
+				case "OutNoRoutes":
+					procSnmp.Ip.OutNoRoutes = value
+				case "ReasmTimeout":
+					procSnmp.Ip.ReasmTimeout = value
+				case "ReasmReqds":
+					procSnmp.Ip.ReasmReqds = value
+				case "ReasmOKs":
+					procSnmp.Ip.ReasmOKs = value
+				case "ReasmFails":
+					procSnmp.Ip.ReasmFails = value
+				case "FragOKs":
+					procSnmp.Ip.FragOKs = value
+				case "FragFails":
+					procSnmp.Ip.FragFails = value
+				case "FragCreates":
+					procSnmp.Ip.FragCreates = value
+				}
+			case "Icmp":
+				switch key {
+				case "InMsgs":
+					procSnmp.Icmp.InMsgs = value
+				case "InErrors":
+					procSnmp.Icmp.InErrors = value
+				case "InCsumErrors":
+					procSnmp.Icmp.InCsumErrors = value
+				case "InDestUnreachs":
+					procSnmp.Icmp.InDestUnreachs = value
+				case "InTimeExcds":
+					procSnmp.Icmp.InTimeExcds = value
+				case "InParmProbs":
+					procSnmp.Icmp.InParmProbs = value
+				case "InSrcQuenchs":
+					procSnmp.Icmp.InSrcQuenchs = value
+				case "InRedirects":
+					procSnmp.Icmp.InRedirects = value
+				case "InEchos":
+					procSnmp.Icmp.InEchos = value
+				case "InEchoReps":
+					procSnmp.Icmp.InEchoReps = value
+				case "InTimestamps":
+					procSnmp.Icmp.InTimestamps = value
+				case "InTimestampReps":
+					procSnmp.Icmp.InTimestampReps = value
+				case "InAddrMasks":
+					procSnmp.Icmp.InAddrMasks = value
+				case "InAddrMaskReps":
+					procSnmp.Icmp.InAddrMaskReps = value
+				case "OutMsgs":
+					procSnmp.Icmp.OutMsgs = value
+				case "OutErrors":
+					procSnmp.Icmp.OutErrors = value
+				case "OutDestUnreachs":
+					procSnmp.Icmp.OutDestUnreachs = value
+				case "OutTimeExcds":
+					procSnmp.Icmp.OutTimeExcds = value
+				case "OutParmProbs":
+					procSnmp.Icmp.OutParmProbs = value
+				case "OutSrcQuenchs":
+					procSnmp.Icmp.OutSrcQuenchs = value
+				case "OutRedirects":
+					procSnmp.Icmp.OutRedirects = value
+				case "OutEchos":
+					procSnmp.Icmp.OutEchos = value
+				case "OutEchoReps":
+					procSnmp.Icmp.OutEchoReps = value
+				case "OutTimestamps":
+					procSnmp.Icmp.OutTimestamps = value
+				case "OutTimestampReps":
+					procSnmp.Icmp.OutTimestampReps = value
+				case "OutAddrMasks":
+					procSnmp.Icmp.OutAddrMasks = value
+				case "OutAddrMaskReps":
+					procSnmp.Icmp.OutAddrMaskReps = value
+				}
+			case "IcmpMsg":
+				switch key {
+				case "InType3":
+					procSnmp.IcmpMsg.InType3 = value
+				case "OutType3":
+					procSnmp.IcmpMsg.OutType3 = value
+				}
+			case "Tcp":
+				switch key {
+				case "RtoAlgorithm":
+					procSnmp.Tcp.RtoAlgorithm = value
+				case "RtoMin":
+					procSnmp.Tcp.RtoMin = value
+				case "RtoMax":
+					procSnmp.Tcp.RtoMax = value
+				case "MaxConn":
+					procSnmp.Tcp.MaxConn = value
+				case "ActiveOpens":
+					procSnmp.Tcp.ActiveOpens = value
+				case "PassiveOpens":
+					procSnmp.Tcp.PassiveOpens = value
+				case "AttemptFails":
+					procSnmp.Tcp.AttemptFails = value
+				case "EstabResets":
+					procSnmp.Tcp.EstabResets = value
+				case "CurrEstab":
+					procSnmp.Tcp.CurrEstab = value
+				case "InSegs":
+					procSnmp.Tcp.InSegs = value
+				case "OutSegs":
+					procSnmp.Tcp.OutSegs = value
+				case "RetransSegs":
+					procSnmp.Tcp.RetransSegs = value
+				case "InErrs":
+					procSnmp.Tcp.InErrs = value
+				case "OutRsts":
+					procSnmp.Tcp.OutRsts = value
+				case "InCsumErrors":
+					procSnmp.Tcp.InCsumErrors = value
+				}
+			case "Udp":
+				switch key {
+				case "InDatagrams":
+					procSnmp.Udp.InDatagrams = value
+				case "NoPorts":
+					procSnmp.Udp.NoPorts = value
+				case "InErrors":
+					procSnmp.Udp.InErrors = value
+				case "OutDatagrams":
+					procSnmp.Udp.OutDatagrams = value
+				case "RcvbufErrors":
+					procSnmp.Udp.RcvbufErrors = value
+				case "SndbufErrors":
+					procSnmp.Udp.SndbufErrors = value
+				case "InCsumErrors":
+					procSnmp.Udp.InCsumErrors = value
+				case "IgnoredMulti":
+					procSnmp.Udp.IgnoredMulti = value
+				}
+			case "UdpLite":
+				switch key {
+				case "InDatagrams":
+					procSnmp.UdpLite.InDatagrams = value
+				case "NoPorts":
+					procSnmp.UdpLite.NoPorts = value
+				case "InErrors":
+					procSnmp.UdpLite.InErrors = value
+				case "OutDatagrams":
+					procSnmp.UdpLite.OutDatagrams = value
+				case "RcvbufErrors":
+					procSnmp.UdpLite.RcvbufErrors = value
+				case "SndbufErrors":
+					procSnmp.UdpLite.SndbufErrors = value
+				case "InCsumErrors":
+					procSnmp.UdpLite.InCsumErrors = value
+				case "IgnoredMulti":
+					procSnmp.UdpLite.IgnoredMulti = value
+				}
+			}
+		}
+	}
+	return procSnmp, scanner.Err()
+}
diff --git a/proc_snmp6.go b/proc_snmp6.go
new file mode 100644
index 0000000..9438995
--- /dev/null
+++ b/proc_snmp6.go
@@ -0,0 +1,381 @@
+// Copyright 2022 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import (
+	"bufio"
+	"bytes"
+	"errors"
+	"io"
+	"os"
+	"strconv"
+	"strings"
+
+	"github.com/prometheus/procfs/internal/util"
+)
+
+// ProcSnmp6 models the content of /proc/<pid>/net/snmp6.
+type ProcSnmp6 struct {
+	// The process ID.
+	PID int
+	Ip6
+	Icmp6
+	Udp6
+	UdpLite6
+}
+
+type Ip6 struct {
+	InReceives       float64
+	InHdrErrors      float64
+	InTooBigErrors   float64
+	InNoRoutes       float64
+	InAddrErrors     float64
+	InUnknownProtos  float64
+	InTruncatedPkts  float64
+	InDiscards       float64
+	InDelivers       float64
+	OutForwDatagrams float64
+	OutRequests      float64
+	OutDiscards      float64
+	OutNoRoutes      float64
+	ReasmTimeout     float64
+	ReasmReqds       float64
+	ReasmOKs         float64
+	ReasmFails       float64
+	FragOKs          float64
+	FragFails        float64
+	FragCreates      float64
+	InMcastPkts      float64
+	OutMcastPkts     float64
+	InOctets         float64
+	OutOctets        float64
+	InMcastOctets    float64
+	OutMcastOctets   float64
+	InBcastOctets    float64
+	OutBcastOctets   float64
+	InNoECTPkts      float64
+	InECT1Pkts       float64
+	InECT0Pkts       float64
+	InCEPkts         float64
+}
+
+type Icmp6 struct {
+	InMsgs                    float64
+	InErrors                  float64
+	OutMsgs                   float64
+	OutErrors                 float64
+	InCsumErrors              float64
+	InDestUnreachs            float64
+	InPktTooBigs              float64
+	InTimeExcds               float64
+	InParmProblems            float64
+	InEchos                   float64
+	InEchoReplies             float64
+	InGroupMembQueries        float64
+	InGroupMembResponses      float64
+	InGroupMembReductions     float64
+	InRouterSolicits          float64
+	InRouterAdvertisements    float64
+	InNeighborSolicits        float64
+	InNeighborAdvertisements  float64
+	InRedirects               float64
+	InMLDv2Reports            float64
+	OutDestUnreachs           float64
+	OutPktTooBigs             float64
+	OutTimeExcds              float64
+	OutParmProblems           float64
+	OutEchos                  float64
+	OutEchoReplies            float64
+	OutGroupMembQueries       float64
+	OutGroupMembResponses     float64
+	OutGroupMembReductions    float64
+	OutRouterSolicits         float64
+	OutRouterAdvertisements   float64
+	OutNeighborSolicits       float64
+	OutNeighborAdvertisements float64
+	OutRedirects              float64
+	OutMLDv2Reports           float64
+	InType1                   float64
+	InType134                 float64
+	InType135                 float64
+	InType136                 float64
+	InType143                 float64
+	OutType133                float64
+	OutType135                float64
+	OutType136                float64
+	OutType143                float64
+}
+
+type Udp6 struct {
+	InDatagrams  float64
+	NoPorts      float64
+	InErrors     float64
+	OutDatagrams float64
+	RcvbufErrors float64
+	SndbufErrors float64
+	InCsumErrors float64
+	IgnoredMulti float64
+}
+
+type UdpLite6 struct {
+	InDatagrams  float64
+	NoPorts      float64
+	InErrors     float64
+	OutDatagrams float64
+	RcvbufErrors float64
+	SndbufErrors float64
+	InCsumErrors float64
+}
+
+func (p Proc) Snmp6() (ProcSnmp6, error) {
+	filename := p.path("net/snmp6")
+	data, err := util.ReadFileNoStat(filename)
+	if err != nil {
+		// On systems with IPv6 disabled, this file won't exist.
+		// Do nothing.
+		if errors.Is(err, os.ErrNotExist) {
+			return ProcSnmp6{PID: p.PID}, nil
+		}
+
+		return ProcSnmp6{PID: p.PID}, err
+	}
+
+	procSnmp6, err := parseSNMP6Stats(bytes.NewReader(data))
+	procSnmp6.PID = p.PID
+	return procSnmp6, err
+}
+
+// parseSnmp6 parses the metrics from proc/<pid>/net/snmp6 file
+// and returns a map contains those metrics.
+func parseSNMP6Stats(r io.Reader) (ProcSnmp6, error) {
+	var (
+		scanner  = bufio.NewScanner(r)
+		procSnmp6 = ProcSnmp6{}
+	)
+
+	for scanner.Scan() {
+		stat := strings.Fields(scanner.Text())
+		if len(stat) < 2 {
+			continue
+		}
+		// Expect to have "6" in metric name, skip line otherwise
+		if sixIndex := strings.Index(stat[0], "6"); sixIndex != -1 {
+			protocol := stat[0][:sixIndex+1]
+			key := stat[0][sixIndex+1:]
+			value, err := strconv.ParseFloat(stat[1], 64)
+			if err != nil {
+				return procSnmp6, err
+			}
+
+			switch protocol {
+			case "Ip6":
+				switch key {
+				case "InReceives":
+					procSnmp6.Ip6.InReceives = value
+				case "InHdrErrors":
+					procSnmp6.Ip6.InHdrErrors = value
+				case "InTooBigErrors":
+					procSnmp6.Ip6.InTooBigErrors = value
+				case "InNoRoutes":
+					procSnmp6.Ip6.InNoRoutes = value
+				case "InAddrErrors":
+					procSnmp6.Ip6.InAddrErrors = value
+				case "InUnknownProtos":
+					procSnmp6.Ip6.InUnknownProtos = value
+				case "InTruncatedPkts":
+					procSnmp6.Ip6.InTruncatedPkts = value
+				case "InDiscards":
+					procSnmp6.Ip6.InDiscards = value
+				case "InDelivers":
+					procSnmp6.Ip6.InDelivers = value
+				case "OutForwDatagrams":
+					procSnmp6.Ip6.OutForwDatagrams = value
+				case "OutRequests":
+					procSnmp6.Ip6.OutRequests = value
+				case "OutDiscards":
+					procSnmp6.Ip6.OutDiscards = value
+				case "OutNoRoutes":
+					procSnmp6.Ip6.OutNoRoutes = value
+				case "ReasmTimeout":
+					procSnmp6.Ip6.ReasmTimeout = value
+				case "ReasmReqds":
+					procSnmp6.Ip6.ReasmReqds = value
+				case "ReasmOKs":
+					procSnmp6.Ip6.ReasmOKs = value
+				case "ReasmFails":
+					procSnmp6.Ip6.ReasmFails = value
+				case "FragOKs":
+					procSnmp6.Ip6.FragOKs = value
+				case "FragFails":
+					procSnmp6.Ip6.FragFails = value
+				case "FragCreates":
+					procSnmp6.Ip6.FragCreates = value
+				case "InMcastPkts":
+					procSnmp6.Ip6.InMcastPkts = value
+				case "OutMcastPkts":
+					procSnmp6.Ip6.OutMcastPkts = value
+				case "InOctets":
+					procSnmp6.Ip6.InOctets = value
+				case "OutOctets":
+					procSnmp6.Ip6.OutOctets = value
+				case "InMcastOctets":
+					procSnmp6.Ip6.InMcastOctets = value
+				case "OutMcastOctets":
+					procSnmp6.Ip6.OutMcastOctets = value
+				case "InBcastOctets":
+					procSnmp6.Ip6.InBcastOctets = value
+				case "OutBcastOctets":
+					procSnmp6.Ip6.OutBcastOctets = value
+				case "InNoECTPkts":
+					procSnmp6.Ip6.InNoECTPkts = value
+				case "InECT1Pkts":
+					procSnmp6.Ip6.InECT1Pkts = value
+				case "InECT0Pkts":
+					procSnmp6.Ip6.InECT0Pkts = value
+				case "InCEPkts":
+					procSnmp6.Ip6.InCEPkts = value
+
+				}
+			case "Icmp6":
+				switch key {
+				case "InMsgs":
+					procSnmp6.Icmp6.InMsgs = value
+				case "InErrors":
+					procSnmp6.Icmp6.InErrors = value
+				case "OutMsgs":
+					procSnmp6.Icmp6.OutMsgs = value
+				case "OutErrors":
+					procSnmp6.Icmp6.OutErrors = value
+				case "InCsumErrors":
+					procSnmp6.Icmp6.InCsumErrors = value
+				case "InDestUnreachs":
+					procSnmp6.Icmp6.InDestUnreachs = value
+				case "InPktTooBigs":
+					procSnmp6.Icmp6.InPktTooBigs = value
+				case "InTimeExcds":
+					procSnmp6.Icmp6.InTimeExcds = value
+				case "InParmProblems":
+					procSnmp6.Icmp6.InParmProblems = value
+				case "InEchos":
+					procSnmp6.Icmp6.InEchos = value
+				case "InEchoReplies":
+					procSnmp6.Icmp6.InEchoReplies = value
+				case "InGroupMembQueries":
+					procSnmp6.Icmp6.InGroupMembQueries = value
+				case "InGroupMembResponses":
+					procSnmp6.Icmp6.InGroupMembResponses = value
+				case "InGroupMembReductions":
+					procSnmp6.Icmp6.InGroupMembReductions = value
+				case "InRouterSolicits":
+					procSnmp6.Icmp6.InRouterSolicits = value
+				case "InRouterAdvertisements":
+					procSnmp6.Icmp6.InRouterAdvertisements = value
+				case "InNeighborSolicits":
+					procSnmp6.Icmp6.InNeighborSolicits = value
+				case "InNeighborAdvertisements":
+					procSnmp6.Icmp6.InNeighborAdvertisements = value
+				case "InRedirects":
+					procSnmp6.Icmp6.InRedirects = value
+				case "InMLDv2Reports":
+					procSnmp6.Icmp6.InMLDv2Reports = value
+				case "OutDestUnreachs":
+					procSnmp6.Icmp6.OutDestUnreachs = value
+				case "OutPktTooBigs":
+					procSnmp6.Icmp6.OutPktTooBigs = value
+				case "OutTimeExcds":
+					procSnmp6.Icmp6.OutTimeExcds = value
+				case "OutParmProblems":
+					procSnmp6.Icmp6.OutParmProblems = value
+				case "OutEchos":
+					procSnmp6.Icmp6.OutEchos = value
+				case "OutEchoReplies":
+					procSnmp6.Icmp6.OutEchoReplies = value
+				case "OutGroupMembQueries":
+					procSnmp6.Icmp6.OutGroupMembQueries = value
+				case "OutGroupMembResponses":
+					procSnmp6.Icmp6.OutGroupMembResponses = value
+				case "OutGroupMembReductions":
+					procSnmp6.Icmp6.OutGroupMembReductions = value
+				case "OutRouterSolicits":
+					procSnmp6.Icmp6.OutRouterSolicits = value
+				case "OutRouterAdvertisements":
+					procSnmp6.Icmp6.OutRouterAdvertisements = value
+				case "OutNeighborSolicits":
+					procSnmp6.Icmp6.OutNeighborSolicits = value
+				case "OutNeighborAdvertisements":
+					procSnmp6.Icmp6.OutNeighborAdvertisements = value
+				case "OutRedirects":
+					procSnmp6.Icmp6.OutRedirects = value
+				case "OutMLDv2Reports":
+					procSnmp6.Icmp6.OutMLDv2Reports = value
+				case "InType1":
+					procSnmp6.Icmp6.InType1 = value
+				case "InType134":
+					procSnmp6.Icmp6.InType134 = value
+				case "InType135":
+					procSnmp6.Icmp6.InType135 = value
+				case "InType136":
+					procSnmp6.Icmp6.InType136 = value
+				case "InType143":
+					procSnmp6.Icmp6.InType143 = value
+				case "OutType133":
+					procSnmp6.Icmp6.OutType133 = value
+				case "OutType135":
+					procSnmp6.Icmp6.OutType135 = value
+				case "OutType136":
+					procSnmp6.Icmp6.OutType136 = value
+				case "OutType143":
+					procSnmp6.Icmp6.OutType143 = value
+				}
+			case "Udp6":
+				switch key {
+				case "InDatagrams":
+					procSnmp6.Udp6.InDatagrams = value
+				case "NoPorts":
+					procSnmp6.Udp6.NoPorts = value
+				case "InErrors":
+					procSnmp6.Udp6.InErrors = value
+				case "OutDatagrams":
+					procSnmp6.Udp6.OutDatagrams = value
+				case "RcvbufErrors":
+					procSnmp6.Udp6.RcvbufErrors = value
+				case "SndbufErrors":
+					procSnmp6.Udp6.SndbufErrors = value
+				case "InCsumErrors":
+					procSnmp6.Udp6.InCsumErrors = value
+				case "IgnoredMulti":
+					procSnmp6.Udp6.IgnoredMulti = value
+				}
+			case "UdpLite6":
+				switch key {
+				case "InDatagrams":
+					procSnmp6.UdpLite6.InDatagrams = value
+				case "NoPorts":
+					procSnmp6.UdpLite6.NoPorts = value
+				case "InErrors":
+					procSnmp6.UdpLite6.InErrors = value
+				case "OutDatagrams":
+					procSnmp6.UdpLite6.OutDatagrams = value
+				case "RcvbufErrors":
+					procSnmp6.UdpLite6.RcvbufErrors = value
+				case "SndbufErrors":
+					procSnmp6.UdpLite6.SndbufErrors = value
+				case "InCsumErrors":
+					procSnmp6.UdpLite6.InCsumErrors = value
+				}
+			}
+		}
+	}
+	return procSnmp6, scanner.Err()
+}
diff --git a/net_xfrm.go b/xfrm.go
similarity index 96%
rename from net_xfrm.go
rename to xfrm.go
index f9d9d24..eed07c7 100644
--- a/net_xfrm.go
+++ b/xfrm.go
@@ -79,13 +79,10 @@ type XfrmStat struct {
 	// Policy is dead
 	XfrmOutPolDead int
 	// Policy Error
-	XfrmOutPolError int
-	// Forward routing of a packet is not allowed
-	XfrmFwdHdrError int
-	// State is invalid, perhaps expired
+	XfrmOutPolError     int
+	XfrmFwdHdrError     int
 	XfrmOutStateInvalid int
-	// State hasn’t been fully acquired before use
-	XfrmAcquireError int
+	XfrmAcquireError    int
 }
 
 // NewXfrmStat reads the xfrm_stat statistics.


Test patch:
diff --git a/proc_netstat_test.go b/proc_netstat_test.go
new file mode 100644
index 0000000..3a0ed31
--- /dev/null
+++ b/proc_netstat_test.go
@@ -0,0 +1,37 @@
+package procfs
+
+import (
+	"testing"
+)
+
+func TestProcNetstat(t *testing.T) {
+	p, err := getProcFixtures(t).Proc(26231)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	procNetstat, err := p.Netstat()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	for _, test := range []struct {
+		name string
+		want float64
+		have float64
+	}{
+		{name: "pid", want: 26231, have: float64(procNetstat.PID)},
+		{name: "TcpExt:SyncookiesSent", want: 0, have: procNetstat.TcpExt.SyncookiesSent},
+		{name: "TcpExt:EmbryonicRsts", want: 1, have: procNetstat.TcpExt.EmbryonicRsts},
+		{name: "TcpExt:TW", want: 83, have: procNetstat.TcpExt.TW},
+		{name: "TcpExt:PAWSEstab", want: 3640, have: procNetstat.TcpExt.PAWSEstab},
+
+		{name: "IpExt:InNoRoutes", want: 0, have: procNetstat.IpExt.InNoRoutes},
+		{name: "IpExt:InMcastPkts", want: 208, have: procNetstat.IpExt.InMcastPkts},
+		{name: "IpExt:OutMcastPkts", want: 214, have: procNetstat.IpExt.OutMcastPkts},
+	} {
+		if test.want != test.have {
+			t.Errorf("want %s %f, have %f", test.name, test.want, test.have)
+		}
+	}
+}
diff --git a/proc_snmp6_test.go b/proc_snmp6_test.go
new file mode 100644
index 0000000..c3a55bf
--- /dev/null
+++ b/proc_snmp6_test.go
@@ -0,0 +1,35 @@
+package procfs
+
+import "testing"
+
+func TestProcSnmp6(t *testing.T) {
+	p, err := getProcFixtures(t).Proc(26231)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	procSnmp6, err := p.Snmp6()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	for _, test := range []struct {
+		name string
+		want float64
+		have float64
+	}{
+		{name: "pid", want: 26231, have: float64(procSnmp6.PID)},
+		{name: "Ip6InReceives", want: 92166, have: procSnmp6.Ip6.InReceives},
+		{name: "Ip6InDelivers", want: 92053, have: procSnmp6.Ip6.InDelivers},
+		{name: "Ip6OutNoRoutes", want: 169, have: procSnmp6.Ip6.OutNoRoutes},
+		{name: "Ip6InOctets", want: 113479132, have: procSnmp6.Ip6.InOctets},
+		{name: "Icmp6InMsgs", want: 142, have: procSnmp6.Icmp6.InMsgs},
+		{name: "Udp6InDatagrams", want: 2016, have: procSnmp6.Udp6.InDatagrams},
+		{name: "UdpLite6InDatagrams", want: 0, have: procSnmp6.UdpLite6.InDatagrams},
+	} {
+		if test.want != test.have {
+			t.Errorf("want %s %f, have %f", test.name, test.want, test.have)
+		}
+	}
+
+}
diff --git a/proc_snmp_test.go b/proc_snmp_test.go
new file mode 100644
index 0000000..73c8a69
--- /dev/null
+++ b/proc_snmp_test.go
@@ -0,0 +1,52 @@
+// Copyright 2022 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import "testing"
+
+func TestProcSnmp(t *testing.T) {
+	p, err := getProcFixtures(t).Proc(26231)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	procSnmp, err := p.Snmp()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	for _, test := range []struct {
+		name string
+		want float64
+		have float64
+	}{
+		{name: "pid", want: 26231, have: float64(procSnmp.PID)},
+		{name: "IP:Forwarding", want: 2, have: procSnmp.Ip.Forwarding},
+		{name: "IP:DefaultTTL", want: 64, have: procSnmp.Ip.DefaultTTL},
+		{name: "Icmp:InMsgs", want: 45, have: procSnmp.Icmp.InMsgs},
+		{name: "IcmpMsg:InType3", want: 45, have: procSnmp.IcmpMsg.InType3},
+		{name: "IcmpMsg:OutType3", want: 50, have: procSnmp.IcmpMsg.OutType3},
+		{name: "TCP:RtoAlgorithm", want: 1, have: procSnmp.Tcp.RtoAlgorithm},
+		{name: "TCP:RtoMin", want: 200, have: procSnmp.Tcp.RtoMin},
+		{name: "Udp:InDatagrams", want: 10179, have: procSnmp.Udp.InDatagrams},
+		{name: "Udp:NoPorts", want: 50, have: procSnmp.Udp.NoPorts},
+		{name: "UdpLite:InDatagrams", want: 0, have: procSnmp.UdpLite.NoPorts},
+		{name: "UdpLite:NoPorts", want: 0, have: procSnmp.UdpLite.NoPorts},
+	} {
+		if test.want != test.have {
+			t.Errorf("want %s %f, have %f", test.name, test.want, test.have)
+		}
+	}
+
+}