arp.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. // Copyright 2019 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. "fmt"
  16. "net"
  17. "os"
  18. "strconv"
  19. "strings"
  20. )
  21. // Learned from include/uapi/linux/if_arp.h.
  22. const (
  23. // completed entry (ha valid).
  24. ATFComplete = 0x02
  25. // permanent entry.
  26. ATFPermanent = 0x04
  27. // Publish entry.
  28. ATFPublish = 0x08
  29. // Has requested trailers.
  30. ATFUseTrailers = 0x10
  31. // Obsoleted: Want to use a netmask (only for proxy entries).
  32. ATFNetmask = 0x20
  33. // Don't answer this addresses.
  34. ATFDontPublish = 0x40
  35. )
  36. // ARPEntry contains a single row of the columnar data represented in
  37. // /proc/net/arp.
  38. type ARPEntry struct {
  39. // IP address
  40. IPAddr net.IP
  41. // MAC address
  42. HWAddr net.HardwareAddr
  43. // Name of the device
  44. Device string
  45. // Flags
  46. Flags byte
  47. }
  48. // GatherARPEntries retrieves all the ARP entries, parse the relevant columns,
  49. // and then return a slice of ARPEntry's.
  50. func (fs FS) GatherARPEntries() ([]ARPEntry, error) {
  51. data, err := os.ReadFile(fs.proc.Path("net/arp"))
  52. if err != nil {
  53. return nil, fmt.Errorf("%s: error reading arp %s: %w", ErrFileRead, fs.proc.Path("net/arp"), err)
  54. }
  55. return parseARPEntries(data)
  56. }
  57. func parseARPEntries(data []byte) ([]ARPEntry, error) {
  58. lines := strings.Split(string(data), "\n")
  59. entries := make([]ARPEntry, 0)
  60. var err error
  61. const (
  62. expectedDataWidth = 6
  63. expectedHeaderWidth = 9
  64. )
  65. for _, line := range lines {
  66. columns := strings.Fields(line)
  67. width := len(columns)
  68. if width == expectedHeaderWidth || width == 0 {
  69. continue
  70. } else if width == expectedDataWidth {
  71. entry, err := parseARPEntry(columns)
  72. if err != nil {
  73. return []ARPEntry{}, fmt.Errorf("%s: Failed to parse ARP entry: %v: %w", ErrFileParse, entry, err)
  74. }
  75. entries = append(entries, entry)
  76. } else {
  77. return []ARPEntry{}, fmt.Errorf("%s: %d columns found, but expected %d: %w", ErrFileParse, width, expectedDataWidth, err)
  78. }
  79. }
  80. return entries, err
  81. }
  82. func parseARPEntry(columns []string) (ARPEntry, error) {
  83. entry := ARPEntry{Device: columns[5]}
  84. ip := net.ParseIP(columns[0])
  85. entry.IPAddr = ip
  86. if mac, err := net.ParseMAC(columns[3]); err == nil {
  87. entry.HWAddr = mac
  88. } else {
  89. return ARPEntry{}, err
  90. }
  91. if flags, err := strconv.ParseUint(columns[2], 0, 8); err == nil {
  92. entry.Flags = byte(flags)
  93. } else {
  94. return ARPEntry{}, err
  95. }
  96. return entry, nil
  97. }
  98. // IsComplete returns true if ARP entry is marked with complete flag.
  99. func (entry *ARPEntry) IsComplete() bool {
  100. return entry.Flags&ATFComplete != 0
  101. }