net_conntrackstat.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. // Copyright 2020 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. package procfs
  14. import (
  15. "bufio"
  16. "bytes"
  17. "fmt"
  18. "io"
  19. "strings"
  20. "github.com/prometheus/procfs/internal/util"
  21. )
  22. // A ConntrackStatEntry represents one line from net/stat/nf_conntrack
  23. // and contains netfilter conntrack statistics at one CPU core.
  24. type ConntrackStatEntry struct {
  25. Entries uint64
  26. Searched uint64
  27. Found uint64
  28. New uint64
  29. Invalid uint64
  30. Ignore uint64
  31. Delete uint64
  32. DeleteList uint64
  33. Insert uint64
  34. InsertFailed uint64
  35. Drop uint64
  36. EarlyDrop uint64
  37. SearchRestart uint64
  38. }
  39. // ConntrackStat retrieves netfilter's conntrack statistics, split by CPU cores.
  40. func (fs FS) ConntrackStat() ([]ConntrackStatEntry, error) {
  41. return readConntrackStat(fs.proc.Path("net", "stat", "nf_conntrack"))
  42. }
  43. // Parses a slice of ConntrackStatEntries from the given filepath.
  44. func readConntrackStat(path string) ([]ConntrackStatEntry, error) {
  45. // This file is small and can be read with one syscall.
  46. b, err := util.ReadFileNoStat(path)
  47. if err != nil {
  48. // Do not wrap this error so the caller can detect os.IsNotExist and
  49. // similar conditions.
  50. return nil, err
  51. }
  52. stat, err := parseConntrackStat(bytes.NewReader(b))
  53. if err != nil {
  54. return nil, fmt.Errorf("%s: Cannot read file: %v: %w", ErrFileRead, path, err)
  55. }
  56. return stat, nil
  57. }
  58. // Reads the contents of a conntrack statistics file and parses a slice of ConntrackStatEntries.
  59. func parseConntrackStat(r io.Reader) ([]ConntrackStatEntry, error) {
  60. var entries []ConntrackStatEntry
  61. scanner := bufio.NewScanner(r)
  62. scanner.Scan()
  63. for scanner.Scan() {
  64. fields := strings.Fields(scanner.Text())
  65. conntrackEntry, err := parseConntrackStatEntry(fields)
  66. if err != nil {
  67. return nil, err
  68. }
  69. entries = append(entries, *conntrackEntry)
  70. }
  71. return entries, nil
  72. }
  73. // Parses a ConntrackStatEntry from given array of fields.
  74. func parseConntrackStatEntry(fields []string) (*ConntrackStatEntry, error) {
  75. entries, err := util.ParseHexUint64s(fields)
  76. if err != nil {
  77. return nil, fmt.Errorf("%s: Cannot parse entry: %d: %w", ErrFileParse, entries, err)
  78. }
  79. numEntries := len(entries)
  80. if numEntries < 16 || numEntries > 17 {
  81. return nil,
  82. fmt.Errorf("%w: invalid conntrackstat entry, invalid number of fields: %d", ErrFileParse, numEntries)
  83. }
  84. stats := &ConntrackStatEntry{
  85. Entries: *entries[0],
  86. Searched: *entries[1],
  87. Found: *entries[2],
  88. New: *entries[3],
  89. Invalid: *entries[4],
  90. Ignore: *entries[5],
  91. Delete: *entries[6],
  92. DeleteList: *entries[7],
  93. Insert: *entries[8],
  94. InsertFailed: *entries[9],
  95. Drop: *entries[10],
  96. EarlyDrop: *entries[11],
  97. }
  98. // Ignore missing search_restart on Linux < 2.6.35.
  99. if numEntries == 17 {
  100. stats.SearchRestart = *entries[16]
  101. }
  102. return stats, nil
  103. }