// Copyright 2020 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package procfs import ( "bufio" "bytes" "fmt" "io" "strconv" "strings" "github.com/prometheus/procfs/internal/util" ) // A ConntrackStatEntry represents one line from net/stat/nf_conntrack // and contains netfilter conntrack statistics at one CPU core. type ConntrackStatEntry struct { Entries uint64 Found uint64 Invalid uint64 Ignore uint64 Insert uint64 InsertFailed uint64 Drop uint64 EarlyDrop uint64 SearchRestart uint64 } // ConntrackStat retrieves netfilter's conntrack statistics, split by CPU cores. func (fs FS) ConntrackStat() ([]ConntrackStatEntry, error) { return readConntrackStat(fs.proc.Path("net", "stat", "nf_conntrack")) } // Parses a slice of ConntrackStatEntries from the given filepath. func readConntrackStat(path string) ([]ConntrackStatEntry, error) { // This file is small and can be read with one syscall. b, err := util.ReadFileNoStat(path) if err != nil { // Do not wrap this error so the caller can detect os.IsNotExist and // similar conditions. return nil, err } stat, err := parseConntrackStat(bytes.NewReader(b)) if err != nil { return nil, fmt.Errorf("failed to read conntrack stats from %q: %w", path, err) } return stat, nil } // Reads the contents of a conntrack statistics file and parses a slice of ConntrackStatEntries. func parseConntrackStat(r io.Reader) ([]ConntrackStatEntry, error) { var entries []ConntrackStatEntry scanner := bufio.NewScanner(r) scanner.Scan() for scanner.Scan() { fields := strings.Fields(scanner.Text()) conntrackEntry, err := parseConntrackStatEntry(fields) if err != nil { return nil, err } entries = append(entries, *conntrackEntry) } return entries, nil } // Parses a ConntrackStatEntry from given array of fields. func parseConntrackStatEntry(fields []string) (*ConntrackStatEntry, error) { if len(fields) != 17 { return nil, fmt.Errorf("invalid conntrackstat entry, missing fields") } entry := &ConntrackStatEntry{} entries, err := parseConntrackStatField(fields[0]) if err != nil { return nil, err } entry.Entries = entries found, err := parseConntrackStatField(fields[2]) if err != nil { return nil, err } entry.Found = found invalid, err := parseConntrackStatField(fields[4]) if err != nil { return nil, err } entry.Invalid = invalid ignore, err := parseConntrackStatField(fields[5]) if err != nil { return nil, err } entry.Ignore = ignore insert, err := parseConntrackStatField(fields[8]) if err != nil { return nil, err } entry.Insert = insert insertFailed, err := parseConntrackStatField(fields[9]) if err != nil { return nil, err } entry.InsertFailed = insertFailed drop, err := parseConntrackStatField(fields[10]) if err != nil { return nil, err } entry.Drop = drop earlyDrop, err := parseConntrackStatField(fields[11]) if err != nil { return nil, err } entry.EarlyDrop = earlyDrop searchRestart, err := parseConntrackStatField(fields[16]) if err != nil { return nil, err } entry.SearchRestart = searchRestart return entry, nil } // Parses a uint64 from given hex in string. func parseConntrackStatField(field string) (uint64, error) { val, err := strconv.ParseUint(field, 16, 64) if err != nil { return 0, fmt.Errorf("couldn't parse %q field: %w", field, err) } return val, err }