diff --git a/bpf/kprobe_pwru.c b/bpf/kprobe_pwru.c index 28a9161f..5e531399 100644 --- a/bpf/kprobe_pwru.c +++ b/bpf/kprobe_pwru.c @@ -9,6 +9,7 @@ #include "bpf/bpf_ipv6.h" #define PRINT_SKB_STR_SIZE 2048 +#define PRINT_SHINFO_STR_SIZE PRINT_SKB_STR_SIZE #define ETH_P_IP 0x800 #define ETH_P_IPV6 0x86dd @@ -46,6 +47,7 @@ struct tuple { } __attribute__((packed)); u64 print_skb_id = 0; +u64 print_shinfo_id = 0; struct event_t { u32 pid; @@ -54,6 +56,7 @@ struct event_t { u64 skb_addr; u64 ts; typeof(print_skb_id) print_skb_id; + typeof(print_shinfo_id) print_shinfo_id; struct skb_meta meta; struct tuple tuple; s64 print_stack_id; @@ -97,6 +100,7 @@ struct config { u8 output_meta; u8 output_tuple; u8 output_skb; + u8 output_shinfo; u8 output_stack; u8 is_set; u8 track_skb; @@ -119,12 +123,22 @@ struct print_skb_value { u32 len; char str[PRINT_SKB_STR_SIZE]; }; +struct print_shinfo_value { + u32 len; + char str[PRINT_SHINFO_STR_SIZE]; +}; struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(max_entries, 256); __type(key, u32); __type(value, struct print_skb_value); } print_skb_map SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 256); + __type(key, u32); + __type(value, struct print_shinfo_value); +} print_shinfo_map SEC(".maps"); #endif static __always_inline u32 @@ -273,6 +287,49 @@ set_skb_btf(struct sk_buff *skb, typeof(print_skb_id) *event_id) { #endif } +static __always_inline void +set_shinfo_btf(struct sk_buff *skb, typeof(print_shinfo_id) *event_id) { +#ifdef OUTPUT_SKB + struct skb_shared_info *shinfo; + static struct btf_ptr p = {}; + struct print_shinfo_value *v; + typeof(print_shinfo_id) id; + unsigned char *head; + unsigned int end; + long n; + + /* skb_shared_info is located at the end of skb data. + * When CONFIG_NET_SKBUFF_DATA_USES_OFFSET is enabled, skb->end + * is an offset from skb->head to the end of skb data. If not, + * skb->end is a pointer to the end of skb data. For amd64 and + * arm64 (in 64bit arch in general), CONFIG_NET_SKBUFF_DATA_USES_OFFSET + * is enabled by default. + */ + head = BPF_CORE_READ(skb, head); + end = BPF_CORE_READ(skb, end); + shinfo = (struct skb_shared_info *)(head + end); + + p.type_id = bpf_core_type_id_kernel(struct skb_shared_info); + p.ptr = shinfo; + + id = __sync_fetch_and_add(&print_shinfo_id, 1) % 256; + + v = bpf_map_lookup_elem(&print_shinfo_map, (u32 *) &id); + if (!v) { + return; + } + + n = bpf_snprintf_btf(v->str, PRINT_SHINFO_STR_SIZE, &p, sizeof(p), 0); + if (n < 0) { + return; + } + + v->len = n; + + *event_id = id; +#endif +} + static __always_inline u64 get_stackid(struct pt_regs *ctx) { u64 caller_fp; @@ -303,6 +360,10 @@ set_output(void *ctx, struct sk_buff *skb, struct event_t *event) { set_skb_btf(skb, &event->print_skb_id); } + if (cfg->output_shinfo) { + set_shinfo_btf(skb, &event->print_shinfo_id); + } + if (cfg->output_stack) { event->print_stack_id = bpf_get_stackid(ctx, &print_stack_map, BPF_F_FAST_STACK_CMP); } diff --git a/internal/pwru/config.go b/internal/pwru/config.go index 3bd21b76..4587a6f3 100644 --- a/internal/pwru/config.go +++ b/internal/pwru/config.go @@ -24,10 +24,11 @@ type FilterCfg struct { FilterIfindex uint32 // TODO: if there are more options later, then you can consider using a bit map - OutputMeta uint8 - OutputTuple uint8 - OutputSkb uint8 - OutputStack uint8 + OutputMeta uint8 + OutputTuple uint8 + OutputSkb uint8 + OutputShinfo uint8 + OutputStack uint8 IsSet byte TrackSkb byte @@ -42,6 +43,9 @@ func GetConfig(flags *Flags) (cfg FilterCfg, err error) { if flags.OutputSkb { cfg.OutputSkb = 1 } + if flags.OutputShinfo { + cfg.OutputShinfo = 1 + } if flags.OutputMeta { cfg.OutputMeta = 1 } diff --git a/internal/pwru/output.go b/internal/pwru/output.go index 05d90b32..06e89b4c 100644 --- a/internal/pwru/output.go +++ b/internal/pwru/output.go @@ -31,20 +31,22 @@ import ( const absoluteTS string = "15:04:05.000" type output struct { - flags *Flags - lastSeenSkb map[uint64]uint64 // skb addr => last seen TS - printSkbMap *ebpf.Map - printStackMap *ebpf.Map - addr2name Addr2Name - writer *os.File - kprobeMulti bool - kfreeReasons map[uint64]string - ifaceCache map[uint64]map[uint32]string + flags *Flags + lastSeenSkb map[uint64]uint64 // skb addr => last seen TS + printSkbMap *ebpf.Map + printShinfoMap *ebpf.Map + printStackMap *ebpf.Map + addr2name Addr2Name + writer *os.File + kprobeMulti bool + kfreeReasons map[uint64]string + ifaceCache map[uint64]map[uint32]string } // outputStructured is a struct to hold the data for the json output type jsonPrinter struct { Skb string `json:"skb,omitempty"` + Shinfo string `json:"skb_shared_info,omitempty"` Cpu uint32 `json:"cpu,omitempty"` Process string `json:"process,omitempty"` Func string `json:"func,omitempty"` @@ -68,9 +70,7 @@ type jsonTuple struct { Proto uint8 `json:"proto,omitempty"` } -func NewOutput(flags *Flags, printSkbMap *ebpf.Map, printStackMap *ebpf.Map, - addr2Name Addr2Name, kprobeMulti bool, btfSpec *btf.Spec, -) (*output, error) { +func NewOutput(flags *Flags, printSkbMap, printShinfoMap, printStackMap *ebpf.Map, addr2Name Addr2Name, kprobeMulti bool, btfSpec *btf.Spec) (*output, error) { writer := os.Stdout if flags.OutputFile != "" { @@ -95,15 +95,16 @@ func NewOutput(flags *Flags, printSkbMap *ebpf.Map, printStackMap *ebpf.Map, } return &output{ - flags: flags, - lastSeenSkb: map[uint64]uint64{}, - printSkbMap: printSkbMap, - printStackMap: printStackMap, - addr2name: addr2Name, - writer: writer, - kprobeMulti: kprobeMulti, - kfreeReasons: reasons, - ifaceCache: ifs, + flags: flags, + lastSeenSkb: map[uint64]uint64{}, + printSkbMap: printSkbMap, + printShinfoMap: printShinfoMap, + printStackMap: printStackMap, + addr2name: addr2Name, + writer: writer, + kprobeMulti: kprobeMulti, + kfreeReasons: reasons, + ifaceCache: ifs, }, nil } @@ -177,6 +178,10 @@ func (o *output) PrintJson(event *Event) { d.SkbMetadata = getSkbData(event, o) } + if o.flags.OutputShinfo { + d.SkbMetadata = getShinfoData(event, o) + } + // Create new encoder to write the json to stdout or file depending on the flags encoder := json.NewEncoder(o.writer) encoder.SetEscapeHTML(false) @@ -264,6 +269,24 @@ func getSkbData(event *Event, o *output) (skbData string) { return "\n" + string(b[4:4+length]) } +func getShinfoData(event *Event, o *output) (shinfoData string) { + id := uint32(event.PrintShinfoId) + + b, err := o.printShinfoMap.LookupBytes(&id) + if err != nil { + return "" + } + + length := binary.NativeEndian.Uint32(b[:4]) + + // Bounds check + if int(length+4) > len(b) { + return "" + } + + return "\n" + string(b[4:4+length]) +} + func getMetaData(event *Event, o *output) (metaData string) { metaData = fmt.Sprintf("netns=%d mark=%#x iface=%s proto=%#04x mtu=%d len=%d", event.Meta.Netns, event.Meta.Mark, @@ -338,6 +361,10 @@ func (o *output) Print(event *Event) { fmt.Fprintf(o.writer, "%s", getSkbData(event, o)) } + if o.flags.OutputShinfo { + fmt.Fprintf(o.writer, "%s", getShinfoData(event, o)) + } + fmt.Fprintln(o.writer) } diff --git a/internal/pwru/tc_tracer.go b/internal/pwru/tc_tracer.go index 534a6d74..871a304d 100644 --- a/internal/pwru/tc_tracer.go +++ b/internal/pwru/tc_tracer.go @@ -94,7 +94,7 @@ func (t *tcTracer) trace(spec *ebpf.CollectionSpec, } func TraceTC(coll *ebpf.Collection, spec *ebpf.CollectionSpec, - opts *ebpf.CollectionOptions, outputSkb bool, n2a BpfProgName2Addr, + opts *ebpf.CollectionOptions, outputSkb bool, outputShinfo bool, n2a BpfProgName2Addr, ) func() { progs, err := listBpfProgs(ebpf.SchedCLS) if err != nil { @@ -110,6 +110,9 @@ func TraceTC(coll *ebpf.Collection, spec *ebpf.CollectionSpec, if outputSkb { replacedMaps["print_skb_map"] = coll.Maps["print_skb_map"] } + if outputShinfo { + replacedMaps["print_shinfo_map"] = coll.Maps["print_shinfo_map"] + } opts.MapReplacements = replacedMaps var tt tcTracer diff --git a/internal/pwru/types.go b/internal/pwru/types.go index ca2f688f..aed9b19f 100644 --- a/internal/pwru/types.go +++ b/internal/pwru/types.go @@ -41,6 +41,7 @@ type Flags struct { OutputMeta bool OutputTuple bool OutputSkb bool + OutputShinfo bool OutputStack bool OutputLimitLines uint64 OutputFile string @@ -73,6 +74,7 @@ func (f *Flags) SetFlags() { flag.BoolVar(&f.OutputMeta, "output-meta", false, "print skb metadata") flag.BoolVar(&f.OutputTuple, "output-tuple", false, "print L4 tuple") flag.BoolVar(&f.OutputSkb, "output-skb", false, "print skb") + flag.BoolVar(&f.OutputShinfo, "output-skb-shared-info", false, "print skb shared info") flag.BoolVar(&f.OutputStack, "output-stack", false, "print stack") flag.Uint64Var(&f.OutputLimitLines, "output-limit-lines", 0, "exit the program after the number of events has been received/printed") @@ -133,15 +135,16 @@ type StackData struct { } type Event struct { - PID uint32 - Type uint32 - Addr uint64 - SAddr uint64 - Timestamp uint64 - PrintSkbId uint64 - Meta Meta - Tuple Tuple - PrintStackId int64 - ParamSecond uint64 - CPU uint32 + PID uint32 + Type uint32 + Addr uint64 + SAddr uint64 + Timestamp uint64 + PrintSkbId uint64 + PrintShinfoId uint64 + Meta Meta + Tuple Tuple + PrintStackId int64 + ParamSecond uint64 + CPU uint32 } diff --git a/main.go b/main.go index a8d2db67..5927c0cc 100644 --- a/main.go +++ b/main.go @@ -111,9 +111,9 @@ func main() { var bpfSpec *ebpf.CollectionSpec switch { - case flags.OutputSkb && useKprobeMulti: + case (flags.OutputSkb || flags.OutputShinfo) && useKprobeMulti: bpfSpec, err = LoadKProbeMultiPWRU() - case flags.OutputSkb: + case flags.OutputSkb || flags.OutputShinfo: bpfSpec, err = LoadKProbePWRU() case useKprobeMulti: bpfSpec, err = LoadKProbeMultiPWRUWithoutOutputSKB() @@ -193,7 +193,7 @@ func main() { defer coll.Close() if flags.FilterTraceTc { - close := pwru.TraceTC(coll, bpfSpecFentry, &opts, flags.OutputSkb, name2addr) + close := pwru.TraceTC(coll, bpfSpecFentry, &opts, flags.OutputSkb, flags.OutputShinfo, name2addr) defer close() } @@ -304,8 +304,9 @@ func main() { } printSkbMap := coll.Maps["print_skb_map"] + printShinfoMap := coll.Maps["print_shinfo_map"] printStackMap := coll.Maps["print_stack_map"] - output, err := pwru.NewOutput(&flags, printSkbMap, printStackMap, addr2name, useKprobeMulti, btfSpec) + output, err := pwru.NewOutput(&flags, printSkbMap, printShinfoMap, printStackMap, addr2name, useKprobeMulti, btfSpec) if err != nil { log.Fatalf("Failed to create outputer: %s", err) }