diff mbox series

[v5,bpf-next,5/8] libbpf: add API to get XDP/XSK supported features

Message ID a72609ef4f0de7fee5376c40dbf54ad7f13bfb8d.1675245258.git.lorenzo@kernel.org (mailing list archive)
State Accepted
Commit 04d58f1b26a436c429581d286528ea3c01624462
Delegated to: BPF
Headers show
Series xdp: introduce xdp-feature support | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers warning 6 maintainers not CCed: john.fastabend@gmail.com jolsa@kernel.org song@kernel.org haoluo@google.com yhs@fb.com kpsingh@kernel.org
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch warning CHECK: Please use a blank line after function/struct/union/enum declarations WARNING: line length of 85 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 116 this patch: 116
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-VM_Test-4 success Logs for build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-12 success Logs for test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-13 success Logs for test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-17 success Logs for test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-18 success Logs for test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-22 success Logs for test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 success Logs for test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-27 success Logs for test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-28 success Logs for test_progs_no_alu32_parallel on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-32 success Logs for test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-33 success Logs for test_progs_parallel on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-37 success Logs for test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-38 success Logs for test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-9 success Logs for test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-10 success Logs for test_maps on aarch64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-14 success Logs for test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-15 success Logs for test_progs on aarch64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-19 success Logs for test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-20 success Logs for test_progs_no_alu32 on aarch64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-24 success Logs for test_progs_no_alu32_parallel on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-25 success Logs for test_progs_no_alu32_parallel on aarch64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-29 success Logs for test_progs_parallel on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-30 success Logs for test_progs_parallel on aarch64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-34 success Logs for test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-35 success Logs for test_verifier on aarch64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-36 success Logs for test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-16 fail Logs for test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-21 fail Logs for test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-26 success Logs for test_progs_no_alu32_parallel on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-31 success Logs for test_progs_parallel on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-11 success Logs for test_maps on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-PR fail merge-conflict
bpf/vmtest-bpf-next-VM_Test-2 success Logs for build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-3 success Logs for build for aarch64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-5 success Logs for build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-6 success Logs for build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-7 success Logs for llvm-toolchain
bpf/vmtest-bpf-next-VM_Test-8 success Logs for set-matrix

Commit Message

Lorenzo Bianconi Feb. 1, 2023, 10:24 a.m. UTC
Extend bpf_xdp_query routine in order to get XDP/XSK supported features
of netdev over route netlink interface.
Extend libbpf netlink implementation in order to support netlink_generic
protocol.

Co-developed-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Co-developed-by: Marek Majtyka <alardam@gmail.com>
Signed-off-by: Marek Majtyka <alardam@gmail.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
 tools/lib/bpf/libbpf.h  |  3 +-
 tools/lib/bpf/netlink.c | 96 +++++++++++++++++++++++++++++++++++++++++
 tools/lib/bpf/nlattr.h  | 12 ++++++
 3 files changed, 110 insertions(+), 1 deletion(-)

Comments

Andrii Nakryiko Feb. 6, 2023, 10:42 p.m. UTC | #1
On Wed, Feb 1, 2023 at 2:25 AM Lorenzo Bianconi <lorenzo@kernel.org> wrote:
>
> Extend bpf_xdp_query routine in order to get XDP/XSK supported features
> of netdev over route netlink interface.
> Extend libbpf netlink implementation in order to support netlink_generic
> protocol.
>
> Co-developed-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> Co-developed-by: Marek Majtyka <alardam@gmail.com>
> Signed-off-by: Marek Majtyka <alardam@gmail.com>
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
>  tools/lib/bpf/libbpf.h  |  3 +-
>  tools/lib/bpf/netlink.c | 96 +++++++++++++++++++++++++++++++++++++++++
>  tools/lib/bpf/nlattr.h  | 12 ++++++
>  3 files changed, 110 insertions(+), 1 deletion(-)
>

[...]

