diff mbox series

[bpf-next,12/16] libbpf: Add egress XDP support

Message ID 20200420200055.49033-13-dsahern@kernel.org
State Changes Requested
Delegated to: BPF Maintainers
Headers show
Series net: Add support for XDP in egress path | expand

Commit Message

David Ahern April 20, 2020, 8 p.m. UTC
From: David Ahern <dahern@digitalocean.com>

Patch adds egress XDP support in libbpf.

New section name hint, xdp_egress, is added to set expected attach
type at program load. Programs can use xdp_egress as the prefix in
the SEC statement to load the program with the BPF_XDP_EGRESS
attach type set.

egress is added to bpf_xdp_set_link_opts to specify egress type for
use with bpf_set_link_xdp_fd_opts. Update library side to check
for flag and set nla_type to IFLA_XDP_EGRESS.

Add egress version of bpf_get_link_xdp* info and id apis with core
code refactored to handle both rx and tx paths.

Signed-off-by: Prashant Bhole <prashantbhole.linux@gmail.com>
Co-developed-by: David Ahern <dahern@digitalocean.com>
Signed-off-by: David Ahern <dahern@digitalocean.com>
---
 tools/lib/bpf/libbpf.c   |  2 ++
 tools/lib/bpf/libbpf.h   |  9 +++++-
 tools/lib/bpf/libbpf.map |  2 ++
 tools/lib/bpf/netlink.c  | 63 +++++++++++++++++++++++++++++++++++-----
 4 files changed, 67 insertions(+), 9 deletions(-)

Comments

Toke Høiland-Jørgensen April 21, 2020, 10:20 a.m. UTC | #1
David Ahern <dsahern@kernel.org> writes:

> From: David Ahern <dahern@digitalocean.com>
>
> Patch adds egress XDP support in libbpf.
>
> New section name hint, xdp_egress, is added to set expected attach
> type at program load. Programs can use xdp_egress as the prefix in
> the SEC statement to load the program with the BPF_XDP_EGRESS
> attach type set.
>
> egress is added to bpf_xdp_set_link_opts to specify egress type for
> use with bpf_set_link_xdp_fd_opts. Update library side to check
> for flag and set nla_type to IFLA_XDP_EGRESS.
>
> Add egress version of bpf_get_link_xdp* info and id apis with core
> code refactored to handle both rx and tx paths.
>
> Signed-off-by: Prashant Bhole <prashantbhole.linux@gmail.com>
> Co-developed-by: David Ahern <dahern@digitalocean.com>
> Signed-off-by: David Ahern <dahern@digitalocean.com>
> ---
>  tools/lib/bpf/libbpf.c   |  2 ++
>  tools/lib/bpf/libbpf.h   |  9 +++++-
>  tools/lib/bpf/libbpf.map |  2 ++
>  tools/lib/bpf/netlink.c  | 63 +++++++++++++++++++++++++++++++++++-----
>  4 files changed, 67 insertions(+), 9 deletions(-)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 8f480e29a6b0..32fc970495d9 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -6366,6 +6366,8 @@ static const struct bpf_sec_def section_defs[] = {
>  		.is_attach_btf = true,
>  		.expected_attach_type = BPF_LSM_MAC,
>  		.attach_fn = attach_lsm),
> +	BPF_EAPROG_SEC("xdp_egress",		BPF_PROG_TYPE_XDP,
> +						BPF_XDP_EGRESS),
>  	BPF_PROG_SEC("xdp",			BPF_PROG_TYPE_XDP),
>  	BPF_PROG_SEC("perf_event",		BPF_PROG_TYPE_PERF_EVENT),
>  	BPF_PROG_SEC("lwt_in",			BPF_PROG_TYPE_LWT_IN),
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index f1dacecb1619..3feb1242f78e 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -453,14 +453,16 @@ struct xdp_link_info {
>  	__u32 drv_prog_id;
>  	__u32 hw_prog_id;
>  	__u32 skb_prog_id;
> +	__u32 egress_core_prog_id;
>  	__u8 attach_mode;
>  };
>  
>  struct bpf_xdp_set_link_opts {
>  	size_t sz;
>  	int old_fd;
> +	__u8  egress;
>  };
> -#define bpf_xdp_set_link_opts__last_field old_fd
> +#define bpf_xdp_set_link_opts__last_field egress
>  
>  LIBBPF_API int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags);
>  LIBBPF_API int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
> @@ -468,6 +470,11 @@ LIBBPF_API int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
>  LIBBPF_API int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags);
>  LIBBPF_API int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
>  				     size_t info_size, __u32 flags);
> +LIBBPF_API int bpf_get_link_xdp_egress_id(int ifindex, __u32 *prog_id,
> +					  __u32 flags);
> +LIBBPF_API int bpf_get_link_xdp_egress_info(int ifindex,
> +					    struct xdp_link_info *info,
> +					    size_t info_size, __u32 flags);

