blob: 8c6de3791baf55220c92133d8878a54a7dc92c0c [file] [log] [blame]
khenaidood948f772021-08-11 17:49:24 -04001// Copyright 2020 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14package procfs
15
16import (
17 "bufio"
18 "bytes"
19 "fmt"
20 "strconv"
21 "strings"
22
23 "github.com/prometheus/procfs/internal/util"
24)
25
26// NetProtocolStats stores the contents from /proc/net/protocols
27type NetProtocolStats map[string]NetProtocolStatLine
28
29// NetProtocolStatLine contains a single line parsed from /proc/net/protocols. We
30// only care about the first six columns as the rest are not likely to change
31// and only serve to provide a set of capabilities for each protocol.
32type NetProtocolStatLine struct {
33 Name string // 0 The name of the protocol
34 Size uint64 // 1 The size, in bytes, of a given protocol structure. e.g. sizeof(struct tcp_sock) or sizeof(struct unix_sock)
35 Sockets int64 // 2 Number of sockets in use by this protocol
36 Memory int64 // 3 Number of 4KB pages allocated by all sockets of this protocol
37 Pressure int // 4 This is either yes, no, or NI (not implemented). For the sake of simplicity we treat NI as not experiencing memory pressure.
38 MaxHeader uint64 // 5 Protocol specific max header size
39 Slab bool // 6 Indicates whether or not memory is allocated from the SLAB
40 ModuleName string // 7 The name of the module that implemented this protocol or "kernel" if not from a module
41 Capabilities NetProtocolCapabilities
42}
43
44// NetProtocolCapabilities contains a list of capabilities for each protocol
45type NetProtocolCapabilities struct {
46 Close bool // 8
47 Connect bool // 9
48 Disconnect bool // 10
49 Accept bool // 11
50 IoCtl bool // 12
51 Init bool // 13
52 Destroy bool // 14
53 Shutdown bool // 15
54 SetSockOpt bool // 16
55 GetSockOpt bool // 17
56 SendMsg bool // 18
57 RecvMsg bool // 19
58 SendPage bool // 20
59 Bind bool // 21
60 BacklogRcv bool // 22
61 Hash bool // 23
62 UnHash bool // 24
63 GetPort bool // 25
64 EnterMemoryPressure bool // 26
65}
66
67// NetProtocols reads stats from /proc/net/protocols and returns a map of
68// PortocolStatLine entries. As of this writing no official Linux Documentation
69// exists, however the source is fairly self-explanatory and the format seems
70// stable since its introduction in 2.6.12-rc2
71// Linux 2.6.12-rc2 - https://elixir.bootlin.com/linux/v2.6.12-rc2/source/net/core/sock.c#L1452
72// Linux 5.10 - https://elixir.bootlin.com/linux/v5.10.4/source/net/core/sock.c#L3586
73func (fs FS) NetProtocols() (NetProtocolStats, error) {
74 data, err := util.ReadFileNoStat(fs.proc.Path("net/protocols"))
75 if err != nil {
76 return NetProtocolStats{}, err
77 }
78 return parseNetProtocols(bufio.NewScanner(bytes.NewReader(data)))
79}
80
81func parseNetProtocols(s *bufio.Scanner) (NetProtocolStats, error) {
82 nps := NetProtocolStats{}
83
84 // Skip the header line
85 s.Scan()
86
87 for s.Scan() {
88 line, err := nps.parseLine(s.Text())
89 if err != nil {
90 return NetProtocolStats{}, err
91 }
92
93 nps[line.Name] = *line
94 }
95 return nps, nil
96}
97
98func (ps NetProtocolStats) parseLine(rawLine string) (*NetProtocolStatLine, error) {
99 line := &NetProtocolStatLine{Capabilities: NetProtocolCapabilities{}}
100 var err error
101 const enabled = "yes"
102 const disabled = "no"
103
104 fields := strings.Fields(rawLine)
105 line.Name = fields[0]
106 line.Size, err = strconv.ParseUint(fields[1], 10, 64)
107 if err != nil {
108 return nil, err
109 }
110 line.Sockets, err = strconv.ParseInt(fields[2], 10, 64)
111 if err != nil {
112 return nil, err
113 }
114 line.Memory, err = strconv.ParseInt(fields[3], 10, 64)
115 if err != nil {
116 return nil, err
117 }
118 if fields[4] == enabled {
119 line.Pressure = 1
120 } else if fields[4] == disabled {
121 line.Pressure = 0
122 } else {
123 line.Pressure = -1
124 }
125 line.MaxHeader, err = strconv.ParseUint(fields[5], 10, 64)
126 if err != nil {
127 return nil, err
128 }
129 if fields[6] == enabled {
130 line.Slab = true
131 } else if fields[6] == disabled {
132 line.Slab = false
133 } else {
134 return nil, fmt.Errorf("unable to parse capability for protocol: %s", line.Name)
135 }
136 line.ModuleName = fields[7]
137
138 err = line.Capabilities.parseCapabilities(fields[8:])
139 if err != nil {
140 return nil, err
141 }
142
143 return line, nil
144}
145
146func (pc *NetProtocolCapabilities) parseCapabilities(capabilities []string) error {
147 // The capabilities are all bools so we can loop over to map them
148 capabilityFields := [...]*bool{
149 &pc.Close,
150 &pc.Connect,
151 &pc.Disconnect,
152 &pc.Accept,
153 &pc.IoCtl,
154 &pc.Init,
155 &pc.Destroy,
156 &pc.Shutdown,
157 &pc.SetSockOpt,
158 &pc.GetSockOpt,
159 &pc.SendMsg,
160 &pc.RecvMsg,
161 &pc.SendPage,
162 &pc.Bind,
163 &pc.BacklogRcv,
164 &pc.Hash,
165 &pc.UnHash,
166 &pc.GetPort,
167 &pc.EnterMemoryPressure,
168 }
169
170 for i := 0; i < len(capabilities); i++ {
171 if capabilities[i] == "y" {
172 *capabilityFields[i] = true
173 } else if capabilities[i] == "n" {
174 *capabilityFields[i] = false
175 } else {
176 return fmt.Errorf("unable to parse capability block for protocol: position %d", i)
177 }
178 }
179 return nil
180}