1// Copyright 2018 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 "encoding/hex" 20 "errors" 21 "fmt" 22 "io" 23 "io/ioutil" 24 "net" 25 "os" 26 "strconv" 27 "strings" 28 29 "github.com/prometheus/procfs/internal/util" 30) 31 32// IPVSStats holds IPVS statistics, as exposed by the kernel in `/proc/net/ip_vs_stats`. 33type IPVSStats struct { 34 // Total count of connections. 35 Connections uint64 36 // Total incoming packages processed. 37 IncomingPackets uint64 38 // Total outgoing packages processed. 39 OutgoingPackets uint64 40 // Total incoming traffic. 41 IncomingBytes uint64 42 // Total outgoing traffic. 43 OutgoingBytes uint64 44} 45 46// IPVSBackendStatus holds current metrics of one virtual / real address pair. 47type IPVSBackendStatus struct { 48 // The local (virtual) IP address. 49 LocalAddress net.IP 50 // The remote (real) IP address. 51 RemoteAddress net.IP 52 // The local (virtual) port. 53 LocalPort uint16 54 // The remote (real) port. 55 RemotePort uint16 56 // The local firewall mark 57 LocalMark string 58 // The transport protocol (TCP, UDP). 59 Proto string 60 // The current number of active connections for this virtual/real address pair. 61 ActiveConn uint64 62 // The current number of inactive connections for this virtual/real address pair. 63 InactConn uint64 64 // The current weight of this virtual/real address pair. 65 Weight uint64 66} 67 68// IPVSStats reads the IPVS statistics from the specified `proc` filesystem. 69func (fs FS) IPVSStats() (IPVSStats, error) { 70 data, err := util.ReadFileNoStat(fs.proc.Path("net/ip_vs_stats")) 71 if err != nil { 72 return IPVSStats{}, err 73 } 74 75 return parseIPVSStats(bytes.NewReader(data)) 76} 77 78// parseIPVSStats performs the actual parsing of `ip_vs_stats`. 79func parseIPVSStats(r io.Reader) (IPVSStats, error) { 80 var ( 81 statContent []byte 82 statLines []string 83 statFields []string 84 stats IPVSStats 85 ) 86 87 statContent, err := ioutil.ReadAll(r) 88 if err != nil { 89 return IPVSStats{}, err 90 } 91 92 statLines = strings.SplitN(string(statContent), "\n", 4) 93 if len(statLines) != 4 { 94 return IPVSStats{}, errors.New("ip_vs_stats corrupt: too short") 95 } 96 97 statFields = strings.Fields(statLines[2]) 98 if len(statFields) != 5 { 99 return IPVSStats{}, errors.New("ip_vs_stats corrupt: unexpected number of fields") 100 } 101 102 stats.Connections, err = strconv.ParseUint(statFields[0], 16, 64) 103 if err != nil { 104 return IPVSStats{}, err 105 } 106 stats.IncomingPackets, err = strconv.ParseUint(statFields[1], 16, 64) 107 if err != nil { 108 return IPVSStats{}, err 109 } 110 stats.OutgoingPackets, err = strconv.ParseUint(statFields[2], 16, 64) 111 if err != nil { 112 return IPVSStats{}, err 113 } 114 stats.IncomingBytes, err = strconv.ParseUint(statFields[3], 16, 64) 115 if err != nil { 116 return IPVSStats{}, err 117 } 118 stats.OutgoingBytes, err = strconv.ParseUint(statFields[4], 16, 64) 119 if err != nil { 120 return IPVSStats{}, err 121 } 122 123 return stats, nil 124} 125 126// IPVSBackendStatus reads and returns the status of all (virtual,real) server pairs from the specified `proc` filesystem. 127func (fs FS) IPVSBackendStatus() ([]IPVSBackendStatus, error) { 128 file, err := os.Open(fs.proc.Path("net/ip_vs")) 129 if err != nil { 130 return nil, err 131 } 132 defer file.Close() 133 134 return parseIPVSBackendStatus(file) 135} 136 137func parseIPVSBackendStatus(file io.Reader) ([]IPVSBackendStatus, error) { 138 var ( 139 status []IPVSBackendStatus 140 scanner = bufio.NewScanner(file) 141 proto string 142 localMark string 143 localAddress net.IP 144 localPort uint16 145 err error 146 ) 147 148 for scanner.Scan() { 149 fields := strings.Fields(scanner.Text()) 150 if len(fields) == 0 { 151 continue 152 } 153 switch { 154 case fields[0] == "IP" || fields[0] == "Prot" || fields[1] == "RemoteAddress:Port": 155 continue 156 case fields[0] == "TCP" || fields[0] == "UDP": 157 if len(fields) < 2 { 158 continue 159 } 160 proto = fields[0] 161 localMark = "" 162 localAddress, localPort, err = parseIPPort(fields[1]) 163 if err != nil { 164 return nil, err 165 } 166 case fields[0] == "FWM": 167 if len(fields) < 2 { 168 continue 169 } 170 proto = fields[0] 171 localMark = fields[1] 172 localAddress = nil 173 localPort = 0 174 case fields[0] == "->": 175 if len(fields) < 6 { 176 continue 177 } 178 remoteAddress, remotePort, err := parseIPPort(fields[1]) 179 if err != nil { 180 return nil, err 181 } 182 weight, err := strconv.ParseUint(fields[3], 10, 64) 183 if err != nil { 184 return nil, err 185 } 186 activeConn, err := strconv.ParseUint(fields[4], 10, 64) 187 if err != nil { 188 return nil, err 189 } 190 inactConn, err := strconv.ParseUint(fields[5], 10, 64) 191 if err != nil { 192 return nil, err 193 } 194 status = append(status, IPVSBackendStatus{ 195 LocalAddress: localAddress, 196 LocalPort: localPort, 197 LocalMark: localMark, 198 RemoteAddress: remoteAddress, 199 RemotePort: remotePort, 200 Proto: proto, 201 Weight: weight, 202 ActiveConn: activeConn, 203 InactConn: inactConn, 204 }) 205 } 206 } 207 return status, nil 208} 209 210func parseIPPort(s string) (net.IP, uint16, error) { 211 var ( 212 ip net.IP 213 err error 214 ) 215 216 switch len(s) { 217 case 13: 218 ip, err = hex.DecodeString(s[0:8]) 219 if err != nil { 220 return nil, 0, err 221 } 222 case 46: 223 ip = net.ParseIP(s[1:40]) 224 if ip == nil { 225 return nil, 0, fmt.Errorf("invalid IPv6 address: %s", s[1:40]) 226 } 227 default: 228 return nil, 0, fmt.Errorf("unexpected IP:Port: %s", s) 229 } 230 231 portString := s[len(s)-4:] 232 if len(portString) != 4 { 233 return nil, 0, fmt.Errorf("unexpected port string format: %s", portString) 234 } 235 port, err := strconv.ParseUint(portString, 16, 16) 236 if err != nil { 237 return nil, 0, err 238 } 239 240 return ip, uint16(port), nil 241} 242