Isn't the kernel returning both program types in the same message when
dumping an interface? So do we really need a separate getter instead of
just populating xdp_link_info with the egress ID in the existing getter?

-Toke
David Ahern April 21, 2020, 1:03 p.m. UTC | #2
On 4/21/20 4:20 AM, Toke Høiland-Jørgensen wrote:
> Isn't the kernel returning both program types in the same message when
> dumping an interface? So do we really need a separate getter instead of
> just populating xdp_link_info with the egress ID in the existing getter?

That might work with some refactoring of get_xdp_info. I'll take a look.
Toke Høiland-Jørgensen April 21, 2020, 1:28 p.m. UTC | #3
David Ahern <dsahern@gmail.com> writes:

> On 4/21/20 4:20 AM, Toke Høiland-Jørgensen wrote:
>> Isn't the kernel returning both program types in the same message when
>> dumping an interface? So do we really need a separate getter instead of
>> just populating xdp_link_info with the egress ID in the existing getter?
>
> That might work with some refactoring of get_xdp_info. I'll take a look.

Great, thanks! :)

-Toke
Andrii Nakryiko April 23, 2020, 1:19 a.m. UTC | #4
On Mon, Apr 20, 2020 at 1:02 PM David Ahern <dsahern@kernel.org> wrote:
>
> From: David Ahern <dahern@digitalocean.com>
>
> Patch adds egress XDP support in libbpf.
>
> New section name hint, xdp_egress, is added to set expected attach
> type at program load. Programs can use xdp_egress as the prefix in
> the SEC statement to load the program with the BPF_XDP_EGRESS
> attach type set.
>
> egress is added to bpf_xdp_set_link_opts to specify egress type for
> use with bpf_set_link_xdp_fd_opts. Update library side to check
> for flag and set nla_type to IFLA_XDP_EGRESS.
>
> Add egress version of bpf_get_link_xdp* info and id apis with core
> code refactored to handle both rx and tx paths.
>
> Signed-off-by: Prashant Bhole <prashantbhole.linux@gmail.com>
> Co-developed-by: David Ahern <dahern@digitalocean.com>
> Signed-off-by: David Ahern <dahern@digitalocean.com>
> ---
>  tools/lib/bpf/libbpf.c   |  2 ++
>  tools/lib/bpf/libbpf.h   |  9 +++++-
>  tools/lib/bpf/libbpf.map |  2 ++
>  tools/lib/bpf/netlink.c  | 63 +++++++++++++++++++++++++++++++++++-----
>  4 files changed, 67 insertions(+), 9 deletions(-)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 8f480e29a6b0..32fc970495d9 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -6366,6 +6366,8 @@ static const struct bpf_sec_def section_defs[] = {
>                 .is_attach_btf = true,
>                 .expected_attach_type = BPF_LSM_MAC,
>                 .attach_fn = attach_lsm),
> +       BPF_EAPROG_SEC("xdp_egress",            BPF_PROG_TYPE_XDP,
> +                                               BPF_XDP_EGRESS),
>         BPF_PROG_SEC("xdp",                     BPF_PROG_TYPE_XDP),
>         BPF_PROG_SEC("perf_event",              BPF_PROG_TYPE_PERF_EVENT),
>         BPF_PROG_SEC("lwt_in",                  BPF_PROG_TYPE_LWT_IN),
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index f1dacecb1619..3feb1242f78e 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -453,14 +453,16 @@ struct xdp_link_info {
>         __u32 drv_prog_id;
>         __u32 hw_prog_id;
>         __u32 skb_prog_id;
> +       __u32 egress_core_prog_id;

This changes layout of struct xdp_link_info in ABI-breaking way. New
fields have to be added at the end.

>         __u8 attach_mode;
>  };
>
>  struct bpf_xdp_set_link_opts {
>         size_t sz;
>         int old_fd;
> +       __u8  egress;

Is this a true/false field? If yes, why not bool then?

>  };
> -#define bpf_xdp_set_link_opts__last_field old_fd
> +#define bpf_xdp_set_link_opts__last_field egress
>
>  LIBBPF_API int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags);
>  LIBBPF_API int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
> @@ -468,6 +470,11 @@ LIBBPF_API int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
>  LIBBPF_API int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags);
>  LIBBPF_API int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
>                                      size_t info_size, __u32 flags);
> +LIBBPF_API int bpf_get_link_xdp_egress_id(int ifindex, __u32 *prog_id,
> +                                         __u32 flags);
> +LIBBPF_API int bpf_get_link_xdp_egress_info(int ifindex,
> +                                           struct xdp_link_info *info,
> +                                           size_t info_size, __u32 flags);
>
>  struct perf_buffer;
>
> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> index bb8831605b25..51576c8a02fe 100644
> --- a/tools/lib/bpf/libbpf.map
> +++ b/tools/lib/bpf/libbpf.map
> @@ -253,4 +253,6 @@ LIBBPF_0.0.8 {
>                 bpf_program__set_attach_target;
>                 bpf_program__set_lsm;
>                 bpf_set_link_xdp_fd_opts;
> +               bpf_get_link_xdp_egress_id;
> +               bpf_get_link_xdp_egress_info;

This should go into 0.0.9 section, 0.0.8 is sealed now.

>  } LIBBPF_0.0.7;

