* Copyright(c) 2020 Intel Corporation.
  */
 
+#include <bpf/bpf.h>
 #include <bpf/xsk.h>
 #include <linux/version.h>
 #include <poll.h>
        return 1;
 }
 #endif
+
+#ifdef RTE_LIBRTE_AF_XDP_PMD_BPF_LINK
+static int link_lookup(int ifindex, int *link_fd)
+{
+       struct bpf_link_info link_info;
+       __u32 link_len;
+       __u32 id = 0;
+       int err;
+       int fd;
+
+       while (true) {
+               err = bpf_link_get_next_id(id, &id);
+               if (err) {
+                       if (errno == ENOENT) {
+                               err = 0;
+                               break;
+                       }
+                       break;
+               }
+
+               fd = bpf_link_get_fd_by_id(id);
+               if (fd < 0) {
+                       if (errno == ENOENT)
+                               continue;
+                       err = -errno;
+                       break;
+               }
+
+               link_len = sizeof(struct bpf_link_info);
+               memset(&link_info, 0, link_len);
+               err = bpf_obj_get_info_by_fd(fd, &link_info, &link_len);
+               if (err) {
+                       close(fd);
+                       break;
+               }
+               if (link_info.type == BPF_LINK_TYPE_XDP) {
+                       if ((int)link_info.xdp.ifindex == ifindex) {
+                               *link_fd = fd;
+                               break;
+                       }
+               }
+               close(fd);
+       }
+
+       return err;
+}
+
+static bool probe_bpf_link(void)
+{
+       DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts,
+                           .flags = XDP_FLAGS_SKB_MODE);
+       struct bpf_load_program_attr prog_attr;
+       struct bpf_insn insns[2];
+       int prog_fd, link_fd = -1;
+       int ifindex_lo = 1;
+       bool ret = false;
+       int err;
+
+       err = link_lookup(ifindex_lo, &link_fd);
+       if (err)
+               return ret;
+
+       if (link_fd >= 0)
+               return true;
+
+       /* BPF_MOV64_IMM(BPF_REG_0, XDP_PASS), */
+       insns[0].code = BPF_ALU64 | BPF_MOV | BPF_K;
+       insns[0].dst_reg = BPF_REG_0;
+       insns[0].imm = XDP_PASS;
+
+       /* BPF_EXIT_INSN() */
+       insns[1].code = BPF_JMP | BPF_EXIT;
+
+       memset(&prog_attr, 0, sizeof(prog_attr));
+       prog_attr.prog_type = BPF_PROG_TYPE_XDP;
+       prog_attr.insns = insns;
+       prog_attr.insns_cnt = RTE_DIM(insns);
+       prog_attr.license = "GPL";
+
+       prog_fd = bpf_load_program_xattr(&prog_attr, NULL, 0);
+       if (prog_fd < 0)
+               return ret;
+
+       link_fd = bpf_link_create(prog_fd, ifindex_lo, BPF_XDP, &opts);
+       close(prog_fd);
+
+       if (link_fd >= 0) {
+               ret = true;
+               close(link_fd);
+       }
+
+       return ret;
+}
+
+static int link_xdp_program(int if_index, int prog_fd, bool use_bpf_link)
+{
+       DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts);
+       int link_fd, ret = 0;
+
+       if (!use_bpf_link)
+               return bpf_set_link_xdp_fd(if_index, prog_fd,
+                                          XDP_FLAGS_UPDATE_IF_NOEXIST);
+
+       opts.flags = 0;
+       link_fd = bpf_link_create(prog_fd, if_index, BPF_XDP, &opts);
+       if (link_fd < 0)
+               ret = -1;
+
+       return ret;
+}
+#else
+static bool probe_bpf_link(void)
+{
+       return false;
+}
+
+static int link_xdp_program(int if_index, int prog_fd,
+                           bool use_bpf_link __rte_unused)
+{
+       return bpf_set_link_xdp_fd(if_index, prog_fd,
+                                  XDP_FLAGS_UPDATE_IF_NOEXIST);
+}
+#endif
 
 
 if bpf_dep.found() and cc.has_header('bpf/xsk.h') and cc.has_header('linux/if_xdp.h')
     ext_deps += bpf_dep
+    # check for libbpf shared umem APIs
     bpf_ver_dep = dependency('libbpf', version : '>=0.2.0',
             required: false, method: 'pkg-config')
     if bpf_ver_dep.found()
         dpdk_conf.set('RTE_LIBRTE_AF_XDP_PMD_SHARED_UMEM', 1)
     endif
+    # check for libbpf bpf link support
+    bpf_link_dep = dependency('libbpf', version : '>=0.4.0',
+            required: false, method: 'pkg-config')
+    if bpf_link_dep.found()
+        dpdk_conf.set('RTE_LIBRTE_AF_XDP_PMD_BPF_LINK', 1)
+    endif
 else
     build = false
     reason = 'missing dependency, "libbpf"'
 
        bool shared_umem;
        char prog_path[PATH_MAX];
        bool custom_prog_configured;
+       bool use_bpf_link;
 
        struct rte_ether_addr eth_addr;
 
         */
        dev->data->mac_addrs = NULL;
 
-       remove_xdp_program(internals);
+       if (!internals->use_bpf_link)
+               remove_xdp_program(internals);
 
        if (internals->shared_umem) {
                struct internal_list *list;
 }
 
 static int
-load_custom_xdp_prog(const char *prog_path, int if_index)
+load_custom_xdp_prog(const char *prog_path, int if_index, bool use_bpf_link)
 {
        int ret, prog_fd = -1;
        struct bpf_object *obj;
        }
 
        /* Link the program with the given network device */
-       ret = bpf_set_link_xdp_fd(if_index, prog_fd,
-                                       XDP_FLAGS_UPDATE_IF_NOEXIST);
+       ret = link_xdp_program(if_index, prog_fd, use_bpf_link);
        if (ret) {
                AF_XDP_LOG(ERR, "Failed to set prog fd %d on interface\n",
                                prog_fd);
        if (strnlen(internals->prog_path, PATH_MAX) &&
                                !internals->custom_prog_configured) {
                ret = load_custom_xdp_prog(internals->prog_path,
-                                          internals->if_index);
+                                          internals->if_index,
+                                          internals->use_bpf_link);
                if (ret) {
                        AF_XDP_LOG(ERR, "Failed to load custom XDP program %s\n",
                                        internals->prog_path);
        strlcpy(internals->if_name, if_name, IFNAMSIZ);
        strlcpy(internals->prog_path, prog_path, PATH_MAX);
        internals->custom_prog_configured = 0;
+       internals->use_bpf_link = probe_bpf_link();
 
 #ifndef ETH_AF_XDP_SHARED_UMEM
        if (shared_umem) {