google / google/gopacket

gopacket.PacketSource.Packets() channel closes on pcap.Handle.Close() only after the next matching packet is received

Open
#862 4 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.