| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- package firewall
- import (
- "bufio"
- "bytes"
- "fmt"
- "os/exec"
- "strconv"
- "strings"
- )
- // ListActiveConnections executes `conntrack -L -o extended` and parses the output
- func ListActiveConnections() ([]ActiveConnection, error) {
- cmd := exec.Command("conntrack", "-L", "-o", "extended")
- out, err := cmd.Output()
- if err != nil {
- // If conntrack is empty or fails, return empty slice
- return make([]ActiveConnection, 0), nil
- }
- var conns []ActiveConnection
- scanner := bufio.NewScanner(bytes.NewReader(out))
- for scanner.Scan() {
- line := strings.TrimSpace(scanner.Text())
- if line == "" || strings.HasPrefix(line, "conntrack") {
- continue
- }
- conn := parseConntrackLine(line)
- if conn.Protocol != "" && conn.SrcAddress != "" {
- conns = append(conns, conn)
- }
- }
- if conns == nil {
- conns = make([]ActiveConnection, 0)
- }
- return conns, nil
- }
- func parseConntrackLine(line string) ActiveConnection {
- var conn ActiveConnection
- fields := strings.Fields(line)
- if len(fields) < 4 {
- return conn
- }
- // Detect protocol
- protocol := ""
- timeout := 0
- state := ""
- var srcList []string
- var dstList []string
- var sportList []int
- var dportList []int
- for i, f := range fields {
- if f == "tcp" || f == "udp" || f == "icmp" || f == "gre" || f == "esp" {
- protocol = strings.ToUpper(f)
- } else if strings.HasPrefix(f, "src=") {
- srcList = append(srcList, strings.TrimPrefix(f, "src="))
- } else if strings.HasPrefix(f, "dst=") {
- dstList = append(dstList, strings.TrimPrefix(f, "dst="))
- } else if strings.HasPrefix(f, "sport=") {
- p, _ := strconv.Atoi(strings.TrimPrefix(f, "sport="))
- sportList = append(sportList, p)
- } else if strings.HasPrefix(f, "dport=") {
- p, _ := strconv.Atoi(strings.TrimPrefix(f, "dport="))
- dportList = append(dportList, p)
- } else if strings.HasPrefix(f, "mark=") {
- conn.ConnectionMark = strings.TrimPrefix(f, "mark=")
- } else if f == "[ASSURED]" {
- conn.Assured = true
- } else if i > 1 && timeout == 0 {
- if t, err := strconv.Atoi(f); err == nil && t > 0 {
- timeout = t
- }
- }
- if f == "ESTABLISHED" || f == "TIME_WAIT" || f == "CLOSE_WAIT" || f == "FIN_WAIT" || f == "SYN_SENT" || f == "SYN_RECV" {
- state = f
- }
- }
- conn.Protocol = protocol
- conn.Timeout = timeout
- conn.TCPState = state
- if conn.TCPState == "" {
- conn.TCPState = protocol
- }
- if len(srcList) > 0 {
- conn.SrcAddress = srcList[0]
- }
- if len(dstList) > 0 {
- conn.DstAddress = dstList[0]
- }
- if len(sportList) > 0 {
- conn.SrcPort = sportList[0]
- }
- if len(dportList) > 0 {
- conn.DstPort = dportList[0]
- }
- // Reply pair
- if len(srcList) > 1 {
- conn.ReplySrcAddress = srcList[1]
- }
- if len(dstList) > 1 {
- conn.ReplyDstAddress = dstList[1]
- }
- if len(sportList) > 1 {
- conn.ReplySrcPort = sportList[1]
- }
- if len(dportList) > 1 {
- conn.ReplyDstPort = dportList[1]
- }
- conn.ID = fmt.Sprintf("%s-%s:%d-%s:%d", conn.Protocol, conn.SrcAddress, conn.SrcPort, conn.DstAddress, conn.DstPort)
- return conn
- }
- // KillConnection deletes a specific conntrack flow
- func KillConnection(protocol, src string, sport int, dst string, dport int) error {
- args := []string{"-D"}
- if protocol != "" {
- args = append(args, "-p", strings.ToLower(protocol))
- }
- if src != "" {
- args = append(args, "-s", src)
- }
- if dst != "" {
- args = append(args, "-d", dst)
- }
- if sport > 0 {
- args = append(args, "--sport", strconv.Itoa(sport))
- }
- if dport > 0 {
- args = append(args, "--dport", strconv.Itoa(dport))
- }
- cmd := exec.Command("conntrack", args...)
- out, err := cmd.CombinedOutput()
- if err != nil {
- return fmt.Errorf("failed to terminate connection: %s: %w", string(out), err)
- }
- return nil
- }
|