@@ -11,6 +11,7 @@ package rawpacket
1111import (
1212 "errors"
1313 "fmt"
14+ "strings"
1415
1516 "github.com/cilium/ebpf"
1617 "github.com/cilium/ebpf/asm"
@@ -39,6 +40,9 @@ const (
3940
4041 // payload size
4142 structRawPacketEventDataSize = 256
43+
44+ dropStatsKeyStackOffset = int16 (- 8 )
45+ dropStatsValStackOffset = int16 (- 16 )
4246)
4347
4448// ProgOpts defines options
@@ -60,6 +64,7 @@ type ProgOpts struct {
6064 ctxSaveReg asm.Register
6165 tailCallMapFd int
6266 hasGetCurrentCgroupId bool
67+ dropStatsMapFd int
6368}
6469
6570// DefaultProgOpts default options
@@ -97,6 +102,52 @@ func (opts *ProgOpts) WithGetCurrentCgroupID(hasGetCurrentCgroupId bool) *ProgOp
97102 return opts
98103}
99104
105+ // WithDropStatsMapFd sets the map fd used to count dropped packets per filter index.
106+ func (opts * ProgOpts ) WithDropStatsMapFd (fd int ) * ProgOpts {
107+ opts .dropStatsMapFd = fd
108+ return opts
109+ }
110+
111+ func dropStatsIncrementInsts (filterIndex int , dropStatsMapFd int , nextLabel string ) asm.Instructions {
112+ incLabel := fmt .Sprintf ("inc_drop_stat_%d" , filterIndex )
113+ initLabel := fmt .Sprintf ("init_drop_stat_%d" , filterIndex )
114+
115+ return asm.Instructions {
116+ asm .Mov .Reg (asm .R1 , asm .RFP ).WithSymbol (incLabel ),
117+ asm .Add .Imm (asm .R1 , int32 (dropStatsKeyStackOffset )),
118+ asm .Mov .Imm (asm .R2 , int32 (filterIndex )),
119+ asm .StoreMem (asm .R1 , 0 , asm .R2 , asm .DWord ),
120+
121+ asm .LoadMapPtr (asm .R1 , dropStatsMapFd ),
122+ asm .Mov .Reg (asm .R2 , asm .RFP ),
123+ asm .Add .Imm (asm .R2 , int32 (dropStatsKeyStackOffset )),
124+ asm .FnMapLookupElem .Call (),
125+ asm .JEq .Imm (asm .R0 , 0 , initLabel ),
126+
127+ asm .Mov .Reg (asm .R5 , asm .R0 ),
128+ asm .LoadMem (asm .R6 , asm .R5 , 0 , asm .DWord ),
129+ asm .Add .Imm (asm .R6 , 1 ),
130+ asm .StoreMem (asm .R5 , 0 , asm .R6 , asm .DWord ),
131+
132+ asm .Ja .Label (nextLabel ),
133+
134+ asm .Mov .Reg (asm .R3 , asm .RFP ).WithSymbol (initLabel ),
135+ asm .Add .Imm (asm .R3 , int32 (dropStatsValStackOffset )),
136+ asm .Mov .Imm (asm .R4 , 1 ),
137+ asm .StoreMem (asm .R3 , 0 , asm .R4 , asm .DWord ),
138+
139+ asm .LoadMapPtr (asm .R1 , dropStatsMapFd ),
140+ asm .Mov .Reg (asm .R2 , asm .RFP ),
141+ asm .Add .Imm (asm .R2 , int32 (dropStatsKeyStackOffset )),
142+ asm .Mov .Reg (asm .R3 , asm .RFP ),
143+ asm .Add .Imm (asm .R3 , int32 (dropStatsValStackOffset )),
144+ asm .Mov .Imm (asm .R4 , 0 ),
145+ asm .FnMapUpdateElem .Call (),
146+
147+ asm .Ja .Label (nextLabel ),
148+ }
149+ }
150+
100151// FilterToInsts compile a bpf filter expression
101152func FilterToInsts (index int , filter Filter , opts ProgOpts ) (asm.Instructions , error ) {
102153 pcapBPF , err := pcap .CompileBPFFilter (layers .LinkTypeEthernet , 256 , filter .BPFFilter )
@@ -131,25 +182,42 @@ func FilterToInsts(index int, filter Filter, opts ProgOpts) (asm.Instructions, e
131182 )
132183 resultLabel = ""
133184 }
185+ useDropStats := opts .dropStatsMapFd != 0
134186
187+ // Initialize labels
135188 mismatchLabel := fmt .Sprintf ("mismatch_%d_" , index )
189+ afterDropStatsLabel := fmt .Sprintf ("after_drop_stat_%d" , index )
190+ matchLabel := opts .onMatchLabel
191+ mismatchTail := asm.Instructions {
192+ asm .Mov .Imm (asm .R4 , 0 ).WithSymbol (mismatchLabel ),
193+ }
194+ skipLabel := mismatchLabel
195+
196+ // Change labels if it's a drop filter with drop stats
197+ if useDropStats {
198+ matchLabel = fmt .Sprintf ("inc_drop_stat_%d" , index )
199+ skipLabel = afterDropStatsLabel
200+ mismatchTail = asm.Instructions {
201+ asm .Ja .Label (skipLabel ),
202+ }
203+ }
136204
137205 if filter .Pid != 0 {
138206 insts = append (insts ,
139207 // == 0, no match
140- asm .JEq .Imm (cbpfcOpts .Result , 0 , mismatchLabel ).WithSymbol (resultLabel ),
208+ asm .JEq .Imm (cbpfcOpts .Result , 0 , skipLabel ).WithSymbol (resultLabel ),
141209
142210 // check the pid
143211 // load the pid from the packet
144212 asm .LoadMem (asm .R7 , opts .eventPtrReg , structRawPacketEventPidOffset , asm .Word ),
145- asm .JEq .Imm (asm .R7 , int32 (filter .Pid ), opts .onMatchLabel ),
146- asm .Mov .Imm (asm .R4 , 0 ).WithSymbol (mismatchLabel ), // nop instruction, just hold the symbol
213+ asm .JEq .Imm (asm .R7 , int32 (filter .Pid ), matchLabel ),
147214 )
215+ insts = append (insts , mismatchTail ... )
148216 } else if ! filter .CGroupPathKey .IsNull () {
149217 // use the cgroup id which the inode of the cgroup path
150218 insts = append (insts ,
151219 // == 0, no match
152- asm .JEq .Imm (cbpfcOpts .Result , 0 , mismatchLabel ).WithSymbol (resultLabel ),
220+ asm .JEq .Imm (cbpfcOpts .Result , 0 , skipLabel ).WithSymbol (resultLabel ),
153221
154222 // load the cgroup id from the packet
155223 asm .LoadMem (asm .R7 , opts .eventPtrReg , structRawPacketEventCgroupIdOffset , asm .DWord ),
@@ -169,21 +237,33 @@ func FilterToInsts(index int, filter Filter, opts ProgOpts) (asm.Instructions, e
169237
170238 // check the cgroup id
171239 asm .LoadImm (asm .R4 , int64 (filter .CGroupPathKey .Inode ), asm .DWord ),
172- asm .JEq .Reg (asm .R7 , asm .R4 , opts .onMatchLabel ),
173- asm .Mov .Imm (asm .R4 , 0 ).WithSymbol (mismatchLabel ), // nop instruction, just hold the symbol
240+ asm .JEq .Reg (asm .R7 , asm .R4 , matchLabel ),
241+ )
242+ insts = append (insts , mismatchTail ... )
243+ } else if useDropStats {
244+ insts = append (insts ,
245+ asm .JEq .Imm (cbpfcOpts .Result , 0 , skipLabel ).WithSymbol (resultLabel ),
246+ asm .Ja .Label (matchLabel ),
174247 )
175248 } else {
176249 insts = append (insts ,
177- asm .JNE .Imm (cbpfcOpts .Result , 0 , opts . onMatchLabel ).WithSymbol (resultLabel ),
250+ asm .JNE .Imm (cbpfcOpts .Result , 0 , matchLabel ).WithSymbol (resultLabel ),
178251 )
179252 }
253+
254+ if useDropStats {
255+ insts = append (insts , dropStatsIncrementInsts (index , opts .dropStatsMapFd , opts .onMatchLabel )... )
256+ insts = append (insts , asm .Mov .Imm (asm .R4 , 0 ).WithSymbol (afterDropStatsLabel )) // nop instruction, just hold the symbol
257+ }
258+
180259 return insts , nil
181260}
182261
183- // we want to creates progs like that
184- // prog1 -> tc1 -> footer -> prog2 -> tc2 -> footer -> progN -> footer
185- // where each prog is like this
186- // header -> filter 1 -> filter 2 -> ... -> filter n -> footer
262+ // we want to create progs like that
263+ // prog1 -> prog2 -> ... -> progN
264+ //
265+ // where each prog is:
266+ // header -> filter 1 -> filter 2 -> ... -> filter n -> [tail_call] -> footer
187267func filtersToProgs (filters []Filter , opts ProgOpts , headerInsts , footerInsts asm.Instructions ) ([]asm.Instructions , * multierror.Error ) {
188268 var (
189269 progInsts []asm.Instructions
@@ -386,3 +466,11 @@ func DropActionsToProgramSpecs(rawPacketEventMapFd, clsRouterMapFd int, filters
386466
387467 return progSpecs , mErr .ErrorOrNil ()
388468}
469+
470+ // FormatProgramInstructions returns BPF instructions with their indices, useful when
471+ // debugging verifier errors such as "unreachable insn N".
472+ func FormatProgramInstructions (insts asm.Instructions ) string {
473+ var b strings.Builder
474+ fmt .Fprintf (& b , "% 1v" , insts )
475+ return b .String ()
476+ }
0 commit comments