github.com/castai/kvisor@v1.7.1-0.20240516114728-b3572a2607b5/cmd/agent/daemon/netstats/netstats.go (about)

     1  package netstats
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"strconv"
     9  	"strings"
    10  )
    11  
    12  type InterfaceStats struct {
    13  	// The name of the interface.
    14  	Name string `json:"name"`
    15  	// Cumulative count of bytes received.
    16  	RxBytes uint64 `json:"rx_bytes"`
    17  	// Cumulative count of packets received.
    18  	RxPackets uint64 `json:"rx_packets"`
    19  	// Cumulative count of receive errors encountered.
    20  	RxErrors uint64 `json:"rx_errors"`
    21  	// Cumulative count of packets dropped while receiving.
    22  	RxDropped uint64 `json:"rx_dropped"`
    23  	// Cumulative count of bytes transmitted.
    24  	TxBytes uint64 `json:"tx_bytes"`
    25  	// Cumulative count of packets transmitted.
    26  	TxPackets uint64 `json:"tx_packets"`
    27  	// Cumulative count of transmit errors encountered.
    28  	TxErrors uint64 `json:"tx_errors"`
    29  	// Cumulative count of packets dropped while transmitting.
    30  	TxDropped uint64 `json:"tx_dropped"`
    31  }
    32  
    33  func NewReader(procDir string) *Reader {
    34  	return &Reader{procDir: procDir}
    35  }
    36  
    37  type Reader struct {
    38  	procDir string
    39  }
    40  
    41  func (r *Reader) Read(pid uint32) ([]InterfaceStats, error) {
    42  	netStatsFile := filepath.Join(r.procDir, strconv.Itoa(int(pid)), "/net/dev")
    43  
    44  	ifaceStats, err := scanInterfaceStats(netStatsFile)
    45  	if err != nil {
    46  		return []InterfaceStats{}, fmt.Errorf("reading network stats: %w", err)
    47  	}
    48  
    49  	return ifaceStats, nil
    50  }
    51  
    52  func scanInterfaceStats(netStatsFile string) ([]InterfaceStats, error) {
    53  	file, err := os.Open(netStatsFile)
    54  	if err != nil {
    55  		return nil, fmt.Errorf("opening %s: %w", netStatsFile, err)
    56  	}
    57  	defer file.Close()
    58  
    59  	scanner := bufio.NewScanner(file)
    60  
    61  	// Discard header lines
    62  	for i := 0; i < 2; i++ {
    63  		if b := scanner.Scan(); !b {
    64  			return nil, scanner.Err()
    65  		}
    66  	}
    67  
    68  	var stats []InterfaceStats
    69  	for scanner.Scan() {
    70  		line := scanner.Text()
    71  		line = strings.Replace(line, ":", "", -1)
    72  
    73  		fields := strings.Fields(line)
    74  		// If the format of the  line is invalid then don't trust any of the stats
    75  		// in this file.
    76  		if len(fields) != 17 {
    77  			return nil, fmt.Errorf("invalid interface stats line: %s", line)
    78  		}
    79  
    80  		devName := fields[0]
    81  		if isIgnoredDevice(devName) {
    82  			continue
    83  		}
    84  
    85  		i := InterfaceStats{
    86  			Name: devName,
    87  		}
    88  
    89  		statFields := append(fields[1:5], fields[9:13]...)
    90  		statPointers := []*uint64{
    91  			&i.RxBytes, &i.RxPackets, &i.RxErrors, &i.RxDropped,
    92  			&i.TxBytes, &i.TxPackets, &i.TxErrors, &i.TxDropped,
    93  		}
    94  
    95  		err := setInterfaceStatValues(statFields, statPointers)
    96  		if err != nil {
    97  			return nil, fmt.Errorf("cannot parse interface stats (%w): %v", err, line)
    98  		}
    99  
   100  		stats = append(stats, i)
   101  	}
   102  
   103  	return stats, nil
   104  }
   105  
   106  var ignoredDevicePrefixes = []string{"lo", "veth", "docker"}
   107  
   108  func isIgnoredDevice(ifName string) bool {
   109  	for _, prefix := range ignoredDevicePrefixes {
   110  		if strings.HasPrefix(strings.ToLower(ifName), prefix) {
   111  			return true
   112  		}
   113  	}
   114  	return false
   115  }
   116  
   117  func setInterfaceStatValues(fields []string, pointers []*uint64) error {
   118  	for i, v := range fields {
   119  		val, err := strconv.ParseUint(v, 10, 64)
   120  		if err != nil {
   121  			return err
   122  		}
   123  		*pointers[i] = val
   124  	}
   125  	return nil
   126  }