Search code examples
gopcapgopacket

No blocking Ethernet capture


I use this code to capture Ehternet packet:

var snapshotLen int32 = 102400
var promiscuous bool = true
var timeout time.Duration = 1 * time.Second
var handle *pcap.Handle
handle, err = pcap.OpenLive(device, snapshotLen, promiscuous, timeout)
err = handle.SetBPFFilter(filter)

packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
for packet := range packetSource.Packets() {

}

Problem is packetSource.Packets() is blocking: if no packet received, there is an infinite loop. How can I do to set a timeout?


Solution

  • From the docs:

    func (p *PacketSource) Packets() chan Packet

    Packets returns a channel of packets, allowing easy iterating over packets. Packets will be asynchronously read in from the underlying PacketDataSource and written to the returned channel. If the underlying PacketDataSource returns an io.EOF error, the channel will be closed. If any other error is encountered, it is ignored.

    for packet := range packetSource.Packets() {   handlePacket(packet) 
    // Do something with each packet. }
    

    If called more than once, returns the same channel.

    So, since gopacket.PacketSource.Packets() returns a channel, you can employ the usual tactic of selecting on two channels—an operation on one of which is controlled by a timeout.

    In the simplest case, something like this would do:

    timer := time.NewTicker(2 * time.Second)
    defer timer.Stop()
    
    src := gopacket.NewPacketSource(handle, handle.LinkType())
    for {
        select {
            case pkt := <- src.Packets():
                // process a packet in pkt
            case <- timer.C:
                // timed out waiting for 2 seconds
        }
    }
    

    Depending on the exact use case, the second channel can come from another source—such as a context.