conntrack.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. package firewall
  2. import (
  3. "bufio"
  4. "bytes"
  5. "fmt"
  6. "os/exec"
  7. "strconv"
  8. "strings"
  9. )
  10. // ListActiveConnections executes `conntrack -L -o extended` and parses the output
  11. func ListActiveConnections() ([]ActiveConnection, error) {
  12. cmd := exec.Command("conntrack", "-L", "-o", "extended")
  13. out, err := cmd.Output()
  14. if err != nil {
  15. // If conntrack is empty or fails, return empty slice
  16. return make([]ActiveConnection, 0), nil
  17. }
  18. var conns []ActiveConnection
  19. scanner := bufio.NewScanner(bytes.NewReader(out))
  20. for scanner.Scan() {
  21. line := strings.TrimSpace(scanner.Text())
  22. if line == "" || strings.HasPrefix(line, "conntrack") {
  23. continue
  24. }
  25. conn := parseConntrackLine(line)
  26. if conn.Protocol != "" && conn.SrcAddress != "" {
  27. conns = append(conns, conn)
  28. }
  29. }
  30. if conns == nil {
  31. conns = make([]ActiveConnection, 0)
  32. }
  33. return conns, nil
  34. }
  35. func parseConntrackLine(line string) ActiveConnection {
  36. var conn ActiveConnection
  37. fields := strings.Fields(line)
  38. if len(fields) < 4 {
  39. return conn
  40. }
  41. // Detect protocol
  42. protocol := ""
  43. timeout := 0
  44. state := ""
  45. var srcList []string
  46. var dstList []string
  47. var sportList []int
  48. var dportList []int
  49. for i, f := range fields {
  50. if f == "tcp" || f == "udp" || f == "icmp" || f == "gre" || f == "esp" {
  51. protocol = strings.ToUpper(f)
  52. } else if strings.HasPrefix(f, "src=") {
  53. srcList = append(srcList, strings.TrimPrefix(f, "src="))
  54. } else if strings.HasPrefix(f, "dst=") {
  55. dstList = append(dstList, strings.TrimPrefix(f, "dst="))
  56. } else if strings.HasPrefix(f, "sport=") {
  57. p, _ := strconv.Atoi(strings.TrimPrefix(f, "sport="))
  58. sportList = append(sportList, p)
  59. } else if strings.HasPrefix(f, "dport=") {
  60. p, _ := strconv.Atoi(strings.TrimPrefix(f, "dport="))
  61. dportList = append(dportList, p)
  62. } else if strings.HasPrefix(f, "mark=") {
  63. conn.ConnectionMark = strings.TrimPrefix(f, "mark=")
  64. } else if f == "[ASSURED]" {
  65. conn.Assured = true
  66. } else if i > 1 && timeout == 0 {
  67. if t, err := strconv.Atoi(f); err == nil && t > 0 {
  68. timeout = t
  69. }
  70. }
  71. if f == "ESTABLISHED" || f == "TIME_WAIT" || f == "CLOSE_WAIT" || f == "FIN_WAIT" || f == "SYN_SENT" || f == "SYN_RECV" {
  72. state = f
  73. }
  74. }
  75. conn.Protocol = protocol
  76. conn.Timeout = timeout
  77. conn.TCPState = state
  78. if conn.TCPState == "" {
  79. conn.TCPState = protocol
  80. }
  81. if len(srcList) > 0 {
  82. conn.SrcAddress = srcList[0]
  83. }
  84. if len(dstList) > 0 {
  85. conn.DstAddress = dstList[0]
  86. }
  87. if len(sportList) > 0 {
  88. conn.SrcPort = sportList[0]
  89. }
  90. if len(dportList) > 0 {
  91. conn.DstPort = dportList[0]
  92. }
  93. // Reply pair
  94. if len(srcList) > 1 {
  95. conn.ReplySrcAddress = srcList[1]
  96. }
  97. if len(dstList) > 1 {
  98. conn.ReplyDstAddress = dstList[1]
  99. }
  100. if len(sportList) > 1 {
  101. conn.ReplySrcPort = sportList[1]
  102. }
  103. if len(dportList) > 1 {
  104. conn.ReplyDstPort = dportList[1]
  105. }
  106. conn.ID = fmt.Sprintf("%s-%s:%d-%s:%d", conn.Protocol, conn.SrcAddress, conn.SrcPort, conn.DstAddress, conn.DstPort)
  107. return conn
  108. }
  109. // KillConnection deletes a specific conntrack flow
  110. func KillConnection(protocol, src string, sport int, dst string, dport int) error {
  111. args := []string{"-D"}
  112. if protocol != "" {
  113. args = append(args, "-p", strings.ToLower(protocol))
  114. }
  115. if src != "" {
  116. args = append(args, "-s", src)
  117. }
  118. if dst != "" {
  119. args = append(args, "-d", dst)
  120. }
  121. if sport > 0 {
  122. args = append(args, "--sport", strconv.Itoa(sport))
  123. }
  124. if dport > 0 {
  125. args = append(args, "--dport", strconv.Itoa(dport))
  126. }
  127. cmd := exec.Command("conntrack", args...)
  128. out, err := cmd.CombinedOutput()
  129. if err != nil {
  130. return fmt.Errorf("failed to terminate connection: %s: %w", string(out), err)
  131. }
  132. return nil
  133. }