1 | // 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 |
|
---|
14 | package procfs
|
---|
15 |
|
---|
16 | import (
|
---|
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.
|
---|
27 | type 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.
|
---|
32 | type 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.
|
---|
45 | type 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
|
---|
73 | func (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 |
|
---|
81 | func 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 |
|
---|
98 | func (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 |
|
---|
146 | func (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 | }
|
---|