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