> @@ -366,6 +433,10 @@ int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
>                 .ifinfo.ifi_family = AF_PACKET,
>         };
>         struct xdp_id_md xdp_id = {};
> +       struct xdp_features_md md = {
> +               .ifindex = ifindex,
> +       };
> +       __u16 id;
>         int err;
>
>         if (!OPTS_VALID(opts, bpf_xdp_query_opts))
> @@ -393,6 +464,31 @@ int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
>         OPTS_SET(opts, skb_prog_id, xdp_id.info.skb_prog_id);
>         OPTS_SET(opts, attach_mode, xdp_id.info.attach_mode);
>
> +       if (!OPTS_HAS(opts, feature_flags))
> +               return 0;
> +
> +       err = libbpf_netlink_resolve_genl_family_id("netdev", sizeof("netdev"), &id);
> +       if (err < 0)
> +               return libbpf_err(err);
> +
> +       memset(&req, 0, sizeof(req));
> +       req.nh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
> +       req.nh.nlmsg_flags = NLM_F_REQUEST;
> +       req.nh.nlmsg_type = id;
> +       req.gnl.cmd = NETDEV_CMD_DEV_GET;
> +       req.gnl.version = 2;
> +
> +       err = nlattr_add(&req, NETDEV_A_DEV_IFINDEX, &ifindex, sizeof(ifindex));
> +       if (err < 0)
> +               return err;

just noticed this, we need to use libbpf_err(err) here like in other
error cases to set errno properly. Can you please send a follow up?

> +
> +       err = libbpf_netlink_send_recv(&req, NETLINK_GENERIC,
> +                                      parse_xdp_features, NULL, &md);
> +       if (err)
> +               return libbpf_err(err);
> +
> +       opts->feature_flags = md.flags;
> +
>         return 0;
>  }
>

[...]
Lorenzo Bianconi Feb. 6, 2023, 10:55 p.m. UTC | #2
> On Wed, Feb 1, 2023 at 2:25 AM Lorenzo Bianconi <lorenzo@kernel.org> wrote:
> >
> > Extend bpf_xdp_query routine in order to get XDP/XSK supported features
> > of netdev over route netlink interface.
> > Extend libbpf netlink implementation in order to support netlink_generic
> > protocol.
> >
> > Co-developed-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> > Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> > Co-developed-by: Marek Majtyka <alardam@gmail.com>
> > Signed-off-by: Marek Majtyka <alardam@gmail.com>
> > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > ---
> >  tools/lib/bpf/libbpf.h  |  3 +-
> >  tools/lib/bpf/netlink.c | 96 +++++++++++++++++++++++++++++++++++++++++
> >  tools/lib/bpf/nlattr.h  | 12 ++++++
> >  3 files changed, 110 insertions(+), 1 deletion(-)
> >
> 
> [...]
> 
> > @@ -366,6 +433,10 @@ int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
> >                 .ifinfo.ifi_family = AF_PACKET,
> >         };
> >         struct xdp_id_md xdp_id = {};
> > +       struct xdp_features_md md = {
> > +               .ifindex = ifindex,
> > +       };
> > +       __u16 id;
> >         int err;
> >
> >         if (!OPTS_VALID(opts, bpf_xdp_query_opts))
> > @@ -393,6 +464,31 @@ int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
> >         OPTS_SET(opts, skb_prog_id, xdp_id.info.skb_prog_id);
> >         OPTS_SET(opts, attach_mode, xdp_id.info.attach_mode);
> >
> > +       if (!OPTS_HAS(opts, feature_flags))
> > +               return 0;
> > +
> > +       err = libbpf_netlink_resolve_genl_family_id("netdev", sizeof("netdev"), &id);
> > +       if (err < 0)
> > +               return libbpf_err(err);
> > +
> > +       memset(&req, 0, sizeof(req));
> > +       req.nh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
> > +       req.nh.nlmsg_flags = NLM_F_REQUEST;
> > +       req.nh.nlmsg_type = id;
> > +       req.gnl.cmd = NETDEV_CMD_DEV_GET;
> > +       req.gnl.version = 2;
> > +
> > +       err = nlattr_add(&req, NETDEV_A_DEV_IFINDEX, &ifindex, sizeof(ifindex));
> > +       if (err < 0)
> > +               return err;
> 
> just noticed this, we need to use libbpf_err(err) here like in other
> error cases to set errno properly. Can you please send a follow up?

sure, I will post a fix.

Regards,
Lorenzo

