struct xdp_id_md {
        int ifindex;
        __u32 flags;
-       __u32 id;
+       struct xdp_link_info info;
 };
 
 int libbpf_netlink_open(__u32 *nl_pid)
        return dump_link_nlmsg(cookie, ifi, tb);
 }
 
-static unsigned char get_xdp_id_attr(unsigned char mode, __u32 flags)
-{
-       if (mode != XDP_ATTACHED_MULTI)
-               return IFLA_XDP_PROG_ID;
-       if (flags & XDP_FLAGS_DRV_MODE)
-               return IFLA_XDP_DRV_PROG_ID;
-       if (flags & XDP_FLAGS_HW_MODE)
-               return IFLA_XDP_HW_PROG_ID;
-       if (flags & XDP_FLAGS_SKB_MODE)
-               return IFLA_XDP_SKB_PROG_ID;
-
-       return IFLA_XDP_UNSPEC;
-}
-
-static int get_xdp_id(void *cookie, void *msg, struct nlattr **tb)
+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;
-       unsigned char mode, xdp_attr;
        int ret;
 
        if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index)
        if (!xdp_tb[IFLA_XDP_ATTACHED])
                return 0;
 
-       mode = libbpf_nla_getattr_u8(xdp_tb[IFLA_XDP_ATTACHED]);
-       if (mode == XDP_ATTACHED_NONE)
-               return 0;
+       xdp_id->info.attach_mode = libbpf_nla_getattr_u8(
+               xdp_tb[IFLA_XDP_ATTACHED]);
 
-       xdp_attr = get_xdp_id_attr(mode, xdp_id->flags);
-       if (!xdp_attr || !xdp_tb[xdp_attr])
+       if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE)
                return 0;
 
-       xdp_id->id = libbpf_nla_getattr_u32(xdp_tb[xdp_attr]);
+       if (xdp_tb[IFLA_XDP_PROG_ID])
+               xdp_id->info.prog_id = libbpf_nla_getattr_u32(
+                       xdp_tb[IFLA_XDP_PROG_ID]);
+
+       if (xdp_tb[IFLA_XDP_SKB_PROG_ID])
+               xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32(
+                       xdp_tb[IFLA_XDP_SKB_PROG_ID]);
+
+       if (xdp_tb[IFLA_XDP_DRV_PROG_ID])
+               xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32(
+                       xdp_tb[IFLA_XDP_DRV_PROG_ID]);
+
+       if (xdp_tb[IFLA_XDP_HW_PROG_ID])
+               xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32(
+                       xdp_tb[IFLA_XDP_HW_PROG_ID]);
 
        return 0;
 }
 
-int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags)
+int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
+                         size_t info_size, __u32 flags)
 {
        struct xdp_id_md xdp_id = {};
        int sock, ret;
        __u32 nl_pid;
        __u32 mask;
 
-       if (flags & ~XDP_FLAGS_MASK)
+       if (flags & ~XDP_FLAGS_MASK || !info_size)
                return -EINVAL;
 
        /* Check whether the single {HW,DRV,SKB} mode is set */
        xdp_id.ifindex = ifindex;
        xdp_id.flags = flags;
 
-       ret = libbpf_nl_get_link(sock, nl_pid, get_xdp_id, &xdp_id);
-       if (!ret)
-               *prog_id = xdp_id.id;
+       ret = libbpf_nl_get_link(sock, nl_pid, get_xdp_info, &xdp_id);
+       if (!ret) {
+               size_t sz = min(info_size, sizeof(xdp_id.info));
+
+               memcpy(info, &xdp_id.info, sz);
+               memset((void *) info + sz, 0, info_size - sz);
+       }
 
        close(sock);
        return ret;
 }
 
+static __u32 get_xdp_id(struct xdp_link_info *info, __u32 flags)
+{
+       if (info->attach_mode != XDP_ATTACHED_MULTI)
+               return info->prog_id;
+       if (flags & XDP_FLAGS_DRV_MODE)
+               return info->drv_prog_id;
+       if (flags & XDP_FLAGS_HW_MODE)
+               return info->hw_prog_id;
+       if (flags & XDP_FLAGS_SKB_MODE)
+               return info->skb_prog_id;
+
+       return 0;
+}
+
+int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags)
+{
+       struct xdp_link_info info;
+       int ret;
+
+       ret = bpf_get_link_xdp_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)
 {