[...]

> @@ -203,6 +204,7 @@ static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
>  int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
>                              const struct bpf_xdp_set_link_opts *opts)
>  {
> +       __u16 nla_type = IFLA_XDP;
>         int old_fd = -1;
>
>         if (!OPTS_VALID(opts, bpf_xdp_set_link_opts))
> @@ -213,14 +215,22 @@ int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
>                 flags |= XDP_FLAGS_REPLACE;
>         }
>
> +       if (OPTS_HAS(opts, egress)) {

I don't think you need to check OPTS_HAS here, just OPTS_GET with
proper default would work.

> +               __u8 egress = OPTS_GET(opts, egress, 0);
> +
> +               if (egress)
> +                       nla_type = IFLA_XDP_EGRESS;
> +       }
> +
>         return __bpf_set_link_xdp_fd_replace(ifindex, fd,
>                                              old_fd,
> -                                            flags);
> +                                            flags,

nit: old_fd, flags fit on the same line as ifindex, fd, not sure why
there are so many lines for this?

> +                                            nla_type);
>  }
>
>  int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
>  {
> -       return __bpf_set_link_xdp_fd_replace(ifindex, fd, 0, flags);
> +       return __bpf_set_link_xdp_fd_replace(ifindex, fd, 0, flags, IFLA_XDP);
>  }
>

[...]

>
> +int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
> +                         size_t info_size, __u32 flags)
> +{
> +       return __bpf_get_link_xdp_info(ifindex, info, info_size, flags,
> +                                      IFLA_XDP);
> +}
> +
> +int bpf_get_link_xdp_egress_info(int ifindex, struct xdp_link_info *info,
> +                                size_t info_size, __u32 flags)
> +{
> +       return __bpf_get_link_xdp_info(ifindex, info, info_size, flags,
> +                                      IFLA_XDP_EGRESS);
> +}
> +
>  static __u32 get_xdp_id(struct xdp_link_info *info, __u32 flags)
>  {
>         if (info->attach_mode != XDP_ATTACHED_MULTI && !flags)
> @@ -345,6 +376,22 @@ int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags)
>         return ret;
>  }
>
> +int bpf_get_link_xdp_egress_id(int ifindex, __u32 *prog_id, __u32 flags)

Is bpf_get_link_xdp_egress_id() even needed? This is a special case of
bpf_get_link_xdp_egress_info(), I don't think we have to add another
API to support it specifically.

Also, just curious, would it be better to have a generalized
XDP/XDP_EGRESS xdp_info() functions instead of two separate ones?
Could there be some third variant of XDP program later, whatever that
might be?