> 
> > +
> > +       err = libbpf_netlink_send_recv(&req, NETLINK_GENERIC,
> > +                                      parse_xdp_features, NULL, &md);
> > +       if (err)
> > +               return libbpf_err(err);
> > +
> > +       opts->feature_flags = md.flags;
> > +
> >         return 0;
> >  }
> >
> 
> [...]
>
Yonghong Song Feb. 27, 2023, 8:38 p.m. UTC | #3
On 2/1/23 2:24 AM, Lorenzo Bianconi wrote:
> Extend bpf_xdp_query routine in order to get XDP/XSK supported features
> of netdev over route netlink interface.
> Extend libbpf netlink implementation in order to support netlink_generic
> protocol.
> 
> Co-developed-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> Co-developed-by: Marek Majtyka <alardam@gmail.com>
> Signed-off-by: Marek Majtyka <alardam@gmail.com>
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
>   tools/lib/bpf/libbpf.h  |  3 +-
>   tools/lib/bpf/netlink.c | 96 +++++++++++++++++++++++++++++++++++++++++
>   tools/lib/bpf/nlattr.h  | 12 ++++++
>   3 files changed, 110 insertions(+), 1 deletion(-)
> 
[...]
> +
>   int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
>   {
>   	struct libbpf_nla_req req = {
> @@ -366,6 +433,10 @@ int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
>   		.ifinfo.ifi_family = AF_PACKET,
>   	};
>   	struct xdp_id_md xdp_id = {};
> +	struct xdp_features_md md = {
> +		.ifindex = ifindex,
> +	};
> +	__u16 id;
>   	int err;
>   
>   	if (!OPTS_VALID(opts, bpf_xdp_query_opts))
> @@ -393,6 +464,31 @@ int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
>   	OPTS_SET(opts, skb_prog_id, xdp_id.info.skb_prog_id);
>   	OPTS_SET(opts, attach_mode, xdp_id.info.attach_mode);
>   
> +	if (!OPTS_HAS(opts, feature_flags))
> +		return 0;
> +
> +	err = libbpf_netlink_resolve_genl_family_id("netdev", sizeof("netdev"), &id);
> +	if (err < 0)
> +		return libbpf_err(err);

Hi, Lorenzo,

Using latest libbpf repo (https://github.com/libbpf/libbpf, sync'ed from 
source), looks like the above change won't work if the program is 
running on an old kernel, e.g., 5.12 kernel.

In this particular combination, in user space, bpf_xdp_query_opts does
have 'feature_flags' member, so the control can reach
libbpf_netlink_resolve_genl_family_id(). However, the family 'netdev'
is only available in latest kernel (after this patch set). So
the error will return in the above.

This breaks backward compatibility since old working application won't
work any more with a refresh of libbpf.

I could not come up with an easy solution for this. One thing we could
do is to treat 'libbpf_netlink_resolve_genl_family_id()' as a probe, so
return 0 if probe fails.

   err = libbpf_netlink_resolve_genl_family_id("netdev", 
sizeof("netdev"), &id);
   if (err < 0)
	return 0;

Please let me know whether my suggestion makes sense or there could be a
better solution.


> +
> +	memset(&req, 0, sizeof(req));
> +	req.nh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
> +	req.nh.nlmsg_flags = NLM_F_REQUEST;
> +	req.nh.nlmsg_type = id;
> +	req.gnl.cmd = NETDEV_CMD_DEV_GET;
> +	req.gnl.version = 2;
> +
> +	err = nlattr_add(&req, NETDEV_A_DEV_IFINDEX, &ifindex, sizeof(ifindex));
> +	if (err < 0)
> +		return err;
> +
> +	err = libbpf_netlink_send_recv(&req, NETLINK_GENERIC,
> +				       parse_xdp_features, NULL, &md);
> +	if (err)
> +		return libbpf_err(err);
> +
> +	opts->feature_flags = md.flags;
> +
>   	return 0;
>   }
>   
[...]
Andrii Nakryiko Feb. 27, 2023, 9:01 p.m. UTC | #4
On Mon, Feb 27, 2023 at 12:39 PM Yonghong Song <yhs@meta.com> wrote:
>
>
>
> On 2/1/23 2:24 AM, Lorenzo Bianconi wrote:
> > Extend bpf_xdp_query routine in order to get XDP/XSK supported features
> > of netdev over route netlink interface.
> > Extend libbpf netlink implementation in order to support netlink_generic
> > protocol.
> >
> > Co-developed-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> > Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> > Co-developed-by: Marek Majtyka <alardam@gmail.com>
> > Signed-off-by: Marek Majtyka <alardam@gmail.com>
> > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > ---
> >   tools/lib/bpf/libbpf.h  |  3 +-
> >   tools/lib/bpf/netlink.c | 96 +++++++++++++++++++++++++++++++++++++++++
> >   tools/lib/bpf/nlattr.h  | 12 ++++++
> >   3 files changed, 110 insertions(+), 1 deletion(-)
> >
> [...]
> > +
> >   int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
> >   {
> >       struct libbpf_nla_req req = {
> > @@ -366,6 +433,10 @@ int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
> >               .ifinfo.ifi_family = AF_PACKET,
> >       };
> >       struct xdp_id_md xdp_id = {};
> > +     struct xdp_features_md md = {
> > +             .ifindex = ifindex,
> > +     };
> > +     __u16 id;
> >       int err;
> >
> >       if (!OPTS_VALID(opts, bpf_xdp_query_opts))
> > @@ -393,6 +464,31 @@ int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
> >       OPTS_SET(opts, skb_prog_id, xdp_id.info.skb_prog_id);
> >       OPTS_SET(opts, attach_mode, xdp_id.info.attach_mode);
> >
> > +     if (!OPTS_HAS(opts, feature_flags))
> > +             return 0;
> > +
> > +     err = libbpf_netlink_resolve_genl_family_id("netdev", sizeof("netdev"), &id);
> > +     if (err < 0)
> > +             return libbpf_err(err);
>
> Hi, Lorenzo,
>
> Using latest libbpf repo (https://github.com/libbpf/libbpf, sync'ed from
> source), looks like the above change won't work if the program is
> running on an old kernel, e.g., 5.12 kernel.
>
> In this particular combination, in user space, bpf_xdp_query_opts does
> have 'feature_flags' member, so the control can reach
> libbpf_netlink_resolve_genl_family_id(). However, the family 'netdev'
> is only available in latest kernel (after this patch set). So
> the error will return in the above.
>
> This breaks backward compatibility since old working application won't
> work any more with a refresh of libbpf.
>
> I could not come up with an easy solution for this. One thing we could
> do is to treat 'libbpf_netlink_resolve_genl_family_id()' as a probe, so
> return 0 if probe fails.
>
>    err = libbpf_netlink_resolve_genl_family_id("netdev",
> sizeof("netdev"), &id);
>    if (err < 0)
>         return 0;
>
> Please let me know whether my suggestion makes sense or there could be a
> better solution.
>

feature_flags is an output parameter and if the "netdev" family
doesn't exist then there are no feature flags to return, right?

Is there a specific error code that's returned when such a family
doesn't exist? If yes, we should check for it and return 0 for
feature_flags. If not, we'll have to do a generic < 0 check as
Yonghong proposes.


>
> > +
> > +     memset(&req, 0, sizeof(req));
> > +     req.nh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
> > +     req.nh.nlmsg_flags = NLM_F_REQUEST;
> > +     req.nh.nlmsg_type = id;
> > +     req.gnl.cmd = NETDEV_CMD_DEV_GET;
> > +     req.gnl.version = 2;
> > +
> > +     err = nlattr_add(&req, NETDEV_A_DEV_IFINDEX, &ifindex, sizeof(ifindex));
> > +     if (err < 0)
> > +             return err;
> > +
> > +     err = libbpf_netlink_send_recv(&req, NETLINK_GENERIC,
> > +                                    parse_xdp_features, NULL, &md);
> > +     if (err)
> > +             return libbpf_err(err);
> > +
> > +     opts->feature_flags = md.flags;
> > +
> >       return 0;
> >   }
> >
> [...]
Yonghong Song Feb. 27, 2023, 10:05 p.m. UTC | #5
On 2/27/23 1:01 PM, Andrii Nakryiko wrote:
> On Mon, Feb 27, 2023 at 12:39 PM Yonghong Song <yhs@meta.com> wrote:
>>
>>
>>
>> On 2/1/23 2:24 AM, Lorenzo Bianconi wrote:
>>> Extend bpf_xdp_query routine in order to get XDP/XSK supported features
>>> of netdev over route netlink interface.
>>> Extend libbpf netlink implementation in order to support netlink_generic
>>> protocol.
>>>
>>> Co-developed-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
>>> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
>>> Co-developed-by: Marek Majtyka <alardam@gmail.com>
>>> Signed-off-by: Marek Majtyka <alardam@gmail.com>
>>> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
>>> ---
>>>    tools/lib/bpf/libbpf.h  |  3 +-
>>>    tools/lib/bpf/netlink.c | 96 +++++++++++++++++++++++++++++++++++++++++
>>>    tools/lib/bpf/nlattr.h  | 12 ++++++
>>>    3 files changed, 110 insertions(+), 1 deletion(-)
>>>
>> [...]
>>> +
>>>    int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
>>>    {
>>>        struct libbpf_nla_req req = {
>>> @@ -366,6 +433,10 @@ int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
>>>                .ifinfo.ifi_family = AF_PACKET,
>>>        };
>>>        struct xdp_id_md xdp_id = {};
>>> +     struct xdp_features_md md = {
>>> +             .ifindex = ifindex,
>>> +     };
>>> +     __u16 id;
>>>        int err;
>>>
>>>        if (!OPTS_VALID(opts, bpf_xdp_query_opts))
>>> @@ -393,6 +464,31 @@ int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
>>>        OPTS_SET(opts, skb_prog_id, xdp_id.info.skb_prog_id);
>>>        OPTS_SET(opts, attach_mode, xdp_id.info.attach_mode);
>>>
>>> +     if (!OPTS_HAS(opts, feature_flags))
>>> +             return 0;
>>> +
>>> +     err = libbpf_netlink_resolve_genl_family_id("netdev", sizeof("netdev"), &id);
>>> +     if (err < 0)
>>> +             return libbpf_err(err);
>>
>> Hi, Lorenzo,
>>
>> Using latest libbpf repo (https://github.com/libbpf/libbpf, sync'ed from
>> source), looks like the above change won't work if the program is
>> running on an old kernel, e.g., 5.12 kernel.
>>
>> In this particular combination, in user space, bpf_xdp_query_opts does
>> have 'feature_flags' member, so the control can reach
>> libbpf_netlink_resolve_genl_family_id(). However, the family 'netdev'
>> is only available in latest kernel (after this patch set). So
>> the error will return in the above.
>>
>> This breaks backward compatibility since old working application won't
>> work any more with a refresh of libbpf.
>>
>> I could not come up with an easy solution for this. One thing we could
>> do is to treat 'libbpf_netlink_resolve_genl_family_id()' as a probe, so
>> return 0 if probe fails.
>>
>>     err = libbpf_netlink_resolve_genl_family_id("netdev",
>> sizeof("netdev"), &id);
>>     if (err < 0)
>>          return 0;
>>
>> Please let me know whether my suggestion makes sense or there could be a
>> better solution.
>>
> 
> feature_flags is an output parameter and if the "netdev" family
> doesn't exist then there are no feature flags to return, right?
> 
> Is there a specific error code that's returned when such a family
> doesn't exist? If yes, we should check for it and return 0 for
> feature_flags. If not, we'll have to do a generic < 0 check as
> Yonghong proposes.

We can check -ENOENT.

         err = libbpf_netlink_resolve_genl_family_id("netdev", 
sizeof("netdev"), &id);
-       if (err < 0)
+       if (err < 0) {
+               if (err == -ENOENT)
+                       return 0;
                 return libbpf_err(err);
+       }

Let me propose a patch for this.

> 
> 
>>
>>> +
>>> +     memset(&req, 0, sizeof(req));
>>> +     req.nh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
>>> +     req.nh.nlmsg_flags = NLM_F_REQUEST;
>>> +     req.nh.nlmsg_type = id;
>>> +     req.gnl.cmd = NETDEV_CMD_DEV_GET;
>>> +     req.gnl.version = 2;
>>> +
>>> +     err = nlattr_add(&req, NETDEV_A_DEV_IFINDEX, &ifindex, sizeof(ifindex));
>>> +     if (err < 0)
>>> +             return err;
>>> +
>>> +     err = libbpf_netlink_send_recv(&req, NETLINK_GENERIC,
>>> +                                    parse_xdp_features, NULL, &md);
>>> +     if (err)
>>> +             return libbpf_err(err);
>>> +
>>> +     opts->feature_flags = md.flags;
>>> +
>>>        return 0;
>>>    }
>>>
>> [...]
Andrii Nakryiko Feb. 27, 2023, 10:49 p.m. UTC | #6
On Mon, Feb 27, 2023 at 2:05 PM Yonghong Song <yhs@meta.com> wrote:
>
>
>
> On 2/27/23 1:01 PM, Andrii Nakryiko wrote:
> > On Mon, Feb 27, 2023 at 12:39 PM Yonghong Song <yhs@meta.com> wrote:
> >>
> >>
> >>
> >> On 2/1/23 2:24 AM, Lorenzo Bianconi wrote:
> >>> Extend bpf_xdp_query routine in order to get XDP/XSK supported features
> >>> of netdev over route netlink interface.
> >>> Extend libbpf netlink implementation in order to support netlink_generic
> >>> protocol.
> >>>
> >>> Co-developed-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> >>> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> >>> Co-developed-by: Marek Majtyka <alardam@gmail.com>
> >>> Signed-off-by: Marek Majtyka <alardam@gmail.com>
> >>> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> >>> ---
> >>>    tools/lib/bpf/libbpf.h  |  3 +-
> >>>    tools/lib/bpf/netlink.c | 96 +++++++++++++++++++++++++++++++++++++++++
> >>>    tools/lib/bpf/nlattr.h  | 12 ++++++
> >>>    3 files changed, 110 insertions(+), 1 deletion(-)
> >>>
> >> [...]
> >>> +
> >>>    int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
> >>>    {
> >>>        struct libbpf_nla_req req = {
> >>> @@ -366,6 +433,10 @@ int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
> >>>                .ifinfo.ifi_family = AF_PACKET,
> >>>        };
> >>>        struct xdp_id_md xdp_id = {};
> >>> +     struct xdp_features_md md = {
> >>> +             .ifindex = ifindex,
> >>> +     };
> >>> +     __u16 id;
> >>>        int err;
> >>>
> >>>        if (!OPTS_VALID(opts, bpf_xdp_query_opts))
> >>> @@ -393,6 +464,31 @@ int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
> >>>        OPTS_SET(opts, skb_prog_id, xdp_id.info.skb_prog_id);
> >>>        OPTS_SET(opts, attach_mode, xdp_id.info.attach_mode);
> >>>
> >>> +     if (!OPTS_HAS(opts, feature_flags))
> >>> +             return 0;
> >>> +
> >>> +     err = libbpf_netlink_resolve_genl_family_id("netdev", sizeof("netdev"), &id);
> >>> +     if (err < 0)
> >>> +             return libbpf_err(err);
> >>
> >> Hi, Lorenzo,
> >>
> >> Using latest libbpf repo (https://github.com/libbpf/libbpf, sync'ed from
> >> source), looks like the above change won't work if the program is
> >> running on an old kernel, e.g., 5.12 kernel.
> >>
> >> In this particular combination, in user space, bpf_xdp_query_opts does
> >> have 'feature_flags' member, so the control can reach
> >> libbpf_netlink_resolve_genl_family_id(). However, the family 'netdev'
> >> is only available in latest kernel (after this patch set). So
> >> the error will return in the above.
> >>
> >> This breaks backward compatibility since old working application won't
> >> work any more with a refresh of libbpf.
> >>
> >> I could not come up with an easy solution for this. One thing we could
> >> do is to treat 'libbpf_netlink_resolve_genl_family_id()' as a probe, so
> >> return 0 if probe fails.
> >>
> >>     err = libbpf_netlink_resolve_genl_family_id("netdev",
> >> sizeof("netdev"), &id);
> >>     if (err < 0)
> >>          return 0;
> >>
> >> Please let me know whether my suggestion makes sense or there could be a
> >> better solution.
> >>
> >
> > feature_flags is an output parameter and if the "netdev" family
> > doesn't exist then there are no feature flags to return, right?
> >
> > Is there a specific error code that's returned when such a family
> > doesn't exist? If yes, we should check for it and return 0 for
> > feature_flags. If not, we'll have to do a generic < 0 check as
> > Yonghong proposes.
>
> We can check -ENOENT.
>
>          err = libbpf_netlink_resolve_genl_family_id("netdev",
> sizeof("netdev"), &id);
> -       if (err < 0)
> +       if (err < 0) {
> +               if (err == -ENOENT)
> +                       return 0;
>                  return libbpf_err(err);
> +       }
>
> Let me propose a patch for this.

we might add more options beyond feature_flags, so these early returns
are a bit error-prone, let's convert this code to if () { } else?

>
> >
> >
> >>
> >>> +
> >>> +     memset(&req, 0, sizeof(req));
> >>> +     req.nh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
> >>> +     req.nh.nlmsg_flags = NLM_F_REQUEST;
> >>> +     req.nh.nlmsg_type = id;
> >>> +     req.gnl.cmd = NETDEV_CMD_DEV_GET;
> >>> +     req.gnl.version = 2;
> >>> +
> >>> +     err = nlattr_add(&req, NETDEV_A_DEV_IFINDEX, &ifindex, sizeof(ifindex));
> >>> +     if (err < 0)
> >>> +             return err;
> >>> +
> >>> +     err = libbpf_netlink_send_recv(&req, NETLINK_GENERIC,
> >>> +                                    parse_xdp_features, NULL, &md);
> >>> +     if (err)
> >>> +             return libbpf_err(err);
> >>> +
> >>> +     opts->feature_flags = md.flags;
> >>> +
> >>>        return 0;
> >>>    }
> >>>
> >> [...]
diff mbox series

