google / gopacket

Provides packet processing capabilities for Go
BSD 3-Clause "New" or "Revised" License
6.27k stars 1.12k forks source link

pcap handle.Close() blocks for a long time #1089

Open stanimirivanovde opened 1 year ago

stanimirivanovde commented 1 year ago

May be this is the same issue as #890

I have an ARP scanner that scans large /16 subnets. I send packets to each IP to see if it is live (65K IPs). The scan works fine but when I call handle.Close() the code blocks for about an hour before proceeding. Inspecting the goroutines looks like we are waiting on a mutex lock in handle.Close():

    handle, err := pcap.OpenLive(deviceName, 65536, true, pcap.BlockForever)

The problem is somewhat related to pcap.BlockForever because when I set my own timeout the Close() finishes successfully. The total scan takes about 30 seconds and if I don't set a timeout to pcap.OpenLive() then I never finish the scan. I thought it never finished but when I left it running for a while it finished after 40-50 minutes. It was not doing anything just being stuck on the close. I thought this is something I did and debugged all my goroutine logic but it looks like the issue is in the pcap.OpenLive(). I can provide more code if needed.

XinRoom commented 1 year ago

I also encountered this problem in Linux, for example:

package main

import (
    "io"
    "log"
    "time"

    "github.com/google/gopacket/pcap"
)

var (
    device  = "eth0"
    snaplen = 1024
)

func openLive() *pcap.Handle {
    handle, err := pcap.OpenLive(device, int32(snaplen), false, pcap.BlockForever)
    if err != nil {
        log.Fatal(err)
    }
    err = handle.SetBPFFilter("dst 1.2.3.4") // A random filter to make sure no data is received
    if err != nil {
        log.Fatal(err)
    }
    go func() {
        for {
            data, _, err := handle.ZeroCopyReadPacketData()
            if err != nil {
                if err == io.EOF {
                    log.Println(err)
                    return
                }
                log.Println(err)
            }
            log.Println(data)
        }
    }()
    return handle
}

func main() {
    log.Println("Started")
    h := openLive()
    time.Sleep(10 * time.Second)
    log.Println("Closing")
    h.Close()
    log.Println("Closed")
    time.Sleep(10 * time.Second)
}
hujun-open commented 1 year ago

I encountered the same problem with pcap.BlockForever, to get around this, I used a small duration like time.Millisecond iso pcap.BlockForever, then check returned error of ReadPacketData, if it is pcap.NextErrorTimeoutExpired, and handle it accordingly.

Archimes commented 1 month ago

nice ! thank you solved this problem