]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsfd: introduce bpf-link anon-inode support
authorWanBingjiang <wanbingjiang@webray.com.cn>
Wed, 15 Jul 2026 08:33:26 +0000 (16:33 +0800)
committerWanBingjiang <wanbingjiang@webray.com.cn>
Mon, 20 Jul 2026 10:29:42 +0000 (18:29 +0800)
Add BPF link file descriptor decoding to lsfd, following the same
pattern as bpf-prog and bpf-map.

Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
lsfd-cmd/lsfd.1.adoc
lsfd-cmd/lsfd.c
lsfd-cmd/lsfd.h
lsfd-cmd/unkn.c

index 469116bee239e44ed65b0e8748ceede85ea32147..6d95a995ea546773161448be0bdd1ab2fb7c5a71 100644 (file)
@@ -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_]
 +
index ea5022decb8bcb82870fba3a6e186381452274ce..988d37e4291cfde06d4f407c18ad111e901dcfd1 100644 (file)
@@ -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") },
index bb8f85bb8ad312110c60ba2d11ba47bdcfd0aaf6..70c344ee9dc1aeb4de0345f157ce6110f7f5e9af 100644 (file)
@@ -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,
index 3bac2feb40d7d0c9957f6753a1344c8c29c838af..555a404c4af1b1233e09479d5ba2fc45db71fd81 100644 (file)
@@ -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)