net_openbsd.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. // +build openbsd
  2. package net
  3. import (
  4. "context"
  5. "fmt"
  6. "os/exec"
  7. "regexp"
  8. "strconv"
  9. "strings"
  10. "syscall"
  11. "github.com/shirou/gopsutil/internal/common"
  12. )
  13. var portMatch = regexp.MustCompile(`(.*)\.(\d+)$`)
  14. func ParseNetstat(output string, mode string,
  15. iocs map[string]IOCountersStat) error {
  16. lines := strings.Split(output, "\n")
  17. exists := make([]string, 0, len(lines)-1)
  18. columns := 6
  19. if mode == "ind" {
  20. columns = 10
  21. }
  22. for _, line := range lines {
  23. values := strings.Fields(line)
  24. if len(values) < 1 || values[0] == "Name" {
  25. continue
  26. }
  27. if common.StringsHas(exists, values[0]) {
  28. // skip if already get
  29. continue
  30. }
  31. if len(values) < columns {
  32. continue
  33. }
  34. base := 1
  35. // sometimes Address is omitted
  36. if len(values) < columns {
  37. base = 0
  38. }
  39. parsed := make([]uint64, 0, 8)
  40. var vv []string
  41. if mode == "inb" {
  42. vv = []string{
  43. values[base+3], // BytesRecv
  44. values[base+4], // BytesSent
  45. }
  46. } else {
  47. vv = []string{
  48. values[base+3], // Ipkts
  49. values[base+4], // Ierrs
  50. values[base+5], // Opkts
  51. values[base+6], // Oerrs
  52. values[base+8], // Drops
  53. }
  54. }
  55. for _, target := range vv {
  56. if target == "-" {
  57. parsed = append(parsed, 0)
  58. continue
  59. }
  60. t, err := strconv.ParseUint(target, 10, 64)
  61. if err != nil {
  62. return err
  63. }
  64. parsed = append(parsed, t)
  65. }
  66. exists = append(exists, values[0])
  67. n, present := iocs[values[0]]
  68. if !present {
  69. n = IOCountersStat{Name: values[0]}
  70. }
  71. if mode == "inb" {
  72. n.BytesRecv = parsed[0]
  73. n.BytesSent = parsed[1]
  74. } else {
  75. n.PacketsRecv = parsed[0]
  76. n.Errin = parsed[1]
  77. n.PacketsSent = parsed[2]
  78. n.Errout = parsed[3]
  79. n.Dropin = parsed[4]
  80. n.Dropout = parsed[4]
  81. }
  82. iocs[n.Name] = n
  83. }
  84. return nil
  85. }
  86. func IOCounters(pernic bool) ([]IOCountersStat, error) {
  87. return IOCountersWithContext(context.Background(), pernic)
  88. }
  89. func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
  90. netstat, err := exec.LookPath("netstat")
  91. if err != nil {
  92. return nil, err
  93. }
  94. out, err := invoke.CommandWithContext(ctx, netstat, "-inb")
  95. if err != nil {
  96. return nil, err
  97. }
  98. out2, err := invoke.CommandWithContext(ctx, netstat, "-ind")
  99. if err != nil {
  100. return nil, err
  101. }
  102. iocs := make(map[string]IOCountersStat)
  103. lines := strings.Split(string(out), "\n")
  104. ret := make([]IOCountersStat, 0, len(lines)-1)
  105. err = ParseNetstat(string(out), "inb", iocs)
  106. if err != nil {
  107. return nil, err
  108. }
  109. err = ParseNetstat(string(out2), "ind", iocs)
  110. if err != nil {
  111. return nil, err
  112. }
  113. for _, ioc := range iocs {
  114. ret = append(ret, ioc)
  115. }
  116. if pernic == false {
  117. return getIOCountersAll(ret)
  118. }
  119. return ret, nil
  120. }
  121. // NetIOCountersByFile is an method which is added just a compatibility for linux.
  122. func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
  123. return IOCountersByFileWithContext(context.Background(), pernic, filename)
  124. }
  125. func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) {
  126. return IOCounters(pernic)
  127. }
  128. func FilterCounters() ([]FilterStat, error) {
  129. return FilterCountersWithContext(context.Background())
  130. }
  131. func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
  132. return nil, common.ErrNotImplementedError
  133. }
  134. func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
  135. return ConntrackStatsWithContext(context.Background(), percpu)
  136. }
  137. func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
  138. return nil, common.ErrNotImplementedError
  139. }
  140. // NetProtoCounters returns network statistics for the entire system
  141. // If protocols is empty then all protocols are returned, otherwise
  142. // just the protocols in the list are returned.
  143. // Not Implemented for OpenBSD
  144. func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
  145. return ProtoCountersWithContext(context.Background(), protocols)
  146. }
  147. func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
  148. return nil, common.ErrNotImplementedError
  149. }
  150. func parseNetstatLine(line string) (ConnectionStat, error) {
  151. f := strings.Fields(line)
  152. if len(f) < 5 {
  153. return ConnectionStat{}, fmt.Errorf("wrong line,%s", line)
  154. }
  155. var netType, netFamily uint32
  156. switch f[0] {
  157. case "tcp":
  158. netType = syscall.SOCK_STREAM
  159. netFamily = syscall.AF_INET
  160. case "udp":
  161. netType = syscall.SOCK_DGRAM
  162. netFamily = syscall.AF_INET
  163. case "tcp6":
  164. netType = syscall.SOCK_STREAM
  165. netFamily = syscall.AF_INET6
  166. case "udp6":
  167. netType = syscall.SOCK_DGRAM
  168. netFamily = syscall.AF_INET6
  169. default:
  170. return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[0])
  171. }
  172. laddr, raddr, err := parseNetstatAddr(f[3], f[4], netFamily)
  173. if err != nil {
  174. return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s %s", f[3], f[4])
  175. }
  176. n := ConnectionStat{
  177. Fd: uint32(0), // not supported
  178. Family: uint32(netFamily),
  179. Type: uint32(netType),
  180. Laddr: laddr,
  181. Raddr: raddr,
  182. Pid: int32(0), // not supported
  183. }
  184. if len(f) == 6 {
  185. n.Status = f[5]
  186. }
  187. return n, nil
  188. }
  189. func parseNetstatAddr(local string, remote string, family uint32) (laddr Addr, raddr Addr, err error) {
  190. parse := func(l string) (Addr, error) {
  191. matches := portMatch.FindStringSubmatch(l)
  192. if matches == nil {
  193. return Addr{}, fmt.Errorf("wrong addr, %s", l)
  194. }
  195. host := matches[1]
  196. port := matches[2]
  197. if host == "*" {
  198. switch family {
  199. case syscall.AF_INET:
  200. host = "0.0.0.0"
  201. case syscall.AF_INET6:
  202. host = "::"
  203. default:
  204. return Addr{}, fmt.Errorf("unknown family, %d", family)
  205. }
  206. }
  207. lport, err := strconv.Atoi(port)
  208. if err != nil {
  209. return Addr{}, err
  210. }
  211. return Addr{IP: host, Port: uint32(lport)}, nil
  212. }
  213. laddr, err = parse(local)
  214. if remote != "*.*" { // remote addr exists
  215. raddr, err = parse(remote)
  216. if err != nil {
  217. return laddr, raddr, err
  218. }
  219. }
  220. return laddr, raddr, err
  221. }
  222. // Return a list of network connections opened.
  223. func Connections(kind string) ([]ConnectionStat, error) {
  224. return ConnectionsWithContext(context.Background(), kind)
  225. }
  226. func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
  227. var ret []ConnectionStat
  228. args := []string{"-na"}
  229. switch strings.ToLower(kind) {
  230. default:
  231. fallthrough
  232. case "":
  233. fallthrough
  234. case "all":
  235. fallthrough
  236. case "inet":
  237. // nothing to add
  238. case "inet4":
  239. args = append(args, "-finet")
  240. case "inet6":
  241. args = append(args, "-finet6")
  242. case "tcp":
  243. args = append(args, "-ptcp")
  244. case "tcp4":
  245. args = append(args, "-ptcp", "-finet")
  246. case "tcp6":
  247. args = append(args, "-ptcp", "-finet6")
  248. case "udp":
  249. args = append(args, "-pudp")
  250. case "udp4":
  251. args = append(args, "-pudp", "-finet")
  252. case "udp6":
  253. args = append(args, "-pudp", "-finet6")
  254. case "unix":
  255. return ret, common.ErrNotImplementedError
  256. }
  257. netstat, err := exec.LookPath("netstat")
  258. if err != nil {
  259. return nil, err
  260. }
  261. out, err := invoke.CommandWithContext(ctx, netstat, args...)
  262. if err != nil {
  263. return nil, err
  264. }
  265. lines := strings.Split(string(out), "\n")
  266. for _, line := range lines {
  267. if !(strings.HasPrefix(line, "tcp") || strings.HasPrefix(line, "udp")) {
  268. continue
  269. }
  270. n, err := parseNetstatLine(line)
  271. if err != nil {
  272. continue
  273. }
  274. ret = append(ret, n)
  275. }
  276. return ret, nil
  277. }