> +{
> +       struct xdp_link_info info;
> +       int ret;
> +
> +       /* egress path does not support SKB, DRV or HW mode */
> +       if (flags & XDP_FLAGS_MODES)
> +               return -EINVAL;
> +
> +       ret = bpf_get_link_xdp_egress_info(ifindex, &info, sizeof(info), flags);
> +       if (!ret)
> +               *prog_id = get_xdp_id(&info, flags);
> +
> +       return ret;
> +}
> +
>  int libbpf_nl_get_link(int sock, unsigned int nl_pid,
>                        libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
>  {
> --
> 2.21.1 (Apple Git-122.3)
>
David Ahern April 23, 2020, 1:33 a.m. UTC | #5
On 4/22/20 7:19 PM, Andrii Nakryiko wrote:
>>
>> +int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
>> +                         size_t info_size, __u32 flags)
>> +{
>> +       return __bpf_get_link_xdp_info(ifindex, info, info_size, flags,
>> +                                      IFLA_XDP);
>> +}
>> +
>> +int bpf_get_link_xdp_egress_info(int ifindex, struct xdp_link_info *info,
>> +                                size_t info_size, __u32 flags)
>> +{
>> +       return __bpf_get_link_xdp_info(ifindex, info, info_size, flags,
>> +                                      IFLA_XDP_EGRESS);
>> +}
>> +
>>  static __u32 get_xdp_id(struct xdp_link_info *info, __u32 flags)
>>  {
>>         if (info->attach_mode != XDP_ATTACHED_MULTI && !flags)
>> @@ -345,6 +376,22 @@ int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags)
>>         return ret;
>>  }
>>
>> +int bpf_get_link_xdp_egress_id(int ifindex, __u32 *prog_id, __u32 flags)
> 
> Is bpf_get_link_xdp_egress_id() even needed? This is a special case of
> bpf_get_link_xdp_egress_info(), I don't think we have to add another
> API to support it specifically.
> 
> Also, just curious, would it be better to have a generalized
> XDP/XDP_EGRESS xdp_info() functions instead of two separate ones?

Toke mentioned that too, and I have removed bpf_get_link_xdp_egress_info.

bpf_get_link_xdp_egress_id is needed. The flags argument is a direct
pass through for the kernel uapi and correlates to XDP_FLAGS that are
passed to the kernel. There is no harm or overhead in exporting symbols.
Having a simple, easy to use API for applications is important to making
a library user friendly which is key to its adoption.

Will fix the other comments.
diff mbox series

Patch

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 8f480e29a6b0..32fc970495d9 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -6366,6 +6366,8 @@  static const struct bpf_sec_def section_defs[] = {
 		.is_attach_btf = true,
 		.expected_attach_type = BPF_LSM_MAC,
 		.attach_fn = attach_lsm),
+	BPF_EAPROG_SEC("xdp_egress",		BPF_PROG_TYPE_XDP,
+						BPF_XDP_EGRESS),
 	BPF_PROG_SEC("xdp",			BPF_PROG_TYPE_XDP),
 	BPF_PROG_SEC("perf_event",		BPF_PROG_TYPE_PERF_EVENT),
 	BPF_PROG_SEC("lwt_in",			BPF_PROG_TYPE_LWT_IN),
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index f1dacecb1619..3feb1242f78e 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -453,14 +453,16 @@  struct xdp_link_info {
 	__u32 drv_prog_id;
 	__u32 hw_prog_id;
 	__u32 skb_prog_id;
+	__u32 egress_core_prog_id;
 	__u8 attach_mode;
 };
 
 struct bpf_xdp_set_link_opts {
 	size_t sz;
 	int old_fd;
+	__u8  egress;
 };
-#define bpf_xdp_set_link_opts__last_field old_fd
+#define bpf_xdp_set_link_opts__last_field egress
 
 LIBBPF_API int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags);
 LIBBPF_API int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
@@ -468,6 +470,11 @@  LIBBPF_API int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
 LIBBPF_API int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags);
 LIBBPF_API int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
 				     size_t info_size, __u32 flags);
+LIBBPF_API int bpf_get_link_xdp_egress_id(int ifindex, __u32 *prog_id,
+					  __u32 flags);
+LIBBPF_API int bpf_get_link_xdp_egress_info(int ifindex,
+					    struct xdp_link_info *info,
+					    size_t info_size, __u32 flags);
 
 struct perf_buffer;
 
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index bb8831605b25..51576c8a02fe 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -253,4 +253,6 @@  LIBBPF_0.0.8 {
 		bpf_program__set_attach_target;
 		bpf_program__set_lsm;
 		bpf_set_link_xdp_fd_opts;
+		bpf_get_link_xdp_egress_id;
+		bpf_get_link_xdp_egress_info;
 } LIBBPF_0.0.7;
diff --git a/tools/lib/bpf/netlink.c b/tools/lib/bpf/netlink.c
index 0b709fd10bba..342acacf7cda 100644
--- a/tools/lib/bpf/netlink.c
+++ b/tools/lib/bpf/netlink.c
@@ -28,6 +28,7 @@  typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t,
 struct xdp_id_md {
 	int ifindex;
 	__u32 flags;
+	__u16 nla_type;
 	struct xdp_link_info info;
 };
 
@@ -133,7 +134,7 @@  static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq,
 }
 
 static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
