diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9fe2c3c1..6d87ac1f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -114,7 +114,6 @@ jobs: expected-output-pattern: '1.1.1.1:80' - name: Test --filter-track-skb - # DNAT uses: ./.github/actions/pwru-test with: test-name: filter-track-skb @@ -125,6 +124,25 @@ jobs: curl -vvv -sS --fail --connect-timeout "1" -o /dev/null http://10.10.20.99:80 || true expected-output-pattern: '10.10.14.2:80' + - name: Test ARP filter + uses: ./.github/actions/pwru-test + with: + test-name: filter-arp + pwru-pcap-filter: 'arp and arp[7] = 1 and arp[24]= 169 and arp[25] = 254 and arp[26] = 0 and arp[27] = 1' + traffic-setup: | + ip net a pwru + ip l a pwru-veth type veth peer name pwru-veth-peer + ip l s pwru-veth-peer up + ip l s pwru-veth netns pwru + ip net e pwru ip l s pwru-veth up + ip r a 10.0.0.1 dev pwru-veth-peer + ip net e pwru ip a a 10.0.0.1 dev pwru-veth + ip net e pwru ip r a 169.254.0.1 dev pwru-veth + ip net e pwru ip r a default via 169.254.0.1 dev pwru-veth + + ping -W1 -c1 10.0.0.1 || true + expected-output-pattern: 'arp_rcv' + - name: Fetch artifacts if: ${{ !success() }} uses: cilium/little-vm-helper@908ab1ff8a596a03cd5221a1f8602dc44c3f906d diff --git a/internal/libpcap/compile.go b/internal/libpcap/compile.go index 2c830350..8834d4c4 100644 --- a/internal/libpcap/compile.go +++ b/internal/libpcap/compile.go @@ -42,10 +42,11 @@ func CompileCbpf(expr string, l3 bool) (insts []bpf.Instruction, err error) { return } - pcap := C.pcap_open_dead(C.DLT_EN10MB, MAXIMUM_SNAPLEN) + pcapType := C.DLT_EN10MB if l3 { - pcap = C.pcap_open_dead(C.DLT_RAW, MAXIMUM_SNAPLEN) + pcapType = C.DLT_RAW } + pcap := C.pcap_open_dead(C.int(pcapType), MAXIMUM_SNAPLEN) if pcap == nil { return nil, fmt.Errorf("failed to pcap_open_dead: %+v\n", C.PCAP_ERROR) } diff --git a/internal/libpcap/inject.go b/internal/libpcap/inject.go index e59675b2..e8955b73 100644 --- a/internal/libpcap/inject.go +++ b/internal/libpcap/inject.go @@ -2,7 +2,6 @@ package libpcap import ( "errors" - "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" @@ -17,8 +16,7 @@ func InjectFilters(program *ebpf.ProgramSpec, filterExpr string) (err error) { // This could happen for l2 only filters such as "arp". In this // case we don't want to exit with an error, but instead inject // a deny-all filter to reject all l3 skbs. - fmt.Printf("L3 filter injection failed while L2 filter injection succeeded, injecting a deny-all L3 filter: %+v\n", err) - return injectFilter(program, "src host 0.0.0.0 and dst host 0.0.0.0 and tcp", true) + return injectFilter(program, "__pwru_reject_all__", true) } return } @@ -43,19 +41,27 @@ func injectFilter(program *ebpf.ProgramSpec, filterExpr string, l3 bool) (err er return errors.New("Cannot find the injection position") } - filterEbpf, err := CompileEbpf(filterExpr, cbpfc.EBPFOpts{ - // The rejection position is in the beginning of the `filter_pcap_ebpf` function: - // filter_pcap_ebpf(void *_skb, void *__skb, void *___skb, void *data, void* data_end) - // So we can confidently say, skb->data is at r4, skb->data_end is at r5. - PacketStart: asm.R4, - PacketEnd: asm.R5, - Result: asm.R0, - ResultLabel: "result" + suffix, - // R0-R3 are also safe to use thanks to the placeholder parameters _skb, __skb, ___skb. - Working: [4]asm.Register{asm.R0, asm.R1, asm.R2, asm.R3}, - LabelPrefix: "filter" + suffix, - StackOffset: -int(AvailableOffset), - }, l3) + var filterEbpf asm.Instructions + if filterExpr == "__pwru_reject_all__" { + // let data = data_end, so kprobe_pwru.c:filter_pcap_ebpf_l3() always returns false + filterEbpf = asm.Instructions{ + asm.Mov.Reg(asm.R4, asm.R5), // r4 = r5 (data = data_end) + } + } else { + filterEbpf, err = CompileEbpf(filterExpr, cbpfc.EBPFOpts{ + // The rejection position is in the beginning of the `filter_pcap_ebpf` function: + // filter_pcap_ebpf(void *_skb, void *__skb, void *___skb, void *data, void* data_end) + // So we can confidently say, skb->data is at r4, skb->data_end is at r5. + PacketStart: asm.R4, + PacketEnd: asm.R5, + Result: asm.R0, + ResultLabel: "result" + suffix, + // R0-R3 are also safe to use thanks to the placeholder parameters _skb, __skb, ___skb. + Working: [4]asm.Register{asm.R0, asm.R1, asm.R2, asm.R3}, + LabelPrefix: "filter" + suffix, + StackOffset: -int(AvailableOffset), + }, l3) + } if err != nil { return }