On Wed, Jun 7, 2023 at 12:26 PM Daniel Borkmann <daniel@xxxxxxxxxxxxx> wrote: > > Implement tcx BPF link support for libbpf. > > The bpf_program__attach_fd_opts() API has been refactored slightly in order to > pass bpf_link_create_opts pointer as input. > > A new bpf_program__attach_tcx_opts() has been added on top of this which allows > for passing all relevant data via extensible struct bpf_tcx_opts. > > The program sections tcx/ingress and tcx/egress correspond to the hook locations > for tc ingress and egress, respectively. > > For concrete usage examples, see the extensive selftests that have been > developed as part of this series. > > Signed-off-by: Daniel Borkmann <daniel@xxxxxxxxxxxxx> > --- > tools/lib/bpf/bpf.c | 5 +++++ > tools/lib/bpf/bpf.h | 7 +++++++ > tools/lib/bpf/libbpf.c | 44 +++++++++++++++++++++++++++++++++++----- > tools/lib/bpf/libbpf.h | 17 ++++++++++++++++ > tools/lib/bpf/libbpf.map | 1 + > 5 files changed, 69 insertions(+), 5 deletions(-) > > diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c > index a3d1b7ebe224..c340d3cbc6bd 100644 > --- a/tools/lib/bpf/bpf.c > +++ b/tools/lib/bpf/bpf.c > @@ -746,6 +746,11 @@ int bpf_link_create(int prog_fd, int target_fd, > if (!OPTS_ZEROED(opts, tracing)) > return libbpf_err(-EINVAL); > break; > + case BPF_TCX_INGRESS: > + case BPF_TCX_EGRESS: > + attr.link_create.tcx.relative_fd = OPTS_GET(opts, tcx.relative_fd, 0); > + attr.link_create.tcx.expected_revision = OPTS_GET(opts, tcx.expected_revision, 0); can you also add an OPTS_ZEROED check like for other types of links? > + break; > default: > if (!OPTS_ZEROED(opts, flags)) > return libbpf_err(-EINVAL); > diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h > index 480c584a6f7f..12591516dca0 100644 > --- a/tools/lib/bpf/bpf.h > +++ b/tools/lib/bpf/bpf.h > @@ -370,6 +370,13 @@ struct bpf_link_create_opts { > struct { > __u64 cookie; > } tracing; > + struct { > + union { > + __u32 relative_fd; > + __u32 relative_id; > + }; same comment about union, let's not add it and have two separate fields > + __u32 expected_revision; > + } tcx; > }; > size_t :0; > }; > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c > index b89127471c6a..d7b6ff49f02e 100644 > --- a/tools/lib/bpf/libbpf.c > +++ b/tools/lib/bpf/libbpf.c > @@ -133,6 +133,7 @@ static const char * const link_type_name[] = { > [BPF_LINK_TYPE_KPROBE_MULTI] = "kprobe_multi", > [BPF_LINK_TYPE_STRUCT_OPS] = "struct_ops", > [BPF_LINK_TYPE_NETFILTER] = "netfilter", > + [BPF_LINK_TYPE_TCX] = "tcx", > }; > > static const char * const map_type_name[] = { > @@ -11685,11 +11686,10 @@ static int attach_lsm(const struct bpf_program *prog, long cookie, struct bpf_li > } > > static struct bpf_link * > -bpf_program__attach_fd(const struct bpf_program *prog, int target_fd, int btf_id, > - const char *target_name) > +bpf_program__attach_fd_opts(const struct bpf_program *prog, > + const struct bpf_link_create_opts *opts, > + int target_fd, const char *target_name) nit: please keep opts as the last argument > { > - DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts, > - .target_btf_id = btf_id); > enum bpf_attach_type attach_type; > char errmsg[STRERR_BUFSIZE]; > struct bpf_link *link; > @@ -11707,7 +11707,7 @@ bpf_program__attach_fd(const struct bpf_program *prog, int target_fd, int btf_id > link->detach = &bpf_link__detach_fd; > > attach_type = bpf_program__expected_attach_type(prog); > - link_fd = bpf_link_create(prog_fd, target_fd, attach_type, &opts); > + link_fd = bpf_link_create(prog_fd, target_fd, attach_type, opts); > if (link_fd < 0) { > link_fd = -errno; > free(link); > @@ -11720,6 +11720,17 @@ bpf_program__attach_fd(const struct bpf_program *prog, int target_fd, int btf_id > return link; > } > > +static struct bpf_link * > +bpf_program__attach_fd(const struct bpf_program *prog, int target_fd, int btf_id, > + const char *target_name) > +{ > + LIBBPF_OPTS(bpf_link_create_opts, opts, > + .target_btf_id = btf_id, > + ); > + > + return bpf_program__attach_fd_opts(prog, &opts, target_fd, target_name); it seems like the only user of btf_id is bpf_program__attach_freplace, so I'd just inline this there, and for all other 4 cases let's just pass NULL as options? That means we don't really need bpf_program__attach_fd_opts() and can just add opts to bpf_program__attach_fd(). We'll have shorter name. BTW, given it's not exposed API, let's drop double underscore and call it just bpf_program_attach_fd()? > +} > + > struct bpf_link * > bpf_program__attach_cgroup(const struct bpf_program *prog, int cgroup_fd) > { > @@ -11738,6 +11749,29 @@ struct bpf_link *bpf_program__attach_xdp(const struct bpf_program *prog, int ifi > return bpf_program__attach_fd(prog, ifindex, 0, "xdp"); > } > > +struct bpf_link * > +bpf_program__attach_tcx_opts(const struct bpf_program *prog, > + const struct bpf_tcx_opts *opts) we don't have non-opts variant, so let's keep the name short (like we did with bpf_program__attach_netlink): bpf_program__attach_tcx(). > +{ > + LIBBPF_OPTS(bpf_link_create_opts, link_create_opts); > + int ifindex = OPTS_GET(opts, ifindex, 0); let's not do OPTS_GET before we checked OPTS_VALID > + > + if (!OPTS_VALID(opts, bpf_tcx_opts)) > + return libbpf_err_ptr(-EINVAL); > + if (!ifindex) { > + pr_warn("prog '%s': target netdevice ifindex cannot be zero\n", > + prog->name); > + return libbpf_err_ptr(-EINVAL); > + } > + > + link_create_opts.tcx.expected_revision = OPTS_GET(opts, expected_revision, 0); > + link_create_opts.tcx.relative_fd = OPTS_GET(opts, relative_fd, 0); > + link_create_opts.flags = OPTS_GET(opts, flags, 0); > + > + /* target_fd/target_ifindex use the same field in LINK_CREATE */ > + return bpf_program__attach_fd_opts(prog, &link_create_opts, ifindex, "tc"); > +} > + > struct bpf_link *bpf_program__attach_freplace(const struct bpf_program *prog, > int target_fd, > const char *attach_func_name) > diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h > index 754da73c643b..8ffba0f67c60 100644 > --- a/tools/lib/bpf/libbpf.h > +++ b/tools/lib/bpf/libbpf.h > @@ -718,6 +718,23 @@ LIBBPF_API struct bpf_link * > bpf_program__attach_freplace(const struct bpf_program *prog, > int target_fd, const char *attach_func_name); > > +struct bpf_tcx_opts { > + /* size of this struct, for forward/backward compatibility */ > + size_t sz; > + int ifindex; > + __u32 flags; > + union { > + __u32 relative_fd; > + __u32 relative_id; > + }; same thing about not using unions here :) > + __u32 expected_revision; and let's add `size_t :0;` to prevent compiler from leaving garbage values in a padding at the end of the struct (once you drop union there will be padding) > +}; > +#define bpf_tcx_opts__last_field expected_revision > + > +LIBBPF_API struct bpf_link * > +bpf_program__attach_tcx_opts(const struct bpf_program *prog, > + const struct bpf_tcx_opts *opts); > + > struct bpf_map; > > LIBBPF_API struct bpf_link *bpf_map__attach_struct_ops(const struct bpf_map *map); > diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map > index a29b90e9713c..f66b714512c2 100644 > --- a/tools/lib/bpf/libbpf.map > +++ b/tools/lib/bpf/libbpf.map > @@ -396,4 +396,5 @@ LIBBPF_1.3.0 { > global: > bpf_obj_pin_opts; > bpf_prog_detach_opts; > + bpf_program__attach_tcx_opts; > } LIBBPF_1.2.0; > -- > 2.34.1 >