Patch

diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 8777ff21ea1d..b18581277eb2 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -1048,9 +1048,10 @@  struct bpf_xdp_query_opts {
 	__u32 hw_prog_id;	/* output */
 	__u32 skb_prog_id;	/* output */
 	__u8 attach_mode;	/* output */
+	__u64 feature_flags;	/* output */
 	size_t :0;
 };
-#define bpf_xdp_query_opts__last_field attach_mode
+#define bpf_xdp_query_opts__last_field feature_flags
 
 LIBBPF_API int bpf_xdp_attach(int ifindex, int prog_fd, __u32 flags,
 			      const struct bpf_xdp_attach_opts *opts);
diff --git a/tools/lib/bpf/netlink.c b/tools/lib/bpf/netlink.c
index d2468a04a6c3..32b13b7a11b0 100644
--- a/tools/lib/bpf/netlink.c
+++ b/tools/lib/bpf/netlink.c
@@ -9,6 +9,7 @@ 
 #include <linux/if_ether.h>
 #include <linux/pkt_cls.h>
 #include <linux/rtnetlink.h>
+#include <linux/netdev.h>
 #include <sys/socket.h>
 #include <errno.h>
 #include <time.h>
@@ -39,6 +40,12 @@  struct xdp_id_md {
 	int ifindex;
 	__u32 flags;
 	struct xdp_link_info info;
+	__u64 feature_flags;
+};
+
+struct xdp_features_md {
+	int ifindex;
+	__u64 flags;
 };
 
 static int libbpf_netlink_open(__u32 *nl_pid, int proto)