-					 __u32 flags)
+					 __u32 flags, __u16 nla_type)
 {
 	int sock, seq = 0, ret;
 	struct nlattr *nla, *nla_xdp;
@@ -160,7 +161,7 @@  static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
 	/* started nested attribute for XDP */
 	nla = (struct nlattr *)(((char *)&req)
 				+ NLMSG_ALIGN(req.nh.nlmsg_len));
-	nla->nla_type = NLA_F_NESTED | IFLA_XDP;
+	nla->nla_type = NLA_F_NESTED | nla_type;
 	nla->nla_len = NLA_HDRLEN;
 
 	/* add XDP fd */
@@ -203,6 +204,7 @@  static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
 int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
 			     const struct bpf_xdp_set_link_opts *opts)
 {
+	__u16 nla_type = IFLA_XDP;
 	int old_fd = -1;
 
 	if (!OPTS_VALID(opts, bpf_xdp_set_link_opts))
@@ -213,14 +215,22 @@  int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
 		flags |= XDP_FLAGS_REPLACE;
 	}
 
+	if (OPTS_HAS(opts, egress)) {
+		__u8 egress = OPTS_GET(opts, egress, 0);
+
+		if (egress)
+			nla_type = IFLA_XDP_EGRESS;
+	}
+
 	return __bpf_set_link_xdp_fd_replace(ifindex, fd,
 					     old_fd,
-					     flags);
+					     flags,
+					     nla_type);
 }
 
 int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
 {
-	return __bpf_set_link_xdp_fd_replace(ifindex, fd, 0, flags);
+	return __bpf_set_link_xdp_fd_replace(ifindex, fd, 0, flags, IFLA_XDP);
 }
 
 static int __dump_link_nlmsg(struct nlmsghdr *nlh,
@@ -243,15 +253,16 @@  static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)
 	struct nlattr *xdp_tb[IFLA_XDP_MAX + 1];
 	struct xdp_id_md *xdp_id = cookie;
 	struct ifinfomsg *ifinfo = msg;
+	__u16 atype = xdp_id->nla_type;
 	int ret;
 
 	if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index)
 		return 0;
 
-	if (!tb[IFLA_XDP])
+	if (!tb[atype])
 		return 0;
 
-	ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL);
+	ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[atype], NULL);
 	if (ret)
 		return ret;
 
@@ -280,11 +291,16 @@  static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)
 		xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32(
 			xdp_tb[IFLA_XDP_HW_PROG_ID]);
 
+	if (xdp_tb[IFLA_XDP_EGRESS_CORE_PROG_ID])
+		xdp_id->info.egress_core_prog_id = libbpf_nla_getattr_u32(
+			xdp_tb[IFLA_XDP_EGRESS_CORE_PROG_ID]);
+
 	return 0;
 }
 
-int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
-			  size_t info_size, __u32 flags)
+static int __bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
+				   size_t info_size, __u32 flags,
+				   __u16 nla_type)
 {
 	struct xdp_id_md xdp_id = {};
 	int sock, ret;
@@ -306,6 +322,7 @@  int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
 
 	xdp_id.ifindex = ifindex;
 	xdp_id.flags = flags;
+	xdp_id.nla_type = nla_type;
 
 	ret = libbpf_nl_get_link(sock, nl_pid, get_xdp_info, &xdp_id);
 	if (!ret) {
@@ -319,6 +336,20 @@  int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
 	return ret;
 }
 
+int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
+			  size_t info_size, __u32 flags)
+{
+	return __bpf_get_link_xdp_info(ifindex, info, info_size, flags,
+				       IFLA_XDP);
+}
+
+int bpf_get_link_xdp_egress_info(int ifindex, struct xdp_link_info *info,
+				 size_t info_size, __u32 flags)
+{
+	return __bpf_get_link_xdp_info(ifindex, info, info_size, flags,
+				       IFLA_XDP_EGRESS);
+}
+
 static __u32 get_xdp_id(struct xdp_link_info *info, __u32 flags)
 {
 	if (info->attach_mode != XDP_ATTACHED_MULTI && !flags)
@@ -345,6 +376,22 @@  int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags)
 	return ret;
 }
 
+int bpf_get_link_xdp_egress_id(int ifindex, __u32 *prog_id, __u32 flags)
+{
+	struct xdp_link_info info;
+	int ret;
+
+	/* egress path does not support SKB, DRV or HW mode */
+	if (flags & XDP_FLAGS_MODES)
+		return -EINVAL;
+
+	ret = bpf_get_link_xdp_egress_info(ifindex, &info, sizeof(info), flags);
+	if (!ret)
+		*prog_id = get_xdp_id(&info, flags);
+
+	return ret;
+}
+
 int libbpf_nl_get_link(int sock, unsigned int nl_pid,
 		       libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
 {