From: WanBingjiang Date: Wed, 15 Jul 2026 08:33:26 +0000 (+0800) Subject: lsfd: introduce bpf-link anon-inode support X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=524ce8a3a8480d6f11206352579e73536748e50f;p=thirdparty%2Futil-linux.git lsfd: introduce bpf-link anon-inode support Add BPF link file descriptor decoding to lsfd, following the same pattern as bpf-prog and bpf-map. Signed-off-by: WanBingjiang --- diff --git a/lsfd-cmd/lsfd.1.adoc b/lsfd-cmd/lsfd.1.adoc index 469116bee..6d95a995e 100644 --- a/lsfd-cmd/lsfd.1.adoc +++ b/lsfd-cmd/lsfd.1.adoc @@ -144,6 +144,18 @@ Association between file and process. BLKDRV <``string``>:: Block device driver name resolved by `/proc/devices`. +BPF-LINK.ID <``number``>:: +Bpf link ID. + +BPF-LINK.TYPE <``string``>:: +Decoded name of bpf link type. + +BPF-LINK.TYPE.RAW <``number``>:: +Bpf link type (raw). + +BPF-LINK.PROG-ID <``number``>:: +ID of the bpf-prog that this link attaches. + BPF-MAP.ID <``number``>:: Bpf map ID. @@ -299,6 +311,9 @@ state=_SOCK.STATE_ type=_SOCK.TYPE_ laddr=_VSOCK.LADDR_[ raddr=_VSOCK.RADDR_] + `raddr` is not shown for listening sockets. + +bpf-link::: +id=_BPF-LINK.ID_ type=_BPF-LINK.TYPE_[ prog=_BPF-LINK.PROG-ID_] ++ bpf-map::: id=_BPF-MAP.ID_ type=_BPF-MAP.TYPE_[ name=_BPF.NAME_] + diff --git a/lsfd-cmd/lsfd.c b/lsfd-cmd/lsfd.c index ea5022dec..988d37e42 100644 --- a/lsfd-cmd/lsfd.c +++ b/lsfd-cmd/lsfd.c @@ -186,6 +186,18 @@ static const struct colinfo infos[] = { [COL_BLKDRV] = { "BLKDRV", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING, N_("block device driver name resolved by /proc/devices") }, + [COL_BPF_LINK_ID] = { "BPF-LINK.ID", + 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, + N_("bpf link ID") }, + [COL_BPF_LINK_TYPE] = { "BPF-LINK.TYPE", + 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING, + N_("bpf link type (decoded)") }, + [COL_BPF_LINK_TYPE_RAW]= { "BPF-LINK.TYPE.RAW", + 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, + N_("bpf link type (raw)") }, + [COL_BPF_LINK_PROG_ID] = { "BPF-LINK.PROG-ID", + 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, + N_("bpf prog ID that this link attaches") }, [COL_BPF_MAP_ID] = { "BPF-MAP.ID", 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, N_("bpf map id associated with the fd") }, diff --git a/lsfd-cmd/lsfd.h b/lsfd-cmd/lsfd.h index bb8f85bb8..70c344ee9 100644 --- a/lsfd-cmd/lsfd.h +++ b/lsfd-cmd/lsfd.h @@ -57,6 +57,10 @@ enum { COL_AINODECLASS, COL_ASSOC, COL_BLKDRV, + COL_BPF_LINK_ID, + COL_BPF_LINK_PROG_ID, + COL_BPF_LINK_TYPE, + COL_BPF_LINK_TYPE_RAW, COL_BPF_MAP_ID, COL_BPF_MAP_TYPE, COL_BPF_MAP_TYPE_RAW, diff --git a/lsfd-cmd/unkn.c b/lsfd-cmd/unkn.c index 3bac2feb4..555a404c4 100644 --- a/lsfd-cmd/unkn.c +++ b/lsfd-cmd/unkn.c @@ -1318,6 +1318,159 @@ static const struct anon_ops anon_bpf_map_ops = { .handle_fdinfo = anon_bpf_map_handle_fdinfo, }; +/* + * bpf-link + */ +static const char *const bpf_link_type_table[] = { + [0] = "unspec", /* BPF_LINK_TYPE_UNSPEC */ + [1] = "raw_tracepoint", /* BPF_LINK_TYPE_RAW_TRACEPOINT */ + [2] = "tracing", /* BPF_LINK_TYPE_TRACING */ + [3] = "cgroup", /* BPF_LINK_TYPE_CGROUP */ + [4] = "iter", /* BPF_LINK_TYPE_ITER */ + [5] = "netns", /* BPF_LINK_TYPE_NETNS */ + [6] = "xdp", /* BPF_LINK_TYPE_XDP */ + [7] = "perf_event", /* BPF_LINK_TYPE_PERF_EVENT */ + [8] = "kprobe_multi", /* BPF_LINK_TYPE_KPROBE_MULTI */ + [9] = "struct_ops", /* BPF_LINK_TYPE_STRUCT_OPS */ + [10] = "netfilter", /* BPF_LINK_TYPE_NETFILTER */ + [11] = "tcx", /* BPF_LINK_TYPE_TCX */ + [12] = "uprobe_multi", /* BPF_LINK_TYPE_UPROBE_MULTI */ + [13] = "netkit", /* BPF_LINK_TYPE_NETKIT */ + [14] = "sockmap", /* BPF_LINK_TYPE_SOCKMAP */ +}; + +struct anon_bpf_link_data { + int id; + int prog_id; + char *type_str; /* "link_type" string from fdinfo */ +}; + +static bool anon_bpf_link_probe(const char *str) +{ + return strncmp(str, "bpf_link", 8) == 0; +} + +static int anon_bpf_link_type_name_to_id(const char *name) +{ + for (size_t i = 0; i < ARRAY_SIZE(bpf_link_type_table); i++) + if (bpf_link_type_table[i] + && strcmp(bpf_link_type_table[i], name) == 0) + return (int)i; + return -1; +} + +static bool anon_bpf_link_fill_column(struct proc *proc __attribute__((__unused__)), + struct unkn *unkn, + struct libscols_line *ln __attribute__((__unused__)), + int column_id, + size_t column_index __attribute__((__unused__)), + char **str) +{ + struct anon_bpf_link_data *data = (struct anon_bpf_link_data *)unkn->anon_data; + int t; + + switch(column_id) { + case COL_BPF_LINK_ID: + if (data->id >= 0) + xasprintf(str, "%d", data->id); + return true; + case COL_BPF_LINK_TYPE_RAW: + if (data->type_str && data->type_str[0]) { + t = anon_bpf_link_type_name_to_id(data->type_str); + if (t >= 0) + xasprintf(str, "%d", t); + } + return true; + case COL_BPF_LINK_TYPE: + if (data->type_str && data->type_str[0]) + *str = xstrdup(data->type_str); + return true; + case COL_BPF_LINK_PROG_ID: + if (data->prog_id >= 0) + xasprintf(str, "%d", data->prog_id); + return true; + default: + return false; + } +} + +static char *anon_bpf_link_get_name(struct unkn *unkn) +{ + char *str = NULL; + struct anon_bpf_link_data *data = (struct anon_bpf_link_data *)unkn->anon_data; + + if (data->type_str && data->type_str[0]) + xasprintf(&str, "id=%d type=%s", data->id, data->type_str); + else + xasprintf(&str, "id=%d", data->id); + + if (data->prog_id >= 0) + xstrfappend(&str, " prog=%d", data->prog_id); + + return str; +} + +static void anon_bpf_link_init(struct unkn *unkn) +{ + struct anon_bpf_link_data *data = xmalloc(sizeof(*data)); + data->id = -1; + data->prog_id = -1; + data->type_str = NULL; + unkn->anon_data = data; +} + +static void anon_bpf_link_free(struct unkn *unkn) +{ + struct anon_bpf_link_data *data = (struct anon_bpf_link_data *)unkn->anon_data; + free(data->type_str); + free(data); +} + +static int anon_bpf_link_handle_fdinfo(struct unkn *unkn, const char *key, const char *value) +{ + if (strcmp(key, "link_id") == 0) { + int32_t t = -1; + int rc = ul_strtos32(value, &t, 10); + if (rc < 0) + return 0; /* ignore -- parse failed */ + ((struct anon_bpf_link_data *)unkn->anon_data)->id = (int)t; + return 1; + } + + if (strcmp(key, "link_type") == 0) { + /* + * The kernel exposes link_type as a string (e.g. "xdp"). + * Store it directly; the integer id is reverse-looked-up + * via bpf_link_type_table for BPF-LINK.TYPE.RAW. + */ + free(((struct anon_bpf_link_data *)unkn->anon_data)->type_str); + ((struct anon_bpf_link_data *)unkn->anon_data)->type_str = xstrdup(value); + return 1; + } + + if (strcmp(key, "prog_id") == 0) { + int32_t t = -1; + int rc = ul_strtos32(value, &t, 10); + if (rc < 0) + return 0; /* ignore -- parse failed */ + ((struct anon_bpf_link_data *)unkn->anon_data)->prog_id = (int)t; + return 1; + } + + return 0; +} + +static const struct anon_ops anon_bpf_link_ops = { + .class = "bpf-link", + .probe = anon_bpf_link_probe, + .get_name = anon_bpf_link_get_name, + .fill_column = anon_bpf_link_fill_column, + .init = anon_bpf_link_init, + .free = anon_bpf_link_free, + .handle_fdinfo = anon_bpf_link_handle_fdinfo, +}; + + /* * generic (fallback implementation) */ @@ -1339,6 +1492,7 @@ static const struct anon_ops *const anon_ops[] = { &anon_inotify_ops, &anon_bpf_prog_ops, &anon_bpf_map_ops, + &anon_bpf_link_ops, }; static const struct anon_ops *anon_probe(const char *str)