@@ -238,6 +245,43 @@  static int libbpf_netlink_send_recv(struct libbpf_nla_req *req,
 	return ret;
 }
 
+static int parse_genl_family_id(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn,
+				void *cookie)
+{
+	struct genlmsghdr *gnl = NLMSG_DATA(nh);
+	struct nlattr *na = (struct nlattr *)((void *)gnl + GENL_HDRLEN);
+	struct nlattr *tb[CTRL_ATTR_FAMILY_ID + 1];
+	__u16 *id = cookie;
+
+	libbpf_nla_parse(tb, CTRL_ATTR_FAMILY_ID, na,
+			 NLMSG_PAYLOAD(nh, sizeof(*gnl)), NULL);
+	if (!tb[CTRL_ATTR_FAMILY_ID])
+		return NL_CONT;
+
+	*id = libbpf_nla_getattr_u16(tb[CTRL_ATTR_FAMILY_ID]);
+	return NL_DONE;
+}
+
+static int libbpf_netlink_resolve_genl_family_id(const char *name,
+						 __u16 len, __u16 *id)
+{
+	struct libbpf_nla_req req = {
+		.nh.nlmsg_len	= NLMSG_LENGTH(GENL_HDRLEN),
+		.nh.nlmsg_type	= GENL_ID_CTRL,
+		.nh.nlmsg_flags	= NLM_F_REQUEST,
+		.gnl.cmd	= CTRL_CMD_GETFAMILY,
+		.gnl.version	= 2,
+	};
+	int err;
+
+	err = nlattr_add(&req, CTRL_ATTR_FAMILY_NAME, name, len);
+	if (err < 0)
+		return err;
+
+	return libbpf_netlink_send_recv(&req, NETLINK_GENERIC,
+					parse_genl_family_id, NULL, id);
+}
+
 static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
 					 __u32 flags)
 {
@@ -357,6 +401,29 @@  static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)
 	return 0;
 }
 
