Skip to content

Commit 02ce9de

Browse files
committed
feat(dropwatch): read kernel skb_drop_reason and resolve its name
Read the kfree_skb tracepoint reason field via CO-RE (a trace_event_raw_kfree_skb flavor + bpf_core_field_exists), falling back to the SKB_DROP_REASON_UNSUPPORT sentinel on kernels older than v5.17 that lack the field. The tracepoint attach point and ctx type are unchanged. In userspace, resolve the numeric reason to its name from the running kernel's BTF (enum skb_drop_reason) at startup, handle the v6.4+ subsystem high-16-bits encoding, and surface it in both text and JSON output. Change-Id: Ia7a11d20eab1fdb7c4f1e8ca4e0c38e4fec17982
1 parent 3d5fd43 commit 02ce9de

3 files changed

Lines changed: 78 additions & 14 deletions

File tree

bpf/dropwatch.c

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,6 @@
2121
#define SK_FL_TYPE_SHIFT 16
2222
#define SK_FL_TYPE_MASK 0xffff0000
2323

24-
/* Reserved for future kernel skb_drop_reason (SKB_DROP_REASON_NOT_SPECIFIED,
25-
* ...) passthrough; (u32)-1 is out of band: kernel reason values grow upward
26-
* from 0 (low 16 bits code, high 16 bits subsystem since v6.4) and can never
27-
* reach it. */
28-
#define SKB_DROP_REASON_UNSUPPORT ((u32)-1)
2924
#define PKT_RAW_LEN 120
3025

3126
struct packet_meta {
@@ -38,11 +33,10 @@ struct packet_meta {
3833
u32 dev_flags; /* 4 */
3934
u32 queue_mapping; /* 4 */
4035
u32 drop_reason; /* 4 */
41-
u32 type; /* 4 */
4236
u32 net_inum; /* 4 */
4337
u8 dev_name[IFNAMSIZ]; /* 16 */
4438
u8 comm[COMPAT_TASK_COMM_LEN]; /* 16 */
45-
}; /* total: 96 bytes */
39+
}; /* 92 bytes + 4 tail pad = 96 */
4640

