...

Source file src/github.com/prometheus/procfs/ipvs.go

Documentation: github.com/prometheus/procfs

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

View as plain text