[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20260106085527.4774-1-gyutae.opensource@navercorp.com>
Date: Tue, 6 Jan 2026 17:55:27 +0900
From: gyutae.opensource@...ercorp.com
To: Quentin Monnet <qmo@...nel.org>,
bpf@...r.kernel.org
Cc: linux-kernel@...r.kernel.org,
Alexei Starovoitov <ast@...nel.org>,
Daniel Borkmann <daniel@...earbox.net>,
Andrii Nakryiko <andrii@...nel.org>,
Martin KaFai Lau <martin.lau@...ux.dev>,
Eduard Zingerman <eddyz87@...il.com>,
Song Liu <song@...nel.org>,
Yonghong Song <yonghong.song@...ux.dev>,
John Fastabend <john.fastabend@...il.com>,
KP Singh <kpsingh@...nel.org>,
Stanislav Fomichev <sdf@...ichev.me>,
Hao Luo <haoluo@...gle.com>,
Jiri Olsa <jolsa@...nel.org>,
Gyutae Bae <gyutae.bae@...ercorp.com>,
Siwan Kim <siwan.kim@...ercorp.com>,
Daniel Xu <dxu@...uu.xyz>,
Jiayuan Chen <jiayuan.chen@...ux.dev>,
Tao Chen <chen.dylane@...ux.dev>,
Kumar Kartikeya Dwivedi <memxor@...il.com>
Subject: [PATCH v2] bpftool: Add 'prepend' option for tcx attach to insert at chain start
From: Gyutae Bae <gyutae.bae@...ercorp.com>
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 queries the first program ID in the chain and uses
BPF_F_BEFORE flag with the relative_id to insert the new program before
the existing first program. If the chain is empty, the program is simply
attached normally.
This change includes:
- Add get_first_tcx_prog_id() helper to retrieve the first program ID
- 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 <siwan.kim@...ercorp.com>
Signed-off-by: Siwan Kim <siwan.kim@...ercorp.com>
Signed-off-by: Gyutae Bae <gyutae.bae@...ercorp.com>
---
Hi Quentin.
Apologies for the delay in getting back to you. I had to sort out
some configuration issues with my mail system to ensure proper
delivery.
I really appreciate your detailed review and feedback on the first
version. I have incorporated your suggestions in this v2 patch.
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 '== 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 | 47 +++++++++++++++++--
3 files changed, 74 insertions(+), 12 deletions(-)
diff --git a/tools/bpf/bpftool/Documentation/bpftool-net.rst b/tools/bpf/bpftool/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
============
| **bpftool** **net** { **show** | **list** } [ **dev** *NAME* ]
-| **bpftool** **net attach** *ATTACH_TYPE* *PROG* **dev** *NAME* [ **overwrite** ]
+| **bpftool** **net attach** *ATTACH_TYPE* *PROG* **dev** *NAME* [ **overwrite** | **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 bpf
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 specified
- by *ATTACH_TYPE*. Previously attached bpf program can be replaced by the
- command used with **overwrite** option. Currently, only XDP-related modes
- 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 does not support it;
@@ -72,11 +70,18 @@ bpftool net attach *ATTACH_TYPE* *PROG* dev *NAME* [ overwrite ]
**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** option
+ can be used to attach the program at the beginning of the chain instead of
+ at the end.
+
bpftool net detach *ATTACH_TYPE* dev *NAME*
Detach bpf program attached to network interface *NAME* with type specified
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..7935a6ba1491 100644
--- a/tools/bpf/bpftool/net.c
+++ b/tools/bpf/bpftool/net.c
@@ -637,6 +637,25 @@ static int net_parse_dev(int *argc, char ***argv)
return ifindex;
}
+static int get_first_tcx_prog_id(int ifindex, enum bpf_attach_type type, __u32 *first_id)
+{
+ int ret;
+
+ LIBBPF_OPTS(bpf_prog_query_opts, optq);
+ __u32 prog_ids[1] = {};
+
+ optq.prog_ids = prog_ids;
+ optq.count = ARRAY_SIZE(prog_ids);
+
+ ret = bpf_prog_query_opts(ifindex, type, &optq);
+
+ if (ret == 0 && optq.count > 0) {
+ *first_id = prog_ids[0];
+ return 0;
+ }
+ return -1;
+}
+
static int do_attach_detach_xdp(int progfd, enum net_attach_type attach_type,
int ifindex, bool overwrite)
{
@@ -666,10 +685,21 @@ static int get_tcx_type(enum net_attach_type attach_type)
}
}
-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 = get_tcx_type(attach_type);
+ if (prepend) {
+ __u32 relative_id;
+
+ if (!get_first_tcx_prog_id(ifindex, type, &relative_id)) {
+ LIBBPF_OPTS(bpf_prog_attach_opts, opts,
+ .flags = BPF_F_BEFORE | BPF_F_ID,
+ .relative_id = relative_id
+ );
+ return bpf_prog_attach_opts(progfd, ifindex, type, &opts);
+ }
+ }
return bpf_prog_attach(progfd, ifindex, type, 0);
}
@@ -685,6 +715,7 @@ static int do_attach(int argc, char **argv)
enum net_attach_type attach_type;
int progfd, ifindex, err = 0;
bool overwrite = false;
+ bool prepend = false;
/* parse attach args */
if (!REQ_ARGS(5))
@@ -710,8 +741,16 @@ static int do_attach(int argc, char **argv)
if (argc) {
if (is_prefix(*argv, "overwrite")) {
overwrite = true;
+ } else if (is_prefix(*argv, "prepend")) {
+ if (attach_type != NET_ATTACH_TYPE_TCX_INGRESS &&
+ attach_type != NET_ATTACH_TYPE_TCX_EGRESS) {
+ p_err("'prepend' is only supported for tcx_ingress/tcx_egress");
+ err = -EINVAL;
+ goto cleanup;
+ }
+ prepend = true;
} else {
- p_err("expected 'overwrite', got: '%s'?", *argv);
+ p_err("expected 'overwrite' or 'prepend', got: '%s'?", *argv);
err = -EINVAL;
goto cleanup;
}
@@ -728,7 +767,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 = do_attach_tcx(progfd, attach_type, ifindex);
+ err = do_attach_tcx(progfd, attach_type, ifindex, prepend);
break;
default:
break;
@@ -985,7 +1024,7 @@ static int do_help(int argc, char **argv)
fprintf(stderr,
"Usage: %1$s %2$s { show | list } [dev <devname>]\n"
- " %1$s %2$s attach ATTACH_TYPE PROG dev <devname> [ overwrite ]\n"
+ " %1$s %2$s attach ATTACH_TYPE PROG dev <devname> [ overwrite | prepend ]\n"
" %1$s %2$s detach ATTACH_TYPE dev <devname>\n"
" %1$s %2$s help\n"
"\n"
--
2.39.5 (Apple Git-154)
Powered by blists - more mailing lists