+static int parse_xdp_features(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn,
+			      void *cookie)
+{
+	struct genlmsghdr *gnl = NLMSG_DATA(nh);
+	struct nlattr *na = (struct nlattr *)((void *)gnl + GENL_HDRLEN);
+	struct nlattr *tb[NETDEV_CMD_MAX + 1];
+	struct xdp_features_md *md = cookie;
+	__u32 ifindex;
+
+	libbpf_nla_parse(tb, NETDEV_CMD_MAX, na,
+			 NLMSG_PAYLOAD(nh, sizeof(*gnl)), NULL);
+
+	if (!tb[NETDEV_A_DEV_IFINDEX] || !tb[NETDEV_A_DEV_XDP_FEATURES])
+		return NL_CONT;
+
+	ifindex = libbpf_nla_getattr_u32(tb[NETDEV_A_DEV_IFINDEX]);
+	if (ifindex != md->ifindex)
+		return NL_CONT;
+
+	md->flags = libbpf_nla_getattr_u64(tb[NETDEV_A_DEV_XDP_FEATURES]);
+	return NL_DONE;
+}
+
 int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
 {
 	struct libbpf_nla_req req = {
@@ -366,6 +433,10 @@  int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
 		.ifinfo.ifi_family = AF_PACKET,
 	};
 	struct xdp_id_md xdp_id = {};
+	struct xdp_features_md md = {
+		.ifindex = ifindex,
+	};
+	__u16 id;
 	int err;
 
 	if (!OPTS_VALID(opts, bpf_xdp_query_opts))
@@ -393,6 +464,31 @@  int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
 	OPTS_SET(opts, skb_prog_id, xdp_id.info.skb_prog_id);
 	OPTS_SET(opts, attach_mode, xdp_id.info.attach_mode);
 
+	if (!OPTS_HAS(opts, feature_flags))
+		return 0;
+
+	err = libbpf_netlink_resolve_genl_family_id("netdev", sizeof("netdev"), &id);
+	if (err < 0)
+		return libbpf_err(err);
+
+	memset(&req, 0, sizeof(req));
+	req.nh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	req.nh.nlmsg_flags = NLM_F_REQUEST;
+	req.nh.nlmsg_type = id;
+	req.gnl.cmd = NETDEV_CMD_DEV_GET;
+	req.gnl.version = 2;
+
+	err = nlattr_add(&req, NETDEV_A_DEV_IFINDEX, &ifindex, sizeof(ifindex));
+	if (err < 0)
+		return err;
+
+	err = libbpf_netlink_send_recv(&req, NETLINK_GENERIC,
+				       parse_xdp_features, NULL, &md);
+	if (err)
+		return libbpf_err(err);
+
+	opts->feature_flags = md.flags;
+
 	return 0;
 }
 
