arping.go 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. package neighbors
  2. import (
  3. "bufio"
  4. "bytes"
  5. "context"
  6. "errors"
  7. "fmt"
  8. "os/exec"
  9. "regexp"
  10. "goa.design/clue/log"
  11. )
  12. type (
  13. ARPing interface {
  14. Ping(ctx context.Context, ifi, hw, ip string) (bool, error)
  15. Count(count uint)
  16. }
  17. arping struct {
  18. arpingCmd, sudoCmd, count string
  19. }
  20. )
  21. func NewARPing(count uint) (ARPing, error) {
  22. arpingCmd, err := exec.LookPath("arping")
  23. if err != nil {
  24. return nil, err
  25. }
  26. cmd := exec.Command(arpingCmd, "--help")
  27. b, err := cmd.Output()
  28. if err != nil {
  29. return nil, fmt.Errorf(`incompatible "arping" command (%w)`, err)
  30. }
  31. ok, err := regexp.Match("^ARPing ", b)
  32. if err != nil {
  33. return nil, err
  34. }
  35. if !ok {
  36. r := bufio.NewReaderSize(bytes.NewReader(b), 32)
  37. l, p, err := r.ReadLine()
  38. if err != nil {
  39. return nil, fmt.Errorf(`incompatible "arping" command (%w)`, err)
  40. }
  41. var e string
  42. if p {
  43. e = "\u2026"
  44. }
  45. return nil, fmt.Errorf(`incompatible "arping" command (%s%v)`, l, e)
  46. }
  47. sudoCmd, err := exec.LookPath("sudo")
  48. if err != nil {
  49. return nil, err
  50. }
  51. return &arping{
  52. arpingCmd: arpingCmd,
  53. sudoCmd: sudoCmd,
  54. count: fmt.Sprint(count),
  55. }, nil
  56. }
  57. func (a *arping) Ping(ctx context.Context, ifi, hw, ip string) (ok bool, err error) {
  58. cmd := exec.CommandContext(ctx, a.sudoCmd, a.arpingCmd, "-c", a.count, "-i", ifi, "-t", hw, "-q", ip)
  59. log.Debug(ctx, log.KV{K: "cmd", V: cmd})
  60. err = cmd.Run()
  61. if err == nil {
  62. ok = true
  63. } else {
  64. var exitError *exec.ExitError
  65. if errors.As(err, &exitError) && len(exitError.Stderr) == 0 {
  66. err = nil
  67. } else {
  68. return
  69. }
  70. }
  71. log.Debug(ctx, log.KV{K: "cmd", V: cmd}, log.KV{K: "ok", V: ok})
  72. return
  73. }
  74. func (a *arping) Count(count uint) {
  75. a.count = fmt.Sprint(count)
  76. }