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 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} 181