Skip to content

Commit

Permalink
support filter with pcap-filter expression
Browse files Browse the repository at this point in the history
  • Loading branch information
mozillazg committed Apr 22, 2024
1 parent e3b2761 commit 5afac24
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 21 deletions.
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@

# Dependency directories (remove the comment below to include it)
# vendor/
test.pcapng
ptcpdump
ptcpdump.pcapng
/ptcpdump
/output/
/*.pcapng
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "lib/libpcap"]
path = lib/libpcap
url = https://github.com/the-tcpdump-group/libpcap.git
54 changes: 49 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,56 @@
.ONESHELL:
SHELL = /bin/sh

GIT = $(shell which git || /bin/false)
OUTPUT = ./output


BPF_SRC = ./bpf
LIBPCAP = ./lib/libpcap
LIBPCAP_SRC = $(abspath $(LIBPCAP))
LIBPCAP_DIST_DIR = $(abspath $(OUTPUT)/libpcap)
LIBPCAP_HEADER_DIR = $(abspath $(LIBPCAP_DIST_DIR)/include)
LIBPCAP_OBJ_DIR = $(abspath $(LIBPCAP_DIST_DIR)/lib)
LIBPCAP_OBJ = $(abspath $(LIBPCAP_OBJ_DIR)/libpcap.a)

CGO_CFLAGS_STATIC = "-I$(LIBPCAP_HEADER_DIR)"
CGO_LDFLAGS_STATIC = "-L$(LIBPCAP_OBJ_DIR) -lelf -lz $(LIBPCAP_OBJ)"


.PHONY: libpcap
libpcap: $(LIBPCAP_OBJ)

$(LIBPCAP_OBJ): $(LIBPCAP_SRC) $(wildcard $(LIBPCAP_SRC)/*.[ch]) | $(LIBPCAP_DIST_DIR)
cd $(LIBPCAP_SRC) && \
./configure --enable-dbus=no && \
$(MAKE) && \
$(MAKE) install prefix=$(LIBPCAP_DIST_DIR)

$(LIBPCAP_SRC):
ifeq ($(wildcard $@), )
echo "INFO: updating submodule 'libpcap'"
$(GIT) submodule update --init --recursive
endif

$(LIBPCAP_DIST_DIR): $(OUTPUT)
mkdir -p $(LIBPCAP_DIST_DIR)

$(OUTPUT):
mkdir -p $(OUTPUT)


.PHONY: build
build: generate
CGO_ENABLED=0 go build -ldflags '-extldflags "-static"'
build: generate libpcap
CGO_CFLAGS=$(CGO_CFLAGS_STATIC) \
CGO_LDFLAGS=$(CGO_LDFLAGS_STATIC) \
CGO_ENABLED=1 go build -tags static -ldflags '-extldflags "-static"'

.PHONY: generate
generate:
go generate ./...

.PHONY: run
run:
sudo ./ptcpdump
.PHONY: clean
clean:
$(MAKE) -C $(LIBPCAP_SRC) clean
rm -rf $(OUTPUT)
rm -f ./ptcpdump
34 changes: 30 additions & 4 deletions bpf/bpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package bpf
import (
"encoding/binary"
"log"
"strings"
"unsafe"

"github.com/cilium/ebpf"
Expand All @@ -11,6 +12,7 @@ import (
"github.com/cilium/ebpf/ringbuf"
"github.com/florianl/go-tc"
"github.com/florianl/go-tc/core"
"github.com/jschwinger233/elibpcap"
"golang.org/x/sys/unix"
"golang.org/x/xerrors"
)
Expand All @@ -31,6 +33,7 @@ type Options struct {
Pid uint32
Comm [16]int8
FollowForks uint8
PcapFilter string
}

func NewBPF() (*BPF, error) {
Expand All @@ -44,7 +47,7 @@ func NewBPF() (*BPF, error) {
}, nil
}

func NewOptions(pid uint, comm string, followForks bool) Options {
func NewOptions(pid uint, comm string, followForks bool, pcapFilter string) Options {
opts := Options{
Pid: uint32(pid),
}
Expand All @@ -62,20 +65,43 @@ func NewOptions(pid uint, comm string, followForks bool) Options {
if followForks {
opts.FollowForks = 1
}
opts.PcapFilter = strings.TrimSpace(pcapFilter)

return opts
}

func (b *BPF) Load(opts Options) error {
if err := b.spec.RewriteConstants(map[string]interface{}{
err := b.spec.RewriteConstants(map[string]interface{}{
"filter_pid": opts.Pid,
"filter_comm": opts.Comm,
"filter_follow_forks": opts.FollowForks,
}); err != nil {
})
if err != nil {
return xerrors.Errorf("rewrite constants: %w", err)
}

err := b.spec.LoadAndAssign(b.objs, &ebpf.CollectionOptions{
if opts.PcapFilter != "" {
for _, progName := range []string{"tc_ingress", "tc_egress"} {
prog, ok := b.spec.Programs[progName]
if !ok {
return xerrors.Errorf("program %s not found", progName)
}
prog.Instructions, err = elibpcap.Inject(
opts.PcapFilter,
prog.Instructions,
elibpcap.Options{
AtBpf2Bpf: "pcap_filter",
DirectRead: true,
L2Skb: true,
},
)
if err != nil {
return xerrors.Errorf("inject pcap filter: %w", err)
}
}
}

err = b.spec.LoadAndAssign(b.objs, &ebpf.CollectionOptions{
Programs: ebpf.ProgramOptions{
LogLevel: ebpf.LogLevelInstruction,
LogSize: ebpf.DefaultVerifierLogSize * 8,
Expand Down
Binary file modified bpf/bpf_x86_bpfel.o
Binary file not shown.
27 changes: 19 additions & 8 deletions bpf/ptcpdump.c
Original file line number Diff line number Diff line change
Expand Up @@ -420,13 +420,21 @@ int BPF_KPROBE(kprobe__security_sk_classify_flow, struct sock *sk) {
return 0;
};

static __always_inline int handle_tc(struct __sk_buff *skb, bool egress) {
static __noinline bool pcap_filter(void *_skb, void *__skb, void *___skb, void *data, void* data_end) {
return data != data_end && _skb == __skb && __skb == ___skb;
};

static __always_inline void handle_tc(struct __sk_buff *skb, bool egress) {
bpf_skb_pull_data(skb, 0);

if (!pcap_filter((void *)skb, (void *)skb, (void *)skb, (void *)(long)skb->data, (void *)(long)skb->data_end)) {
return;
}

struct packet_meta_t packet_meta = {0};
int ret = parse_skb_meta(skb, &packet_meta);
if (ret < 0) {
return TC_ACT_OK;
return;
}


Expand All @@ -446,7 +454,7 @@ static __always_inline int handle_tc(struct __sk_buff *skb, bool egress) {
}

if (key.sport == 0) {
return TC_ACT_OK;
return;
}


Expand All @@ -457,15 +465,16 @@ static __always_inline int handle_tc(struct __sk_buff *skb, bool egress) {
// bpf_printk("[tc] (%s) %pI4 %d", value->comm, &key.saddr[0], key.sport);
} else {
/* bpf_printk("[tc] %pI4 %d bpf_map_lookup_elem is empty", &key.saddr[0], key.sport); */
return TC_ACT_OK;
return;
}