4741
struct packet_raw {
4842
u16 eth_proto; /* 2 */
@@ -86,6 +80,31 @@ char __license[] SEC("license") = "Dual MIT/GPL";
8680
static const struct drop_packet_event zero_data = {};
8781
static const u32 stackmap_key = 0;
8882

83+
/* kfree_skb gained an skb drop reason field in v5.17, absent from the BTF this
84+
* object is compiled against. Carry it in a CO-RE flavor relocated at load time:
85+
* the anonymous enum field matches the kernel's enum skb_drop_reason by the name
86+
* "reason". No reason constants are hardcoded (their values shift across kernels);
87+
* names are resolved at runtime from kernel BTF in userspace (loadDropReasonNames).
88+
*
89+
* SKB_DROP_REASON_UNSUPPORT = -1 satisfies C's "enum needs >=1 enumerator" rule
90+
* and is the out-of-band fallback for kernels predating the field: as (u32)-1 it
91+
* can never collide with real reasons (which grow from 0). */
92+
struct trace_event_raw_kfree_skb___reason {
93+
enum { SKB_DROP_REASON_UNSUPPORT = -1 } reason;
94+
} __attribute__((preserve_access_index));
95+
96+
/* Return the kernel skb drop reason when the running kernel supports it,
97+
* otherwise the out-of-band SKB_DROP_REASON_UNSUPPORT sentinel. */
98+
static inline u32 skb_get_drop_reason(struct trace_event_raw_kfree_skb *ctx)
99+
{
100+
struct trace_event_raw_kfree_skb___reason *ctx_reason = (void *)ctx;
101+
102+
if (bpf_core_field_exists(ctx_reason->reason))
103+
return BPF_CORE_READ(ctx_reason, reason);
104+
105+
return SKB_DROP_REASON_UNSUPPORT;
106+
}
107+
89108
struct sock___5_10 {
90109
u16 sk_type;
91110
u16 sk_protocol;
@@ -209,8 +228,7 @@ int bpf_kfree_skb_prog(struct trace_event_raw_kfree_skb *ctx)
209228
bpf_get_current_comm(&data->meta.comm, sizeof(data->meta.comm));
210229
data->meta.kfree_skb_addr = (u64)(unsigned long)ctx->location;
211230
data->meta.queue_mapping = BPF_CORE_READ(skb, queue_mapping);
212-
data->meta.drop_reason = SKB_DROP_REASON_UNSUPPORT;
213-
data->meta.type = 0;
231+
data->meta.drop_reason = skb_get_drop_reason(ctx);
214232

215233
data->pkt_hdr.pkt_len = BPF_CORE_READ(skb, len);
216234

cmd/dropwatch/format.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ type writer interface {
3333
type textWriter struct{ w io.Writer }
3434

3535
func (s *textWriter) Write(ev *types.DropWatchTracing) error {
36-
if _, err := fmt.Fprintf(s.w, "%s %s len=%d dev=%s pid=%d[%s] addr=%s\n",
36+
if _, err := fmt.Fprintf(s.w, "%s %s len=%d dev=%s pid=%d[%s] addr=%s reason=%s\n",
3737
ev.ObservedTimestamp, ev.Layers,
38-
ev.PacketLen, ev.NetdevName, ev.Pid, ev.Comm, ev.PacketSkbAddr); err != nil {
38+
ev.PacketLen, ev.NetdevName, ev.Pid, ev.Comm, ev.PacketSkbAddr, ev.DropReason); err != nil {
3939
return err
4040
}
4141

cmd/dropwatch/main.go

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"time"
2727
"unsafe"
2828

29+
"github.com/cilium/ebpf/btf"
2930
"github.com/urfave/cli/v2"
3031
"golang.org/x/sys/unix"
3132

@@ -78,7 +79,6 @@ type packetMeta struct {
7879
NetdevFlags uint32
7980
NetdevQueueMapping uint32
8081
DropReason uint32
81-
Type uint32
8282
NetInode uint32
8383
NetdevName [bpf.NetdevNameLen]byte
8484
Comm [bpf.TaskCommLen]byte
@@ -192,7 +192,50 @@ func installDeviceFilter(b bpf.BPF, mode uint32, ifindexes []uint32) error {
192192
return b.WriteMapItems(mapID, items)
193193
}
194194

195-
func formatEvent(ev *dropPacketEvent) *types.DropWatchTracing {
195+
// dropReasonUnsupport mirrors SKB_DROP_REASON_UNSUPPORT in bpf/dropwatch.c: the
196+
// sentinel emitted on kernels whose kfree_skb tracepoint has no reason field.
197+
const dropReasonUnsupport uint32 = 0xffffffff
198+
199+
// loadDropReasonNames builds a map from skb_drop_reason value to name by reading
200+
// the running kernel's BTF. Returns nil when BTF or the enum is unavailable, in
201+
// which case reasons render numerically (see dropReasonName).
202+
func loadDropReasonNames() map[uint32]string {
203+
spec, err := btf.LoadKernelSpec()
204+
if err != nil {
205+
return nil
206+
}
207+
208+
var e *btf.Enum
209+
if err := spec.TypeByName("skb_drop_reason", &e); err != nil {
210+
return nil
211+
}
212+
213+
names := make(map[uint32]string, len(e.Values))
214+
for _, v := range e.Values {
215+
names[uint32(v.Value)] = v.Name
216+
}
217+
return names
218+
}
219+
220+
// dropReasonName translates a raw skb drop reason into its kernel name. The
221+
// out-of-band sentinel renders as SKB_DROP_REASON_UNSUPPORT. Since v6.4 the high
222+
// 16 bits carry the subsystem; only the CORE subsystem (0) maps to
223+
// enum skb_drop_reason. Unknown values and non-CORE subsystems render as hex.
224+
func dropReasonName(reason uint32, names map[uint32]string) string {
225+
if reason == dropReasonUnsupport {
226+
return "SKB_DROP_REASON_UNSUPPORT"
227+
}
228+
229+
if reason>>16 == 0 {
230+
if name, ok := names[reason&0xffff]; ok {
231+
return name
232+
}
233+
}
234+
235+
return fmt.Sprintf("0x%x", reason)
236+
}
237+
238+
func formatEvent(ev *dropPacketEvent, reasonNames map[uint32]string) *types.DropWatchTracing {
196239
pkt := packet.Hdr{
197240
EthProto: ev.Raw.EthProto,
198241
RawLen: uint8(ev.Raw.RawLen),
@@ -211,6 +254,7 @@ func formatEvent(ev *dropPacketEvent) *types.DropWatchTracing {
211254

212255
return &types.DropWatchTracing{
213256
ObservedTimestamp: time.Now().UTC().Format(time.RFC3339Nano),
257+
DropReason: dropReasonName(ev.Meta.DropReason, reasonNames),
214258
Comm: bytesutil.ToStr(ev.Meta.Comm[:]),
215259
Pid: ev.Meta.TgidPid >> 32,
216260
MemoryCgroupCSSAddr: kernaddr.Format(ev.Meta.MemoryCgroupCSSAddr),
@@ -349,6 +393,8 @@ func mainAction(c *cli.Context) error {
349393

350394
sink := newWriter(outputFmt, sockClient)
351395

396+
reasonNames := loadDropReasonNames()
397+
352398
var ev dropPacketEvent
353399

354400
for {
@@ -366,7 +412,7 @@ func mainAction(c *cli.Context) error {
366412
continue
367413
}
368414

369-
if err := sink.Write(formatEvent(&ev)); err != nil {
415+
if err := sink.Write(formatEvent(&ev, reasonNames)); err != nil {
370416
log.Errorf("dropwatch: send event: %v", err)
371417
return nil
372418
}

0 commit comments

Comments
 (0)