.../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(-)
From: Gyutae Bae <gyutae.bae@navercorp.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 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 <siwan.kim@navercorp.com>
Signed-off-by: Siwan Kim <siwan.kim@navercorp.com>
Signed-off-by: Gyutae Bae <gyutae.bae@navercorp.com>
---
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 '== 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/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..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_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) {
+ LIBBPF_OPTS(bpf_prog_attach_opts, opts,
+ .flags = 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 = 0;
bool overwrite = false;
+ bool prepend = 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 = 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 +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 = do_attach_tcx(progfd, attach_type, ifindex);
+ err = 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 <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)
On 07/01/2026 02:29, gyutae.opensource@navercorp.com wrote:
> From: Gyutae Bae <gyutae.bae@navercorp.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 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 <siwan.kim@navercorp.com>
> Signed-off-by: Siwan Kim <siwan.kim@navercorp.com>
> Signed-off-by: Gyutae Bae <gyutae.bae@navercorp.com>
> ---
> 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 '== 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/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_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) {
> + LIBBPF_OPTS(bpf_prog_attach_opts, opts,
> + .flags = 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 = 0;
> bool overwrite = false;
> + bool prepend = 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 = true;
Just one minor thing, can we error out here if the attach type is tcx
please? Like you do for "prepend" below, when it's not tcx. So that we
don't let users believe they're overwriting their program.
> + } 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;
> }
Looks good otherwise, thank you! Pending that change:
Reviewed-by: Quentin Monnet <qmo@kernel.org>
© 2016 - 2026 Red Hat, Inc.