From nobody Sat Feb 7 23:34:04 2026 Received: from cvsmtppost02.nmdf.navercorp.com (cvsmtppost02.nmdf.navercorp.com [114.111.35.59]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9A6E723EAB4 for ; Wed, 7 Jan 2026 02:29:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=114.111.35.59 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767752975; cv=none; b=CQ4W1Y4vi3b/J+iRa+Jz+wLH9+QWOY4k2IrdOM5PGuqCN1VTgVs8gL3crXW3xP95oyT2ryzlrL4zVucs/dboM4gjJUq3Vyi0jeM3an4lld7ldJ549Zrr7DJGcVs1jMZpHcZonrOZeDQ/MDfkl4UHcWiNKCWANPa8blDoG7rwQ2Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767752975; c=relaxed/simple; bh=vrmaHrm+Bj/vX/5LtR1MsluGwpf9RwxIyyEm0x6F5YY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=DtTfxsHbRFxnYooACPjvB/tqcoHeZLCNVrbAPeYvbZ6DuYhXGwDF5sNvRXEub9xASgfJVORjMC5lcLfGYAYbQ0UdrWO+2e+3OwVXGGkCP7lfwJ9dYz06CDk2UHUa9FbzZ3PXSYeYaYqTJ7W/G+vqoZM0WAiQLFV5/vICIp7jCJk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=navercorp.com; spf=pass smtp.mailfrom=navercorp.com; dkim=pass (2048-bit key) header.d=navercorp.com header.i=@navercorp.com header.b=kWUT7JrI; arc=none smtp.client-ip=114.111.35.59 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=navercorp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=navercorp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=navercorp.com header.i=@navercorp.com header.b="kWUT7JrI" Received: from cvsendbo03.nmdf ([10.112.20.53]) by cvsmtppost02.nmdf.navercorp.com with ESMTP id euJ6sJXSTgWBh9JlIoGzOg for ; Wed, 07 Jan 2026 02:29:30 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=navercorp.com; s=s20171120; t=1767752970; bh=vrmaHrm+Bj/vX/5LtR1MsluGwpf9RwxIyyEm0x6F5YY=; h=From:To:Subject:Date:Message-Id:From:Subject:Feedback-ID: X-Works-Security; b=kWUT7JrIcA4eWaRBs+M/BEZa83RsMJkpSR5WrrOEKwEPK9jrkavnZRXF1OAA8PYSI zX4nETfIos44NMJO0BCikMOVFjW9eiAg2VL5G+4PSfJTSlBIna+ABPmhBK5G1HzHsD u81aAZM+lm111ANmLeTWnhNh+Y9Ulmnu30mvopGk0rjUJUWdL/BJD0wZjV+E5K91dw OQcL39JEh2QrOexxxRRx282WHyMd5SRXq21iQPaXg4JC+mfexkzsZBkTbyt2noTIeH fIeHnvXP/4HEmiKWOr1isWvqFEuFp0th3Og2ux6GnrynrHTIWgsfdc4GC9Lo35MhFm mrZngkUltOvXQ== X-Session-ID: 1OH1s1e9RmaR0aFndciuog X-Works-Send-Opt: xQbwjAiYjHm2KHwYjHmlUVg= X-Works-Smtp-Source: QZKZFqbrFqJZ+HmmFqEm+6E= Received: from localhost.localdomain ([10.25.143.76]) by cvnsmtp01.nmdf.navercorp.com with ESMTP id 1OH1s1e9RmaR0aFndciuog for (version=TLSv1.3 cipher=TLS_CHACHA20_POLY1305_SHA256); Wed, 07 Jan 2026 02:29:30 -0000 From: gyutae.opensource@navercorp.com To: Quentin Monnet , bpf@vger.kernel.org, Daniel Borkmann Cc: linux-kernel@vger.kernel.org, Alexei Starovoitov , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , John Fastabend , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Gyutae Bae , Siwan Kim , Daniel Xu , Jiayuan Chen , Tao Chen , Kumar Kartikeya Dwivedi Subject: [PATCH v3] bpftool: Add 'prepend' option for tcx attach to insert at chain start Date: Wed, 7 Jan 2026 11:29:11 +0900 Message-Id: <20260107022911.81672-1-gyutae.opensource@navercorp.com> X-Mailer: git-send-email 2.39.5 (Apple Git-154) In-Reply-To: <43c23468-530b-45f3-af22-f03484e5148c@kernel.org> References: <43c23468-530b-45f3-af22-f03484e5148c@kernel.org> 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" From: Gyutae Bae Add support for the 'prepend' option when attaching tcx_ingress and tcx_egress programs. This option allows inserting a BPF program at the beginning of the TCX chain instead of appending it at the end. The implementation uses BPF_F_BEFORE flag which automatically inserts the program at the beginning of the chain when no relative reference is specified. This change includes: - Modify do_attach_tcx() to support prepend insertion using BPF_F_BEFORE - Update documentation to describe the new 'prepend' option - Add bash completion support for the 'prepend' option on tcx attach types - Add example usage in the documentation The 'prepend' option is only valid for tcx_ingress and tcx_egress attach types. For XDP attach types, the existing 'overwrite' option remains available. Example usage: # bpftool net attach tcx_ingress name tc_prog dev lo prepend This feature is useful when the order of program execution in the TCX chain matters and users need to ensure certain programs run first. Co-developed-by: Siwan Kim Signed-off-by: Siwan Kim Signed-off-by: Gyutae Bae Reviewed-by: Quentin Monnet --- Hi Daniel. Thank you for the detailed feedback. Thanks to your explanation, I now understand that BPF_F_BEFORE and BPF_F_AFTER work as standalone flags. This has made the implementation much simpler and cleaner. Thanks, Gyutae. Changes in v3: - Simplified implementation by using BPF_F_BEFORE alone (Daniel) - Removed get_first_tcx_prog_id() helper function (Daniel) Changes in v2: - Renamed 'head' to 'prepend' for consistency with 'overwrite' (Quentin) - Moved relative_id variable to relevant scope inside if block (Quentin) - Changed condition style from '=3D=3D 0' to '!' (Quentin) - Updated documentation to clarify 'overwrite' is XDP-only (Quentin) - Removed outdated "only XDP-related modes are supported" note (Quentin) - Removed extra help text from do_help() for consistency (Quentin) .../bpf/bpftool/Documentation/bpftool-net.rst | 30 ++++++++++++++----- tools/bpf/bpftool/bash-completion/bpftool | 9 +++++- tools/bpf/bpftool/net.c | 23 +++++++++++--- 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/tools/bpf/bpftool/Documentation/bpftool-net.rst b/tools/bpf/bp= ftool/Documentation/bpftool-net.rst index a9ed8992800f..22da07087e42 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-net.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-net.rst @@ -24,7 +24,7 @@ NET COMMANDS =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D | **bpftool** **net** { **show** | **list** } [ **dev** *NAME* ] -| **bpftool** **net attach** *ATTACH_TYPE* *PROG* **dev** *NAME* [ **overw= rite** ] +| **bpftool** **net attach** *ATTACH_TYPE* *PROG* **dev** *NAME* [ **overw= rite** | **prepend** ] | **bpftool** **net detach** *ATTACH_TYPE* **dev** *NAME* | **bpftool** **net help** | @@ -58,11 +58,9 @@ bpftool net { show | list } [ dev *NAME* ] then all bpf programs attached to non clsact qdiscs, and finally all b= pf programs attached to root and clsact qdisc. -bpftool net attach *ATTACH_TYPE* *PROG* dev *NAME* [ overwrite ] +bpftool net attach *ATTACH_TYPE* *PROG* dev *NAME* [ overwrite | prepend ] Attach bpf program *PROG* to network interface *NAME* with type specif= ied - by *ATTACH_TYPE*. Previously attached bpf program can be replaced by t= he - command used with **overwrite** option. Currently, only XDP-related mo= des - are supported for *ATTACH_TYPE*. + by *ATTACH_TYPE*. *ATTACH_TYPE* can be of: **xdp** - try native XDP and fallback to generic XDP if NIC driver doe= s not support it; @@ -72,11 +70,18 @@ bpftool net attach *ATTACH_TYPE* *PROG* dev *NAME* [ ov= erwrite ] **tcx_ingress** - Ingress TCX. runs on ingress net traffic; **tcx_egress** - Egress TCX. runs on egress net traffic; + For XDP-related attach types (**xdp**, **xdpgeneric**, **xdpdrv**, + **xdpoffload**), the **overwrite** option can be used to replace a + previously attached bpf program. + + For **tcx_ingress** and **tcx_egress** attach types, the **prepend** o= ption + can be used to attach the program at the beginning of the chain instea= d of + at the end. + bpftool net detach *ATTACH_TYPE* dev *NAME* Detach bpf program attached to network interface *NAME* with type spec= ified by *ATTACH_TYPE*. To detach bpf program, same *ATTACH_TYPE* previously= used - for attach must be specified. Currently, only XDP-related modes are - supported for *ATTACH_TYPE*. + for attach must be specified. bpftool net help Print short help message. @@ -191,6 +196,17 @@ EXAMPLES tc: lo(1) tcx/ingress tc_prog prog_id 29 +| +| **# bpftool net attach tcx_ingress name tc_prog2 dev lo prepend** +| **# bpftool net** +| + +:: + + tc: + lo(1) tcx/ingress tc_prog2 prog_id 30 + lo(1) tcx/ingress tc_prog prog_id 29 + | | **# bpftool net attach tcx_ingress name tc_prog dev lo** | **# bpftool net detach tcx_ingress dev lo** diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/= bash-completion/bpftool index 53bcfeb1a76e..a28f0cc522e4 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -1142,7 +1142,14 @@ _bpftool() return 0 ;; 8) - _bpftool_once_attr 'overwrite' + case ${words[3]} in + tcx_ingress|tcx_egress) + _bpftool_once_attr 'prepend' + ;; + *) + _bpftool_once_attr 'overwrite' + ;; + esac return 0 ;; esac diff --git a/tools/bpf/bpftool/net.c b/tools/bpf/bpftool/net.c index cfc6f944f7c3..1a2ba3312a82 100644 --- a/tools/bpf/bpftool/net.c +++ b/tools/bpf/bpftool/net.c @@ -666,10 +666,16 @@ static int get_tcx_type(enum net_attach_type attach_t= ype) } } -static int do_attach_tcx(int progfd, enum net_attach_type attach_type, int= ifindex) +static int do_attach_tcx(int progfd, enum net_attach_type attach_type, int= ifindex, bool prepend) { int type =3D get_tcx_type(attach_type); + if (prepend) { + LIBBPF_OPTS(bpf_prog_attach_opts, opts, + .flags =3D BPF_F_BEFORE + ); + return bpf_prog_attach_opts(progfd, ifindex, type, &opts); + } return bpf_prog_attach(progfd, ifindex, type, 0); } @@ -685,6 +691,7 @@ static int do_attach(int argc, char **argv) enum net_attach_type attach_type; int progfd, ifindex, err =3D 0; bool overwrite =3D false; + bool prepend =3D false; /* parse attach args */ if (!REQ_ARGS(5)) @@ -710,8 +717,16 @@ static int do_attach(int argc, char **argv) if (argc) { if (is_prefix(*argv, "overwrite")) { overwrite =3D true; + } else if (is_prefix(*argv, "prepend")) { + if (attach_type !=3D NET_ATTACH_TYPE_TCX_INGRESS && + attach_type !=3D NET_ATTACH_TYPE_TCX_EGRESS) { + p_err("'prepend' is only supported for tcx_ingress/tcx_egress"); + err =3D -EINVAL; + goto cleanup; + } + prepend =3D true; } else { - p_err("expected 'overwrite', got: '%s'?", *argv); + p_err("expected 'overwrite' or 'prepend', got: '%s'?", *argv); err =3D -EINVAL; goto cleanup; } @@ -728,7 +743,7 @@ static int do_attach(int argc, char **argv) /* attach tcx prog */ case NET_ATTACH_TYPE_TCX_INGRESS: case NET_ATTACH_TYPE_TCX_EGRESS: - err =3D do_attach_tcx(progfd, attach_type, ifindex); + err =3D do_attach_tcx(progfd, attach_type, ifindex, prepend); break; default: break; @@ -985,7 +1000,7 @@ static int do_help(int argc, char **argv) fprintf(stderr, "Usage: %1$s %2$s { show | list } [dev ]\n" - " %1$s %2$s attach ATTACH_TYPE PROG dev [ overwrite ]\n" + " %1$s %2$s attach ATTACH_TYPE PROG dev [ overwrite | pr= epend ]\n" " %1$s %2$s detach ATTACH_TYPE dev \n" " %1$s %2$s help\n" "\n" -- 2.39.5 (Apple Git-154)