@@ -17,6 +17,7 @@ package main
1717import (
1818 "context"
1919 "fmt"
20+ "net"
2021 "os"
2122 "os/signal"
2223 "strings"
@@ -54,7 +55,7 @@ type packetMeta struct {
5455 NetdevIfindex uint32
5556 NetdevFlags uint32
5657 NetdevQueueMapping uint32
57- DropSource uint32
58+ DropReason uint32
5859 Type uint32
5960 NetInode uint32
6061 NetdevName [bpf .NetdevNameLen ]byte
@@ -85,16 +86,38 @@ var (
8586 _ = [1 ]struct {}{}[240 - unsafe .Offsetof (dropPacketEvent {}.Stack )]
8687)
8788
88- // loadDropwatchBPF reads the BPF object at bpfPath, injects filterExpr into the
89- // pcap_stub_l2/l3 stubs, and loads it. Each instance uses a unique BPF name to
90- // allow multiple instances to coexist.
91- func loadDropwatchBPF (bpfPath , filterExpr string ) (bpf.BPF , error ) {
89+ // loadBPFWithFilter reads the BPF object at bpfPath, injects filterExpr into the
90+ // pcap_stub_l2/l3 stubs, applies RewriteConstants for any non-nil consts, and
91+ // loads it. Each instance uses a unique BPF name to allow multiple instances
92+ // to coexist.
93+ func loadBPFWithFilter (bpfPath , filterExpr string , consts map [string ]any ) (bpf.BPF , error ) {
9294 bpfBytes , err := os .ReadFile (bpfPath )
9395 if err != nil {
9496 return nil , fmt .Errorf ("read bpf object: %w" , err )
9597 }
9698 bpfName := fmt .Sprintf ("dropwatch_%d.o" , time .Now ().UnixNano ())
97- return pcapfilter .Load (bpfName , bpfBytes , filterExpr , nil )
99+ return pcapfilter .Load (bpfName , bpfBytes , filterExpr , consts )
100+ }
101+
102+ // deviceFilterConsts resolves devName to an ifindex and returns the consts map
103+ // expected by bpf_skb_filter.h. Returns nil consts when devName is empty
104+ // (filter disabled at the BPF level by leaving filter_ifindex_included == 0).
105+ func deviceFilterConsts (devName string , exclude bool ) (map [string ]any , error ) {
106+ if devName == "" {
107+ return nil , nil
108+ }
109+ iface , err := net .InterfaceByName (devName )
110+ if err != nil {
111+ return nil , fmt .Errorf ("--device %q: %w" , devName , err )
112+ }
113+ var excl uint32
114+ if exclude {
115+ excl = 1
116+ }
117+ return map [string ]any {
118+ "filter_ifindex_included" : uint32 (iface .Index ),
119+ "filter_ifindex_excluded" : excl ,
120+ }, nil
98121}
99122
100123func formatEvent (ev * dropPacketEvent ) * types.DropWatchTracing {
@@ -158,7 +181,12 @@ func mainAction(c *cli.Context) error {
158181 defer sockClient .End ()
159182 }
160183
161- bpfObj , err := loadDropwatchBPF (c .String ("bpf-path" ), c .String ("filter" ))
184+ consts , err := deviceFilterConsts (c .String ("device" ), c .Bool ("device-exclude" ))
185+ if err != nil {
186+ return fmt .Errorf ("dropwatch: %w" , err )
187+ }
188+
189+ bpfObj , err := loadBPFWithFilter (c .String ("bpf-path" ), c .String ("filter" ), consts )
162190 if err != nil {
163191 return fmt .Errorf ("dropwatch: load bpf: %w" , err )
164192 }
@@ -234,6 +262,14 @@ func main() {
234262 Name : "filter" ,
235263 Usage : `tcpdump expression, e.g. "tcp and port 80"` ,
236264 },
265+ & cli.StringFlag {
266+ Name : "device" ,
267+ Usage : "interface to filter on (e.g. eth0); empty = all devices" ,
268+ },
269+ & cli.BoolFlag {
270+ Name : "device-exclude" ,
271+ Usage : "treat --device as a blacklist (drop matching) instead of whitelist (pass matching)" ,
272+ },
237273 & cli.IntFlag {
238274 Name : "duration" ,
239275 Usage : "run for N seconds then exit (0=forever)" ,
0 commit comments