Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minor improvements for L2 pcap filters #258

Merged
merged 2 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
5 changes: 3 additions & 2 deletions internal/libpcap/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
38 changes: 22 additions & 16 deletions internal/libpcap/inject.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package libpcap

import (
"errors"
"fmt"

"github.com/cilium/ebpf"
"github.com/cilium/ebpf/asm"
Expand All @@ -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
}
Expand All @@ -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
}
Expand Down