From nobody Mon Feb 9 23:14:43 2026 Received: from mail-pj1-f65.google.com (mail-pj1-f65.google.com [209.85.216.65]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7AE3B2EA755 for ; Fri, 31 Oct 2025 08:00:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.65 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761897619; cv=none; b=JZcs/yLsmxOkRz64cKpcU2MSMwVjJyLKveg/oaUqwu91fdzBAlljDIxUfWr36FazXzihXX0tx2pM0/ovzzTFpkuHknfhShM3J5KbxtIaI5RWhEYvFazZ6Hx4riWahkFJ5Ui8xXgFKenAILTX/A9fpZOSC9eebYRbExz/h6NhXmI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761897619; c=relaxed/simple; bh=hxFri/KrVTPIFvuVZR/+6IB1+026xsuK0O7ukr/RSDQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=FBUfUnHbRRu6en3rEECjJvpNUlkaW+u9uIOL4HwriJMiOrZ/+kKS6iZ0QJP8a1T1wLb32LFbV9Gxb/PZagyAn2H9ckexw0/QcEENLdXU8V/Vg9HM1Ow9QwwVQuszDmWT0kRqL3Os0bRUeJRZlNq8P8Fvaf5lxIq/W6hYqdsw6sc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Y102qaL3; arc=none smtp.client-ip=209.85.216.65 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Y102qaL3" Received: by mail-pj1-f65.google.com with SMTP id 98e67ed59e1d1-339d7c403b6so1924698a91.2 for ; Fri, 31 Oct 2025 01:00:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1761897616; x=1762502416; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=/IBeku+7OSqR7bZxUebGlRtAXA6L2SMktYHxRLyyFCE=; b=Y102qaL3RAjN/7p4Ucew+r84G80G/5HeX9iMd3omMqFnDe431hrkH13/WGfbT+Uw+e Ok+9YE8652h2H9cO3mUXNvDbMOtV/DL3sX8NKac5KyvHLZpymauO5YVJpO6y00Hd6Q38 vKVkPDWNRBIyIWI9IuWsVVTX9sidyfv9eMYQTFg0ctldptX/udmUkY0LB9HyqlsgYjqO Eg654rpzPQ2Y8Pm2Nc5nXyqFVvEF8CUO9pk9JtSx+dA/lbtHvsGNnLchBNjZ5f7RopqV BH8Ghq8l2i0RJJzMmH1s6d4p1jQM8tOk4o9SiQBrtZBb8+iNzNkAnN7HYj718dr/QsIQ 7vEQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1761897616; x=1762502416; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=/IBeku+7OSqR7bZxUebGlRtAXA6L2SMktYHxRLyyFCE=; b=B0gzj9ridg6sFVtnBPh7+UBp4Av0LpGJ6nTX6VJ/dJoMiXHFjpVkAUJ8xThQ7E9rM6 eqrRZJXACqSZ0AJHAVzwCOUT4EugLAGaZfNdcMX5ep4rykNPq5+9chbsRgMk5LRjgfo9 QgDM+1wbuN5Y2tywd0QyoGP/rBWiz90mC/zE7i6tkmuhP4SUT8coi6rMrd88e8OJ2qRx RVHn8NwSmYjXeBLM1E5cSXzJu9d254tRhnN/7sicWcL+fasCK6XsczsoBjGOPItc9eUy 5gcSfwgfkwwouyAJjRqosFZNcesoiSz2CDL3FSgiPdO8/XrcBtfoEV+F4o5IixEOG6bR h/dw== X-Gm-Message-State: AOJu0YzcEPYnWvDLmCTt3vFYyI9ERS0Rve6Xxi2emw+d7D/w2PY/jRUk +6oRZ8DKSalI75ASrFF7gzmlcMHq7KJNfXxZd6UhGDjkWtDl93bV2BTYraTyfPJ9 X-Gm-Gg: ASbGncsLyE8ME9XTqltsgd9NXAVyA56sXoFJULABH7AWDxUZKgZ5qSiA7VHonvMYsco 3PRKn3TZeL7+7syRjZxobLyK4ydFq1U8s+xP4fGOh4uU4y0vRn+e+FjgBI7z7oqpIufbDe6Dxnu ewu0G/qrPjlxYUazHBoOzavKDs7qCQD4VGgTiIKy48s4Ly6zmEOIH6vFZ0JPjSaCGS+c+ELP5tw RxNRX1V6B0oP8wbR5ABkNulZro+y8UyVPcfgmjLuhsg+toUneZYSUo8m2Jb5mAE6Hw4y06igWMa q87exHJusPTq5fitUhaAHCtvPDguApIElEVhs5of8TNIrxdLqv1oUVahRZ6emHMCEc7s8kljrbi 6c1cd/V255p9Ip9y3Kb5Nu+EWkxQSK6Ghi8bESzx4CqCh6KgOt8puSlnyPtvs/if54KBz0oAwUI Boosq/G+PeD45CXpFZYOdeWU6g/Vz6 X-Google-Smtp-Source: AGHT+IGeoo6eVsiB8x7+YUC9Q7dJX+1EQtFsCbAEMK4Lw4YiaING5vuQwYARP7SFKRS2JKK/GaIjJQ== X-Received: by 2002:a17:90b:2d81:b0:340:7380:d092 with SMTP id 98e67ed59e1d1-3408307c81emr3765278a91.26.1761897615975; Fri, 31 Oct 2025 01:00:15 -0700 (PDT) Received: from E07P150077.ecarx.com.cn ([103.52.189.23]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-b93be4045fbsm1216575a12.28.2025.10.31.01.00.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 31 Oct 2025 01:00:15 -0700 (PDT) From: Jianyun Gao To: linux-kernel@vger.kernel.org Cc: Jianyun Gao , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , John Fastabend , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , "David S. Miller" , Jakub Kicinski , Jesper Dangaard Brouer , bpf@vger.kernel.org (open list:BPF [GENERAL] (Safe Dynamic Programs and Tools)), netdev@vger.kernel.org (open list:XDP (eXpress Data Path):Keyword:(?:\b|_)xdp(?:\b|_)) Subject: [PATCH v2 3/5] libbpf: Add doxygen documentation for bpf_link_* APIs in bpf.h Date: Fri, 31 Oct 2025 15:59:05 +0800 Message-Id: <20251031075908.1472249-4-jianyungao89@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251031075908.1472249-1-jianyungao89@gmail.com> References: <20251031075908.1472249-1-jianyungao89@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add doxygen comment blocks for all public bpf_link_* APIs in tools/lib/bpf/bpf.h. These doc comments are for: -bpf_link_create() -bpf_link_detach() -bpf_link_update() -bpf_link_get_next_id() -bpf_link_get_fd_by_id() -bpf_link_get_fd_by_id_opts() Signed-off-by: Jianyun Gao --- v1->v2: - Fixed the non-ASCII characters in this patch. The v1 is here: https://lore.kernel.org/lkml/20251031032627.1414462-4-jianyungao89@gmail.co= m/ tools/lib/bpf/bpf.h | 482 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 479 insertions(+), 3 deletions(-) diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index cd96d7afed6b..9040fc891b81 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -1208,11 +1208,195 @@ struct bpf_link_create_opts { size_t :0; }; #define bpf_link_create_opts__last_field uprobe_multi.pid - +/** + * @brief Create a persistent BPF link that attaches a loaded BPF program = to a + * kernel hook or target object. + * + * bpf_link_create() wraps the BPF_LINK_CREATE syscall command and establi= shes + * a first-class in-kernel "link" object representing the attachment of + * @p prog_fd to @p target_fd (or to a kernel entity implied by @p attach_= type). + * The returned FD (>=3D 0) owns the lifetime of that attachment: closing = it + * cleanly detaches the program without requiring a separate detach syscal= l. + * + * Compared to legacy bpf_prog_attach()/bpf_raw_tracepoint_open(), link-ba= sed + * attachment: + * - Provides explicit lifetime control (close(link_fd) =3D=3D detach). + * - Enables richer introspection via bpf_link_get_info_by_fd(). + * - Avoids ambiguous detach semantics and races inherent in "implicit d= etach + * on last program FD close" patterns. + * + * Typical usage: + * struct bpf_link_create_opts opts =3D { + * .sz =3D sizeof(opts), + * .flags =3D 0, + * }; + * int link_fd =3D bpf_link_create(prog_fd, target_fd, BPF_TRACE_FENTRY,= &opts); + * if (link_fd < 0) { + * // handle error + * } + * // ... use link_fd; close(link_fd) to detach later. + * + * @param prog_fd + * File descriptor of a previously loaded BPF program (from bpf_pro= g_load() + * or libbpf higher-level loader). Must be valid and compatible with + * @p attach_type. + * + * @param target_fd + * File descriptor of the attach target, when required by @p attach= _type + * (e.g. a cgroup FD, perf event FD, network interface, or another = BPF + * object). For some attach types (e.g. certain tracing variants) t= his may + * be -1 or ignored; passing an inappropriate FD yields -EINVAL. + * + * @param attach_type + * Enumeration value (enum bpf_attach_type) describing the hook/con= text + * at which the program should be executed (e.g. BPF_CGROUP_INET_IN= GRESS, + * BPF_TRACE_FENTRY, BPF_PERF_EVENT, BPF_NETFILTER, etc.). The prog= ram's + * bpf_prog_type and expected_attach_type must be compatible; other= wise + * verification will fail or the syscall returns -EINVAL/-EOPNOTSUP= P. + * + * @param opts + * Optional pointer to a zero-initialized struct bpf_link_create_op= ts + * extended options; may be NULL for defaults. Must set opts->sz to + * sizeof(struct bpf_link_create_opts) when non-NULL. + * + * Common fields: + * - .flags: Link creation flags (most callers set 0; future kern= els + * may define bits for pinning behaviors, exclusivity, etc.). + * - .target_btf_id: For BTF-enabled tracing/fentry/fexit/kprobe = multi + * scenarios, identifies a BTF entity (function/type) this link + * targets. + * - .iter_info / .iter_info_len: Provide iterator-specific metad= ata + * for BPF iter programs. + * + * Attach-type specific nested unions: + * - .perf_event.bpf_cookie: User-defined cookie visible to progr= am via + * bpf_get_attach_cookie() for PERF_EVENT and some tracing type= s. + * - .kprobe_multi: Batch (multi) kprobe attachment: + * * flags: KPROBE_MULTI_* flags controlling semantics. + * * cnt: Number of symbols/addresses. + * * syms / addrs: Symbol names or raw addresses (one of th= em + * used depending on kernel capabilities). + * * cookies: Optional per-probe cookies. + * - .uprobe_multi: Batch uprobes: + * * path: Target binary path. + * * offsets / ref_ctr_offsets: Instruction/file offsets and + * optional reference counter offsets. + * * pid: Target PID (0 for any or to let kernel decide). + * * cookies: Per-uprobe cookies. + * - .tracing.cookie: Generic tracing cookie for newer tracing ty= pes. + * - .netfilter: Attaching to Netfilter with: + * * pf (protocol family), hooknum, priority, flags. + * - .tcx / .netkit / .cgroup: Relative attachment variants allow= ing + * multi-attach ordering and revision consistency: + * * relative_fd / relative_id: Anchor or neighbor link/pro= gram. + * * expected_revision: Revision check to avoid races (fail= with + * -ESTALE if mismatch). + * + * Zero any fields you do not explicitly use for forward compatibil= ity. + * + * @return + * >=3D 0 : Link file descriptor (attachment active). + * < 0 : Negative error code (attachment failed; program not attached). + * + * Error Handling (negative libbpf-style codes; errno also set): + * - -EINVAL: Invalid prog_fd/target_fd/attach_type combination, malform= ed + * opts, bad sizes, unsupported flags, or missing required un= ion + * fields. + * - -EOPNOTSUPP / -ENOTSUP: Attach type or creation mode unsupported by + * running kernel. + * - -EPERM / -EACCES: Insufficient privileges (CAP_BPF/CAP_SYS_ADMIN) or + * blocked by LSM/lockdown. + * - -ENOENT: Target object no longer exists (race) or unresolved symbol= for + * kprobe/uprobes multi-attach. + * - -EBADF: Invalid file descriptor(s). + * - -ENOMEM: Kernel memory/resource exhaustion. + * - -ESTALE: Revision mismatch when using expected_revision (atomicity = guard). + * - Other negative codes: Propagated from underlying bpf() syscall fail= ures. + * + * Lifetime & Ownership: + * - Success returns a link FD. Caller must close() it to detach. + * - Closing the original program FD does NOT detach the link; only clos= ing + * the link FD (or explicit bpf_link_detach()) does. + * - Link FDs can be pinned to bpffs via bpf_obj_pin() for persistence. + * + * Concurrency & Races: + * - Linking can fail if another concurrent operation changes target's s= tate + * (revision checks can mitigate using expected_revision). + * - Multi-attach environments may reorder relative attachments if not u= sing + * relative_* fields; always inspect returned link state if ordering m= atters. + * + * Introspection: + * - Use bpf_link_get_info_by_fd(link_fd, ...) to query link metadata + * (program ID, attach type, target, cookies, multi-probe details). + * - Enumerate existing links via bpf_link_get_next_id() then open with + * bpf_link_get_fd_by_id(). + * + */ LIBBPF_API int bpf_link_create(int prog_fd, int target_fd, enum bpf_attach_type attach_type, const struct bpf_link_create_opts *opts); - +/** + * @brief Detach (tear down) an existing BPF link represented by a link fi= le descriptor. + * + * bpf_link_detach() issues the BPF_LINK_DETACH command to the kernel, bre= aking + * the association between a previously created BPF link (see bpf_link_cre= ate()) + * and its target (cgroup, tracing hook, perf event, netfilter hook, etc.)= . After + * a successful call the program will no longer be invoked at that attach = point. + * + * In most cases you do not need to call bpf_link_detach() explicitly; sim= ply + * closing the link FD (close(link_fd)) also detaches the link. This helpe= r is + * useful when you want to explicitly detach early while keeping the FD op= en for + * introspection (e.g., querying link info after detachment) or when build= ing + * higher-level lifecycle abstractions. + * + * Semantics: + * - Success makes the in-kernel link inactive; subsequent events at the= hook + * no longer trigger the program. + * - The link FD itself does NOT automatically close; you are still resp= onsible + * for close(link_fd) to release user space resources. + * - Repeated calls after a successful detach will fail (idempotency: on= ly the + * first detach succeeds). + * + * Typical usage: + * int link_fd =3D bpf_link_create(prog_fd, target_fd, attach_type, &opt= s); + * ... + * if (bpf_link_detach(link_fd) < 0) + * perror("bpf_link_detach"); + * close(link_fd); // optional: now just releases the FD + * + * Concurrency & races: + * - Detaching can race with another thread closing or detaching the sam= e link. + * In such cases you may observe -EBADF or -ENOENT. + * - Once detached, the program can be safely re-attached elsewhere if d= esired + * (requires a new link via bpf_link_create()). + * + * Privileges: + * - Usually requires CAP_BPF and/or CAP_SYS_ADMIN depending on kernel + * configuration, LSM, and lockdown mode. Lack of privileges yields -E= PERM + * or -EACCES. + * + * Post-detach: + * - The program object remains loaded; its own FD is still valid and ca= n be + * attached again. + * - Maps referenced by the program are unaffected. + * + * @param link_fd File descriptor of the active BPF link to detach; must h= ave + * been obtained via bpf_link_create() or equivalent. + * + * @return 0 on success; < 0 on failure (negative error code as described = above). + * + * Error handling (negative libbpf-style return codes, errno also set): + * - -EBADF: link_fd is not a valid open file descriptor. + * - -EINVAL: link_fd does not refer to a BPF link, or the kernel does n= ot + * support BPF_LINK_DETACH for this link type. + * - -ENOENT: Link already detached or no longer exists (race with close= ()). + * - -EPERM / -EACCES: Insufficient privileges or denied by security pol= icy. + * - -EOPNOTSUPP / -ENOTSUP: Kernel lacks support for link detachment of= this + * specific attach type. + * - -ENOMEM: Transient kernel resource exhaustion (rare in this path). + * - Other negative codes may be propagated from the underlying bpf() sy= scall. + * + */ LIBBPF_API int bpf_link_detach(int link_fd); =20 struct bpf_link_update_opts { @@ -1222,7 +1406,89 @@ struct bpf_link_update_opts { __u32 old_map_fd; /* expected old map FD */ }; #define bpf_link_update_opts__last_field old_map_fd - +/** + * @brief Atomically replace (update) the BPF program or map referenced by= an + * existing link with a new program. + * + * bpf_link_update() wraps the BPF_LINK_UPDATE command of the bpf(2) sysca= ll. + * It allows retargeting an already established BPF link (identified by + * link_fd) to point at a different loaded BPF program (new_prog_fd) witho= ut + * having to tear the link down (detach) and recreate it. This is typically + * used for hot-swapping a program while preserving: + * - Link pinning (bpffs path remains valid). + * - Relative ordering in multi-attach contexts (TC/XDP/cgroup revisions= ). + * - Existing references held by other processes. + * + * Consistency & safety: + * - The update is performed atomically: events arriving at the hook will + * either see the old program before the call, or the new one after the + * call; no window exists with an unattached link. + * - Optional expectations can be enforced via @p opts to avoid races: + * * old_prog_fd: Fail with -ESTALE if the link does not currently + * reference that program. + * * old_map_fd: (Kernel dependent) Can be used when links encapsul= ate + * a map association; if set and mismatched, update fails. + * * flags: Future extension bits (must be 0 on current kernels). + * + * Typical usage: + * struct bpf_link_update_opts u =3D { + * .sz =3D sizeof(u), + * .flags =3D 0, + * .old_prog_fd =3D old_fd, // set to 0 to skip validation + * }; + * if (bpf_link_update(link_fd, new_prog_fd, &u) < 0) + * perror("bpf_link_update"); + * + * Preconditions: + * - link_fd must refer to a valid, updatable BPF link. Not all link typ= es + * support in-place program replacement; unsupported types return -EOP= NOTSUPP. + * - new_prog_fd must be a loaded BPF program whose type and expected at= tach + * type are compatible with the link's attach context. + * - If @p opts is non-NULL, opts->sz MUST be set to sizeof(*opts). + * + * @param link_fd + * File descriptor of the existing BPF link to be updated. + * @param new_prog_fd + * File descriptor of the newly loaded BPF program that should repl= ace + * the currently attached program. + * @param opts + * Optional pointer to bpf_link_update_opts controlling validation: + * - sz: Structure size for forward/backward compatibility. + * - flags: Reserved; must be 0 (unsupported bits yield -EINVAL). + * - old_prog_fd: Expected current program FD (0 to skip check). + * - old_map_fd: Expected current map FD (0 to skip; kernel-spec= ific). + * Pass NULL for default (no expectation checks). + * + * @return + * 0 on success (link now points to new_prog_fd). + * <0 negative libbpf-style error code (typically -errno): + * - -EBADF: Invalid link_fd or new_prog_fd. + * - -EINVAL: Malformed opts (bad sz/flags) or incompatible p= rogram type. + * - -EOPNOTSUPP: Link type does not support updates. + * - -EPERM / -EACCES: Insufficient privileges (CAP_BPF/CAP_S= YS_ADMIN) or blocked by LSM. + * - -ENOENT: Link no longer exists (race) or old_prog_fd ref= ers to a non-existent program. + * - -ESTALE: Expectation mismatch (old_prog_fd / old_map_fd = differs). + * - -ENOMEM: Kernel resource allocation failure. + * - Other -errno codes propagated from the bpf() syscall. + * + * Postconditions: + * - On success, the old program remains loaded; caller should close its= FD + * if no longer needed. + * - Pinning status and link ID are preserved. + * - Maps referenced by the new program must be valid; no automatic rebi= nding + * occurs beyond program substitution. + * + * Caveats: + * - If verifier features differ (e.g., CO-RE relocations) ensure the new + * program was loaded with compatible expectations for the same hook. + * - Updating to a program of a strictly different attach semantics (e.g= ., + * sleepable vs non-sleepable) is rejected if the link type disallows = it. + * + * Thread safety: + * - Safe to call concurrently with other update attempts; only one succ= eeds. + * - Consumers of the link see either old or new program; intermediate s= tates + * are not observable. + */ LIBBPF_API int bpf_link_update(int link_fd, int new_prog_fd, const struct bpf_link_update_opts *opts); =20 @@ -1338,6 +1604,72 @@ LIBBPF_API int bpf_prog_get_next_id(__u32 start_id, = __u32 *next_id); */ LIBBPF_API int bpf_map_get_next_id(__u32 start_id, __u32 *next_id); LIBBPF_API int bpf_btf_get_next_id(__u32 start_id, __u32 *next_id); +/** + * @brief Retrieve the next existing BPF link ID after a given starting ID. + * + * This helper wraps the kernel's BPF_LINK_GET_NEXT_ID command and enumera= tes + * system-wide BPF link objects (each representing a persistent attachment= of + * a BPF program) in strictly ascending order of their kernel-assigned IDs. + * It is typically used to iterate over all currently existing BPF links f= rom + * user space. + * + * Enumeration pattern: + * 1. Initialize start_id to 0 to obtain the first (lowest) existing lin= k ID. + * 2. On success, *next_id is set to the first link ID strictly greater = than start_id. + * 3. Use the returned *next_id as the new start_id for the subsequent c= all. + * 4. Repeat until the function returns -ENOENT, indicating there is no = link + * with ID greater than start_id (end of enumeration). + * + * Concurrency & races: + * - Links can be created or detached concurrently with enumeration. A l= ink ID + * you just retrieved might become invalid before you convert it to an= FD + * (via bpf_link_get_fd_by_id()). Always handle failures when opening = by ID. + * - Enumeration does not provide a consistent snapshot; links created a= fter + * you pass their predecessor ID may appear in later iterations. + * + * Lifetime considerations: + * - Link IDs are monotonically increasing and not reused until wraparou= nd + * (effectively unreachable in normal operation). + * - Successfully retrieving an ID does not pin or otherwise prevent link + * detachment; obtain an FD immediately if you need to interact with t= he link. + * + * Usage example: + * __u32 id =3D 0, next; + * while (bpf_link_get_next_id(id, &next) =3D=3D 0) { + * int link_fd =3D bpf_link_get_fd_by_id(next); + * if (link_fd >=3D 0) { + * // Inspect link (e.g., bpf_link_get_info_by_fd(link_fd)) + * close(link_fd); + * } + * id =3D next; + * } + * // Loop terminates when -ENOENT is returned. + * + * @param start_id + * Starting point for the search. The helper finds the first link ID + * strictly greater than start_id. Use 0 to begin enumeration. + * @param next_id + * Pointer to a __u32 that receives the next link ID on success. + * Must not be NULL. + * + * @return + * 0 on success (next_id populated); + * -ENOENT if there is no link ID greater than start_id (end of it= eration); + * -EINVAL if next_id is NULL or invalid arguments were supplied; + * -EPERM / -EACCES if denied by security policy or lacking require= d privileges; + * Other negative libbpf-style errors (-errno) on transient or syst= em failures. + * + * Error handling notes: + * - Treat -ENOENT as normal termination (not an error condition). + * - For other negative returns, errno will also be set to the underlyin= g cause. + * + * After enumeration: + * - Convert retrieved IDs to FDs with bpf_link_get_fd_by_id() for intro= spection + * or detachment (via bpf_link_detach()). + * - Closing the FD does not destroy the link if other references remain= (e.g., + * pinned in bpffs); the link persists until explicitly detached or all + * references are released. + */ LIBBPF_API int bpf_link_get_next_id(__u32 start_id, __u32 *next_id); =20 struct bpf_get_fd_by_id_opts { @@ -1548,9 +1880,153 @@ LIBBPF_API int bpf_map_get_fd_by_id_opts(__u32 id, LIBBPF_API int bpf_btf_get_fd_by_id(__u32 id); LIBBPF_API int bpf_btf_get_fd_by_id_opts(__u32 id, const struct bpf_get_fd_by_id_opts *opts); +/** + * @brief Obtain a file descriptor for an existing BPF link given its kern= el-assigned ID. + * + * bpf_link_get_fd_by_id() wraps the BPF_LINK_GET_FD_BY_ID command of the = bpf(2) + * syscall. A BPF "link" is a persistent in-kernel object representing an + * attachment of a BPF program to some hook (cgroup, tracing point, perf e= vent, + * netfilter hook, tc/xdp chain, etc.). Each link has a unique, monotonica= lly + * increasing ID. This helper converts such an ID into a process-local file + * descriptor, allowing user space to inspect, pin, update, or detach the = link. + * + * Typical enumeration + open pattern: + * __u32 id =3D 0, next; + * while (bpf_link_get_next_id(id, &next) =3D=3D 0) { + * int link_fd =3D bpf_link_get_fd_by_id(next); + * if (link_fd >=3D 0) { + * // Use link_fd (e.g. bpf_link_get_info_by_fd(), bpf_link_deta= ch(), pin) + * close(link_fd); + * } + * id =3D next; + * } + * // Loop terminates when bpf_link_get_next_id() returns -ENOENT. + * + * Concurrency & races: + * - A link may be detached (or otherwise invalidated) between discoveri= ng its ID + * and calling this function. In that case the call fails with -ENOENT. + * - Successfully retrieving a file descriptor does not prevent later de= tachment + * by other processes; always handle subsequent operation failures gra= cefully. + * + * Lifetime & ownership: + * - On success, the caller owns the returned FD and must close() it whe= n done. + * - Closing the FD decreases the user space reference count; the underl= ying link + * persists while any references (FDs or pinned bpffs path) remain. + * - Detaching the link (via bpf_link_detach() or closing the last activ= e FD) + * invalidates future operations on that FD. + * + * Privileges / access control: + * - May require CAP_BPF and/or CAP_SYS_ADMIN depending on kernel config= uration, + * LSM policy, or lockdown mode. Lack of privileges yields -EPERM / -E= ACCES. + * - Security policies can deny access even if the link ID exists. + * + * Error handling (negative libbpf-style codes; errno is also set): + * - -ENOENT: No link with the specified ID (never existed or already de= tached). + * - -EPERM / -EACCES: Insufficient privilege or blocked by security pol= icy. + * - -EINVAL: Invalid ID (e.g., 0) or kernel rejected the request (rare). + * - -ENOMEM: Transient kernel resource exhaustion while creating the FD. + * - -EBADF, -EFAULT, or other -errno values: Propagated from the underl= ying syscall. + * + * Usage notes: + * - Immediately call bpf_link_get_info_by_fd() after acquiring the FD i= f you need + * metadata (program ID, attach type, target, cookie, etc.). + * - To keep a link across process restarts, pin it to bpffs via bpf_obj= _pin(). + * - Prefer using bpf_link_get_fd_by_id_opts() if you need extended open= semantics + * (e.g., token-based delegated permissions) on newer kernels. + * + * @param id + * Kernel-assigned unique ID of the target BPF link (must be > 0). = Usually + * obtained via bpf_link_get_next_id() or from a prior info query. + * + * @return + * >=3D 0 : File descriptor referring to the BPF link (caller must = close()). + * < 0 : Negative error code (libbpf-style, typically -errno) on f= ailure. + */ LIBBPF_API int bpf_link_get_fd_by_id(__u32 id); +/** + * @brief Obtain a file descriptor for an existing BPF link by kernel-assi= gned link ID + * with extended open options. + * + * bpf_link_get_fd_by_id_opts() is an extended variant of bpf_link_get_fd_= by_id(). + * It wraps the BPF_LINK_GET_FD_BY_ID command of the bpf(2) syscall and co= nverts a + * stable, monotonically increasing BPF link ID into a process-local file = descriptor + * while honoring optional attributes supplied via @p opts. + * + * A BPF "link" represents a persistent attachment of a BPF program to som= e kernel + * hook (cgroup, tracing point, perf event, netfilter, tc/xdp chain, etc.)= . Links can + * be enumerated system-wide by first calling bpf_link_get_next_id(). + * + * Typical enumeration + open pattern: + * __u32 id =3D 0, next; + * while (bpf_link_get_next_id(id, &next) =3D=3D 0) { + * struct bpf_get_fd_by_id_opts o =3D { + * .sz =3D sizeof(o), + * .open_flags =3D 0, + * .token_fd =3D 0, + * }; + * int link_fd =3D bpf_link_get_fd_by_id_opts(next, &o); + * if (link_fd >=3D 0) { + * // inspect link (e.g. bpf_link_get_info_by_fd(link_fd)) + * close(link_fd); + * } + * id =3D next; + * } + * // Loop ends when bpf_link_get_next_id() returns -ENOENT (no more lin= ks). + * + * Concurrency & races: + * - A link may detach between enumeration and opening; handle -ENOENT g= racefully. + * - Successfully obtaining a FD does not prevent future detachment by o= ther processes; + * subsequent operations (e.g., bpf_link_get_info_by_fd()) can still f= ail. + * + * Lifetime & ownership: + * - The returned FD holds a user-space reference; close() decrements it. + * - The underlying link persists while any references remain (FDs or bp= ffs pin). + * - Use bpf_obj_pin() to make the link persistent across process lifeti= mes. + * + * Security: + * - CAP_BPF and/or CAP_SYS_ADMIN may be required depending on kernel co= nfiguration. + * - Token-based access (token_fd) can allow operations in sandboxed env= ironments. + * + * Follow-up introspection: + * - Call bpf_link_get_info_by_fd(link_fd, ...) to retrieve program ID, = attach type, + * target info, cookies, and other metadata. + * - Detach via bpf_link_detach(link_fd) or simply close(link_fd). + * + * Recommended usage notes: + * - Always zero-initialize the opts struct before setting fields. + * - Treat -ENOENT after enumeration as normal termination, not an error= condition. + * - Avoid relying on stable ordering beyond ascending ID sequence; link= s created + * during enumeration may appear after you pass their predecessor ID. + * + * @param id + * Kernel-assigned unique (non-zero) BPF link ID. Usually obtained from + * bpf_link_get_next_id() or from a prior info query. Must be > 0. + * + * @param opts + * Optional pointer to a zero-initialized struct bpf_get_fd_by_id_opts: + * - sz: MUST be set to sizeof(struct bpf_get_fd_by_id_opts) if @p opts + * is non-NULL (enables fwd/backward compatibility). + * - open_flags: Additional open/access flags (currently most callers = set 0; + * unsupported bits yield -EINVAL; semantics are kernel-= specific). + * - token_fd: File descriptor of a BPF token granting delegated permi= ssions + * (set 0 or -1 if unused). Allows restricted environments= to + * open the link without elevated global capabilities. + * Pass NULL for defaults (equivalent to open_flags=3D0, no token). + * + * @return + * >=3D 0 : File descriptor referencing the BPF link (caller owns it; cl= ose() when done). + * < 0 : Negative libbpf-style error code (typically -errno): + * - -ENOENT : Link with @p id does not exist (detached or nev= er created). + * - -EPERM / -EACCES : Insufficient privilege or blocked by LS= M/lockdown. + * - -EINVAL : Invalid @p id (0), malformed @p opts (bad sz / = flags), or + * unsupported open_flags. + * - -ENOMEM : Transient kernel memory/resource exhaustion. + * - Other negative codes: Propagated from underlying bpf() sys= call. + * + */ LIBBPF_API int bpf_link_get_fd_by_id_opts(__u32 id, const struct bpf_get_fd_by_id_opts *opts); + LIBBPF_API int bpf_obj_get_info_by_fd(int bpf_fd, void *info, __u32 *info_= len); =20 /** --=20 2.34.1