Skip to content

Commit

Permalink
Add support for printing skb_shared_info
Browse files Browse the repository at this point in the history
It is useful to track the behavior of the segmentation offload behavior.

Signed-off-by: Yutaro Hayakawa <[email protected]>
  • Loading branch information
YutaroHayakawa authored and brb committed Apr 29, 2024
1 parent 836fa22 commit c076a4c
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 41 deletions.
61 changes: 61 additions & 0 deletions bpf/kprobe_pwru.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -46,6 +47,7 @@ struct tuple {
} __attribute__((packed));

u64 print_skb_id = 0;
u64 print_shinfo_id = 0;

struct event_t {
u32 pid;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down
12 changes: 8 additions & 4 deletions internal/pwru/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
Expand Down
69 changes: 48 additions & 21 deletions internal/pwru/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand All @@ -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 != "" {
Expand All @@ -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
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
}

Expand Down
5 changes: 4 additions & 1 deletion internal/pwru/tc_tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand Down
25 changes: 14 additions & 11 deletions internal/pwru/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type Flags struct {
OutputMeta bool
OutputTuple bool
OutputSkb bool
OutputShinfo bool
OutputStack bool
OutputLimitLines uint64
OutputFile string
Expand Down Expand Up @@ -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")

Expand Down Expand Up @@ -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
}
9 changes: 5 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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()
}

Expand Down Expand Up @@ -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)
}
Expand Down

0 comments on commit c076a4c

Please sign in to comment.