diff --git a/tools/lib/bpf/nlattr.h b/tools/lib/bpf/nlattr.h
index 4d15ae2ff812..d92d1c1de700 100644
--- a/tools/lib/bpf/nlattr.h
+++ b/tools/lib/bpf/nlattr.h
@@ -14,6 +14,7 @@ 
 #include <errno.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
+#include <linux/genetlink.h>
 
 /* avoid multiple definition of netlink features */
 #define __LINUX_NETLINK_H
@@ -58,6 +59,7 @@  struct libbpf_nla_req {
 	union {
 		struct ifinfomsg ifinfo;
 		struct tcmsg tc;
+		struct genlmsghdr gnl;
 	};
 	char buf[128];
 };
@@ -89,11 +91,21 @@  static inline uint8_t libbpf_nla_getattr_u8(const struct nlattr *nla)
 	return *(uint8_t *)libbpf_nla_data(nla);
 }
 
+static inline uint16_t libbpf_nla_getattr_u16(const struct nlattr *nla)
+{
+	return *(uint16_t *)libbpf_nla_data(nla);
+}
+
 static inline uint32_t libbpf_nla_getattr_u32(const struct nlattr *nla)
 {
 	return *(uint32_t *)libbpf_nla_data(nla);
 }
 
+static inline uint64_t libbpf_nla_getattr_u64(const struct nlattr *nla)
+{
+	return *(uint64_t *)libbpf_nla_data(nla);
+}
+
 static inline const char *libbpf_nla_getattr_str(const struct nlattr *nla)
 {
 	return (const char *)libbpf_nla_data(nla);