struct packet_event_t *event;
u32 zero = 0;
event = bpf_map_lookup_elem(&bpf_stack, &zero);
if (!event) {
return TC_ACT_OK;
return;
}
// __builtin_memset(event, 0, sizeof(*event));

if (egress) {
event->meta.packet_type = EGRESS_PACKET;
Expand All @@ -485,7 +494,7 @@ static __always_inline int handle_tc(struct __sk_buff *skb, bool egress) {
bpf_perf_event_output(skb, &packet_events, BPF_F_CURRENT_CPU | (payload_len <<32),
event, offsetof(struct packet_event_t, payload));

return TC_ACT_OK;
return;
}

static __always_inline void handle_exec(struct trace_event_raw_sched_process_exec *ctx) {
Expand Down Expand Up @@ -542,10 +551,12 @@ int tracepoint__sched__sched_process_exec(struct trace_event_raw_sched_process_e

SEC("tc")
int tc_ingress(struct __sk_buff *skb) {
return handle_tc(skb, false);
handle_tc(skb, false);
return TC_ACT_OK;
};

SEC("tc")
int tc_egress(struct __sk_buff *skb) {
return handle_tc(skb, true);
handle_tc(skb, true);
return TC_ACT_OK;
};
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/cilium/ebpf v0.14.0
github.com/florianl/go-tc v0.4.3
github.com/gopacket/gopacket v1.2.0
github.com/jschwinger233/elibpcap v0.0.0-20231010035657-e99300096f5e
github.com/shirou/gopsutil/v3 v3.24.3
github.com/vishvananda/netlink v1.1.0
github.com/x-way/pktdump v0.0.5
Expand All @@ -14,12 +15,14 @@ require (
)

require (
github.com/cloudflare/cbpfc v0.0.0-20230809125630-31aa294050ff // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mdlayher/netlink v1.6.0 // indirect
github.com/mdlayher/socket v0.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2u
github.com/cilium/ebpf v0.8.1/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk=
github.com/cilium/ebpf v0.14.0 h1:0PsxAjO6EjI1rcT+rkp6WcCnE0ZvfkXBYiMedJtrSUs=
github.com/cilium/ebpf v0.14.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso=
github.com/cloudflare/cbpfc v0.0.0-20230809125630-31aa294050ff h1:SLLG1soGN/PYTXkYWiR1PAxWlP1URBvgZPYymC5+0WI=
github.com/cloudflare/cbpfc v0.0.0-20230809125630-31aa294050ff/go.mod h1:1xLQNNpgw8yNlVjGrtUHBx0KL52fif71sKa1PleulAQ=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
Expand Down Expand Up @@ -30,6 +32,8 @@ github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/rasw
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/jschwinger233/elibpcap v0.0.0-20231010035657-e99300096f5e h1:UpDAKPdosPU3cCr8pZepHsirJjMlowczu/ACb2/xK4Q=
github.com/jschwinger233/elibpcap v0.0.0-20231010035657-e99300096f5e/go.mod h1:fUmq00C6Pechtr089JDPhvIc6TxrbUHDlZ5QCYc9tJQ=
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
Expand Down Expand Up @@ -72,6 +76,8 @@ github.com/mozillazg/gopacket v0.0.0-20240420072046-71afeafe42df h1:dP09t/Mu44X7
github.com/mozillazg/gopacket v0.0.0-20240420072046-71afeafe42df/go.mod h1:lnXM4VDqJTe4d2NoZr8DZMtidkhss2Y82QFlamXWfXo=
github.com/mozillazg/pktdump v0.0.0-20240421054319-759ababbd329 h1:mZ5vMcl+pQG4u1lotN3Me9T99n3dNv+xWjhZy+tVmdE=
github.com/mozillazg/pktdump v0.0.0-20240421054319-759ababbd329/go.mod h1:InLCDK8kgkk26VtyPZ51e0igf15eiXDMvvQuV62Wqmw=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
Expand Down
1 change: 1 addition & 0 deletions lib/libpcap
Submodule libpcap added at 104271
5 changes: 4 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"math"
"os"
"os/signal"
"strings"
"syscall"
)

Expand All @@ -29,6 +30,7 @@ type Options struct {
comm string
followForks bool
writeFilePath string
pcapFilter string
}

func (o Options) WritePath() string {
Expand Down Expand Up @@ -114,6 +116,7 @@ func setupFlags() *Options {
flag.BoolVar(&opts.followForks, "follow-forks", false, "Trace child processes when filter by process")
flag.Parse()

opts.pcapFilter = strings.Join(flag.Args(), " ")
return opts
}

Expand Down Expand Up @@ -159,7 +162,7 @@ func main() {
logErr(err)
return
}
if err := bf.Load(bpf.NewOptions(opts.pid, opts.comm, opts.followForks)); err != nil {
if err := bf.Load(bpf.NewOptions(opts.pid, opts.comm, opts.followForks, opts.pcapFilter)); err != nil {
logErr(err)
return
}
Expand Down

0 comments on commit 5afac24

Please sign in to comment.