net_udp.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  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. "encoding/hex"
  17. "fmt"
  18. "io"
  19. "net"
  20. "os"
  21. "strconv"
  22. "strings"
  23. )
  24. const (
  25. // readLimit is used by io.LimitReader while reading the content of the
  26. // /proc/net/udp{,6} files. The number of lines inside such a file is dynamic
  27. // as each line represents a single used socket.
  28. // In theory, the number of available sockets is 65535 (2^16 - 1) per IP.
  29. // With e.g. 150 Byte per line and the maximum number of 65535,
  30. // the reader needs to handle 150 Byte * 65535 =~ 10 MB for a single IP.
  31. readLimit = 4294967296 // Byte -> 4 GiB
  32. )
  33. type (
  34. // NetUDP represents the contents of /proc/net/udp{,6} file without the header.
  35. NetUDP []*netUDPLine
  36. // NetUDPSummary provides already computed values like the total queue lengths or
  37. // the total number of used sockets. In contrast to NetUDP it does not collect
  38. // the parsed lines into a slice.
  39. NetUDPSummary struct {
  40. // TxQueueLength shows the total queue length of all parsed tx_queue lengths.
  41. TxQueueLength uint64
  42. // RxQueueLength shows the total queue length of all parsed rx_queue lengths.
  43. RxQueueLength uint64
  44. // UsedSockets shows the total number of parsed lines representing the
  45. // number of used sockets.
  46. UsedSockets uint64
  47. }
  48. // netUDPLine represents the fields parsed from a single line
  49. // in /proc/net/udp{,6}. Fields which are not used by UDP are skipped.
  50. // For the proc file format details, see https://linux.die.net/man/5/proc.
  51. netUDPLine struct {
  52. Sl uint64
  53. LocalAddr net.IP
  54. LocalPort uint64
  55. RemAddr net.IP
  56. RemPort uint64
  57. St uint64
  58. TxQueue uint64
  59. RxQueue uint64
  60. UID uint64
  61. }
  62. )
  63. // NetUDP returns the IPv4 kernel/networking statistics for UDP datagrams
  64. // read from /proc/net/udp.
  65. func (fs FS) NetUDP() (NetUDP, error) {
  66. return newNetUDP(fs.proc.Path("net/udp"))
  67. }
  68. // NetUDP6 returns the IPv6 kernel/networking statistics for UDP datagrams
  69. // read from /proc/net/udp6.
  70. func (fs FS) NetUDP6() (NetUDP, error) {
  71. return newNetUDP(fs.proc.Path("net/udp6"))
  72. }
  73. // NetUDPSummary returns already computed statistics like the total queue lengths
  74. // for UDP datagrams read from /proc/net/udp.
  75. func (fs FS) NetUDPSummary() (*NetUDPSummary, error) {
  76. return newNetUDPSummary(fs.proc.Path("net/udp"))
  77. }
  78. // NetUDP6Summary returns already computed statistics like the total queue lengths
  79. // for UDP datagrams read from /proc/net/udp6.
  80. func (fs FS) NetUDP6Summary() (*NetUDPSummary, error) {
  81. return newNetUDPSummary(fs.proc.Path("net/udp6"))
  82. }
  83. // newNetUDP creates a new NetUDP{,6} from the contents of the given file.
  84. func newNetUDP(file string) (NetUDP, error) {
  85. f, err := os.Open(file)
  86. if err != nil {
  87. return nil, err
  88. }
  89. defer f.Close()
  90. netUDP := NetUDP{}
  91. lr := io.LimitReader(f, readLimit)
  92. s := bufio.NewScanner(lr)
  93. s.Scan() // skip first line with headers
  94. for s.Scan() {
  95. fields := strings.Fields(s.Text())
  96. line, err := parseNetUDPLine(fields)
  97. if err != nil {
  98. return nil, err
  99. }
  100. netUDP = append(netUDP, line)
  101. }
  102. if err := s.Err(); err != nil {
  103. return nil, err
  104. }
  105. return netUDP, nil
  106. }
  107. // newNetUDPSummary creates a new NetUDP{,6} from the contents of the given file.
  108. func newNetUDPSummary(file string) (*NetUDPSummary, error) {
  109. f, err := os.Open(file)
  110. if err != nil {
  111. return nil, err
  112. }
  113. defer f.Close()
  114. netUDPSummary := &NetUDPSummary{}
  115. lr := io.LimitReader(f, readLimit)
  116. s := bufio.NewScanner(lr)
  117. s.Scan() // skip first line with headers
  118. for s.Scan() {
  119. fields := strings.Fields(s.Text())
  120. line, err := parseNetUDPLine(fields)
  121. if err != nil {
  122. return nil, err
  123. }
  124. netUDPSummary.TxQueueLength += line.TxQueue
  125. netUDPSummary.RxQueueLength += line.RxQueue
  126. netUDPSummary.UsedSockets++
  127. }
  128. if err := s.Err(); err != nil {
  129. return nil, err
  130. }
  131. return netUDPSummary, nil
  132. }
  133. // parseNetUDPLine parses a single line, represented by a list of fields.
  134. func parseNetUDPLine(fields []string) (*netUDPLine, error) {
  135. line := &netUDPLine{}
  136. if len(fields) < 8 {
  137. return nil, fmt.Errorf(
  138. "cannot parse net udp socket line as it has less then 8 columns: %s",
  139. strings.Join(fields, " "),
  140. )
  141. }
  142. var err error // parse error
  143. // sl
  144. s := strings.Split(fields[0], ":")
  145. if len(s) != 2 {
  146. return nil, fmt.Errorf(
  147. "cannot parse sl field in udp socket line: %s", fields[0])
  148. }
  149. if line.Sl, err = strconv.ParseUint(s[0], 0, 64); err != nil {
  150. return nil, fmt.Errorf("cannot parse sl value in udp socket line: %s", err)
  151. }
  152. // local_address
  153. l := strings.Split(fields[1], ":")
  154. if len(l) != 2 {
  155. return nil, fmt.Errorf(
  156. "cannot parse local_address field in udp socket line: %s", fields[1])
  157. }
  158. if line.LocalAddr, err = hex.DecodeString(l[0]); err != nil {
  159. return nil, fmt.Errorf(
  160. "cannot parse local_address value in udp socket line: %s", err)
  161. }
  162. if line.LocalPort, err = strconv.ParseUint(l[1], 16, 64); err != nil {
  163. return nil, fmt.Errorf(
  164. "cannot parse local_address port value in udp socket line: %s", err)
  165. }
  166. // remote_address
  167. r := strings.Split(fields[2], ":")
  168. if len(r) != 2 {
  169. return nil, fmt.Errorf(
  170. "cannot parse rem_address field in udp socket line: %s", fields[1])
  171. }
  172. if line.RemAddr, err = hex.DecodeString(r[0]); err != nil {
  173. return nil, fmt.Errorf(
  174. "cannot parse rem_address value in udp socket line: %s", err)
  175. }
  176. if line.RemPort, err = strconv.ParseUint(r[1], 16, 64); err != nil {
  177. return nil, fmt.Errorf(
  178. "cannot parse rem_address port value in udp socket line: %s", err)
  179. }
  180. // st
  181. if line.St, err = strconv.ParseUint(fields[3], 16, 64); err != nil {
  182. return nil, fmt.Errorf(
  183. "cannot parse st value in udp socket line: %s", err)
  184. }
  185. // tx_queue and rx_queue
  186. q := strings.Split(fields[4], ":")
  187. if len(q) != 2 {
  188. return nil, fmt.Errorf(
  189. "cannot parse tx/rx queues in udp socket line as it has a missing colon: %s",
  190. fields[4],
  191. )
  192. }
  193. if line.TxQueue, err = strconv.ParseUint(q[0], 16, 64); err != nil {
  194. return nil, fmt.Errorf("cannot parse tx_queue value in udp socket line: %s", err)
  195. }
  196. if line.RxQueue, err = strconv.ParseUint(q[1], 16, 64); err != nil {
  197. return nil, fmt.Errorf("cannot parse rx_queue value in udp socket line: %s", err)
  198. }
  199. // uid
  200. if line.UID, err = strconv.ParseUint(fields[7], 0, 64); err != nil {
  201. return nil, fmt.Errorf(
  202. "cannot parse uid value in udp socket line: %s", err)
  203. }
  204. return line, nil
  205. }