gopacket.PacketSource.Packets() channel closes on pcap.Handle.Close() only after the next matching packet is received
- Dominant language
- Go
- Stars
- 6.8k
- Forks
- 1.2k
- PR merge metrics
- No merged PRs in 30d
Description
I created a `pcap.Handle` and used it to create a `gopacket.PacketSource.Packets()` channel.
I'd expect that the channel is closed soon after the `pcap.Handle` is closed. The currently observed behavior is that the channel is closed only after the next packet is received.
Similar issue: https://github.com/google/gopacket/issues/326
Reproduction:
```go
package main
import (
"context"
"log"
"sync"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/pcap"
)
var (
wg = &sync.WaitGroup{}
)
func main() {
log.Println("Started")
defer log.Println("Finished")
ctx, cancel := context.WithCancel(context.Background())
go capturePackets(ctx, wg, "eth0", "host 1.2.3.4")
time.Sleep(1 * time.Second)
log.Println("Attempting to close the pcap handle and expecting the packets channel to be closed soon.")
cancel()
wg.Wait()
}
func capturePackets(ctx context.Context, wg *sync.WaitGroup, networkInterface, bpfFilter string) {
wg.Add(1)
defer wg.Done()
defer log.Println("The gopacket.PacketSources.Packets() channel was closed.")
for packet := range packets(ctx, wg, networkInterface, bpfFilter) {
log.Print(packet)
}
}
func packets(ctx context.Context, wg *sync.WaitGroup, networkInterface, bpfFilter string) chan gopacket.Packet {
log.Printf("Listening on '%s', with '%s'", networkInterface, bpfFilter)
if handle, err := pcap.OpenLive(networkInterface, 100, false, pcap.BlockForever); err != nil {
panic(err)
} else if err = handle.SetBPFFilter(bpfFilter); err != nil {
panic(err)
} else {
ps := gopacket.NewPacketSource(handle, handle.LinkType())
go func() {
wg.Add(1)
defer wg.Done()
<-ctx.Done()
log.Println("Closing the pcap handle.")
handle.Close()
log.Println("Closed the pcap handle.")
}()
return ps.Packets()
}
}
```
Save the file above as `main.go` in a temporary folder and execute that in a docker environment:
```shell
docker run --rm -it -v $(pwd):/mnt golang:1.16.0-alpine3.13 sh
cd /mnt
apk add tmux curl gcc libc-dev libpcap-dev
echo 'export PATH=/usr/local/go/bin:$PATH' >> ~/.profile
tmux
go mod init m
go get github.com/google/gopacket
go run .
```
Output:
```txt
5cf847cd4ac6:/mnt# go run .
2021/03/07 12:42:20 Started
2021/03/07 12:42:20 Listening on 'eth0', with 'host 1.2.3.4'
2021/03/07 12:42:21 Attempting to close the pcap handle and expecting the packets channel to be closed soon.
2021/03/07 12:42:21 Closing the pcap handle.
// waiting indefinitely
```
In tmux, open a new pane (`CTRL-B,"`), and execute `curl 1.2.3.4`, then the `gopacket.PacketSource.Packets()` channel is closed right away:
```txt
5cf847cd4ac6:/mnt# go run .
2021/03/07 12:42:20 Started
2021/03/07 12:42:20 Listening on 'eth0', with 'host 1.2.3.4'
2021/03/07 12:42:21 Attempting to close the pcap handle and expecting the packets channel to be closed soon.
2021/03/07 12:42:21 Closing the pcap handle.
// waiting indefinitely, or until e.g. `curl 1.2.3.4` is executed in another tmux pane
2021/03/07 12:44:37 PACKET: 74 bytes, wire length 74 cap length 74 @ 2021-03-07 12:44:37.481411 +0000 UTC
- Layer 1 (14 bytes) = Ethernet {Contents=[..14..] Payload=[..60..] SrcMAC=...
- Layer 2 (20 bytes) = IPv4 {Contents=[..20..] Payload=[..40..] Version=4 ...
- Layer 3 (40 bytes) = TCP {Contents=[..40..] Payload=[] SrcPort=...
2021/03/07 12:44:37 The gopacket.PacketSources.Packets() channel was closed.
2021/03/07 12:44:37 Closed the pcap handle.
2021/03/07 12:44:37 Finished
5cf847cd4ac6:/mnt#
```
Expected behavior: the `gopacket.PacketSources.Packets()` channel is closed soon after `pcap.Handle.Close()` even if it does not receive any packets.
Contributor guide
Assessment
This issue has not been assessed yet.