diff --git a/drivers/net/tun.c b/drivers/net/tun.c index db16d7a13e00c1..806df9c6d6ad63 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1930,7 +1930,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, rcu_read_lock(); xdp_prog = rcu_dereference(tun->xdp_prog); if (xdp_prog) { - ret = do_xdp_generic(xdp_prog, skb); + ret = do_xdp_generic(xdp_prog, skb, NULL); if (ret != XDP_PASS) { rcu_read_unlock(); local_bh_enable(); @@ -2486,7 +2486,7 @@ static int tun_xdp_one(struct tun_struct *tun, skb_probe_transport_header(skb); if (skb_xdp) { - err = do_xdp_generic(xdp_prog, skb); + err = do_xdp_generic(xdp_prog, skb, NULL); if (err != XDP_PASS) goto out; } diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 88292953aa6fdf..9683c894167136 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1918,6 +1918,7 @@ struct net_device { unsigned int real_num_rx_queues; struct bpf_prog __rcu *xdp_prog; + char __rcu *xdp_lua_funcname; unsigned long gro_flush_timeout; rx_handler_func_t __rcu *rx_handler; void __rcu *rx_handler_data; @@ -3612,7 +3613,7 @@ static inline void dev_consume_skb_any(struct sk_buff *skb) } void generic_xdp_tx(struct sk_buff *skb, struct bpf_prog *xdp_prog); -int do_xdp_generic(struct bpf_prog *xdp_prog, struct sk_buff *skb); +int do_xdp_generic(struct bpf_prog *xdp_prog, struct sk_buff *skb, char *lua_funcname); int netif_rx(struct sk_buff *skb); int netif_rx_ni(struct sk_buff *skb); int netif_receive_skb(struct sk_buff *skb); @@ -3684,6 +3685,8 @@ u32 __dev_xdp_query(struct net_device *dev, bpf_op_t xdp_op, enum bpf_netdev_command cmd); int xdp_umem_query(struct net_device *dev, u16 queue_id); +void generic_xdp_lua_install_func(struct net_device *dev, char *lua_funcname); +int generic_xdp_lua_install_prog(char *lua_prog); int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb); int dev_forward_skb(struct net_device *dev, struct sk_buff *skb); bool is_skb_forwardable(const struct net_device *dev, diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 4a8c02cafa9a12..07d6448f52b64f 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -974,6 +974,8 @@ enum { IFLA_XDP_DRV_PROG_ID, IFLA_XDP_SKB_PROG_ID, IFLA_XDP_HW_PROG_ID, + IFLA_XDP_LUA_PROG, + IFLA_XDP_LUA_FUNC, __IFLA_XDP_MAX, }; diff --git a/net/core/Makefile b/net/core/Makefile index a104dc8faafcc5..3162e2b1f6e0f5 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -8,6 +8,7 @@ obj-y := sock.o request_sock.o skbuff.o datagram.o stream.o scm.o \ obj-$(CONFIG_SYSCTL) += sysctl_net_core.o +CFLAGS_dev.o = -Iinclude/linux/lunatik/ -Iinclude/linux/luadata/ -D_KERNEL obj-y += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \ neighbour.o rtnetlink.o utils.o link_watch.o filter.o \ sock_diag.o dev_ioctl.o tso.o sock_reuseport.o \ diff --git a/net/core/dev.c b/net/core/dev.c index 0891f499c1bb7c..58143e44305760 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -67,6 +67,17 @@ * J Hadi Salim : - Backlog queue sampling * - netif_rx() feedback */ +#ifndef _LUNATIK_H +#define _LUNATIK_H +#include +#include +#include +#endif + +#ifndef _LUADATA_H +#define _LUADATA_H +#include +#endif #include #include @@ -150,11 +161,20 @@ /* This should be increased if a protocol with a bigger head is added. */ #define GRO_MAX_HEAD (MAX_HEADER + 128) +struct lua_state_cpu { + lua_State *L; + int cpu; + struct list_head list; +}; + +typedef struct lua_state_cpu lua_state_cpu; + static DEFINE_SPINLOCK(ptype_lock); static DEFINE_SPINLOCK(offload_lock); struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly; struct list_head ptype_all __read_mostly; /* Taps */ static struct list_head offload_base __read_mostly; +static struct list_head lua_state_cpu_list; static int netif_rx_internal(struct sk_buff *skb); static int call_netdevice_notifiers_info(unsigned long val, @@ -868,6 +888,36 @@ struct net_device *dev_get_by_index(struct net *net, int ifindex) } EXPORT_SYMBOL(dev_get_by_index); +u32 lua_prog_run_xdp(lua_State *L, char *lua_funcname, struct xdp_buff *xdp) +{ + u32 ret = 0; + int base; + int data_ref; + + if (!L) + goto out; + + base = lua_gettop(L); + if (lua_getglobal(L, lua_funcname) != LUA_TFUNCTION) { + pr_err("function %s not found\n", lua_funcname); + goto out; + } + + data_ref = ldata_newref(L, xdp->data, xdp->data_end - xdp->data); + if (lua_pcall(L, 1, 1, 0)) { + pr_err("%s\n", lua_tostring(L, -1)); + goto cleanup; + } + + ret = lua_tointeger(L, -1); + +cleanup: + lua_settop(L, base); + ldata_unref(L, data_ref); +out: + return ret; +} + /** * dev_get_by_napi_id - find a device by napi_id * @napi_id: ID of the NAPI struct @@ -4317,7 +4367,8 @@ static struct netdev_rx_queue *netif_get_rxqueue(struct sk_buff *skb) static u32 netif_receive_generic_xdp(struct sk_buff *skb, struct xdp_buff *xdp, - struct bpf_prog *xdp_prog) + struct bpf_prog *xdp_prog, + char *lua_funcname) { struct netdev_rx_queue *rxqueue; void *orig_data, *orig_data_end; @@ -4325,8 +4376,10 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb, __be16 orig_eth_type; struct ethhdr *eth; bool orig_bcast; - int hlen, off; + int hlen, off, cpu; u32 mac_len; + lua_State *L = NULL; + lua_state_cpu *sc; /* Reinjected packets coming from act_mirred or similar should * not get XDP generic processing. @@ -4372,7 +4425,19 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb, rxqueue = netif_get_rxqueue(skb); xdp->rxq = &rxqueue->xdp_rxq; - act = bpf_prog_run_xdp(xdp_prog, xdp); + if (xdp_prog) + act = bpf_prog_run_xdp(xdp_prog, xdp); + + if (act == XDP_PASS || !xdp_prog) { + cpu = smp_processor_id(); + list_for_each_entry(sc, &lua_state_cpu_list, list) { + if (sc->cpu == cpu) { + L = sc->L; + break; + } + } + act = lua_prog_run_xdp(L, lua_funcname, xdp); + } /* check if bpf_xdp_adjust_head was used */ off = xdp->data - orig_data; @@ -4418,7 +4483,8 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb, bpf_warn_invalid_xdp_action(act); /* fall through */ case XDP_ABORTED: - trace_xdp_exception(skb->dev, xdp_prog, act); + if (xdp_prog) + trace_xdp_exception(skb->dev, xdp_prog, act); /* fall through */ case XDP_DROP: do_drop: @@ -4449,7 +4515,9 @@ void generic_xdp_tx(struct sk_buff *skb, struct bpf_prog *xdp_prog) } HARD_TX_UNLOCK(dev, txq); if (free_skb) { - trace_xdp_exception(dev, xdp_prog, XDP_TX); + if (xdp_prog) + trace_xdp_exception(dev, xdp_prog, XDP_TX); + kfree_skb(skb); } } @@ -4457,14 +4525,17 @@ EXPORT_SYMBOL_GPL(generic_xdp_tx); static DEFINE_STATIC_KEY_FALSE(generic_xdp_needed_key); -int do_xdp_generic(struct bpf_prog *xdp_prog, struct sk_buff *skb) +int do_xdp_generic(struct bpf_prog *xdp_prog, struct sk_buff *skb, + char *lua_funcname) { - if (xdp_prog) { + + if (xdp_prog || lua_funcname) { struct xdp_buff xdp; u32 act; int err; - act = netif_receive_generic_xdp(skb, &xdp, xdp_prog); + act = netif_receive_generic_xdp(skb, &xdp, xdp_prog, + lua_funcname); if (act != XDP_PASS) { switch (act) { case XDP_REDIRECT: @@ -4836,7 +4907,8 @@ static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc, int ret2; preempt_disable(); - ret2 = do_xdp_generic(rcu_dereference(skb->dev->xdp_prog), skb); + ret2 = do_xdp_generic(rcu_dereference(skb->dev->xdp_prog), skb, + rcu_dereference(skb->dev->xdp_lua_funcname)); preempt_enable(); if (ret2 != XDP_PASS) @@ -5183,6 +5255,36 @@ static int generic_xdp_install(struct net_device *dev, struct netdev_bpf *xdp) return ret; } +void generic_xdp_lua_install_func(struct net_device *dev, char *lua_funcname) +{ + char *old = rcu_dereference(dev->xdp_lua_funcname); + + ASSERT_RTNL(); + if (!lua_funcname && old) + static_branch_dec(&generic_xdp_needed_key); + else if(lua_funcname && !old) + static_branch_inc(&generic_xdp_needed_key); + + if (old) + kfree(old); + + rcu_assign_pointer(dev->xdp_lua_funcname, lua_funcname); +} + +int generic_xdp_lua_install_prog(char *lua_prog) +{ + lua_state_cpu *sc; + + list_for_each_entry(sc, &lua_state_cpu_list, list) { + if (luaL_dostring(sc->L, lua_prog)) { + pr_err(KERN_INFO "error: %s\nOn cpu: %d\n", + lua_tostring(sc->L, -1), sc->cpu); + return -EINVAL; + } + } + return 0; +} + static int netif_receive_skb_internal(struct sk_buff *skb) { int ret; @@ -9800,6 +9902,7 @@ static struct pernet_operations __net_initdata default_device_ops = { static int __init net_dev_init(void) { int i, rc = -ENOMEM; + lua_state_cpu *new_state_cpu; BUG_ON(!dev_boot_phase); @@ -9814,6 +9917,7 @@ static int __init net_dev_init(void) INIT_LIST_HEAD(&ptype_base[i]); INIT_LIST_HEAD(&offload_base); + INIT_LIST_HEAD(&lua_state_cpu_list); if (register_pernet_subsys(&netdev_net_ops)) goto out; @@ -9844,6 +9948,23 @@ static int __init net_dev_init(void) init_gro_hash(&sd->backlog); sd->backlog.poll = process_backlog; sd->backlog.weight = weight_p; + + new_state_cpu = (lua_state_cpu *) kmalloc(sizeof(lua_state_cpu), + GFP_ATOMIC); + if (!new_state_cpu) + continue; + + new_state_cpu->L = luaL_newstate(); + if (!new_state_cpu->L) { + kfree(new_state_cpu); + continue; + } + + luaL_openlibs(new_state_cpu->L); + luaL_requiref(new_state_cpu->L, "data", luaopen_data, 1); + new_state_cpu->cpu = i; + + list_add(&new_state_cpu->list, &lua_state_cpu_list); } dev_boot_phase = 0; diff --git a/net/core/filter.c b/net/core/filter.c index 4c6a252d42122d..2888d86f4fdbcb 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3502,10 +3502,14 @@ xdp_do_redirect_slow(struct net_device *dev, struct xdp_buff *xdp, if (unlikely(err)) goto err; - _trace_xdp_redirect(dev, xdp_prog, index); + if (xdp_prog) + _trace_xdp_redirect(dev, xdp_prog, index); + return 0; err: - _trace_xdp_redirect_err(dev, xdp_prog, index, err); + if (xdp_prog) + _trace_xdp_redirect_err(dev, xdp_prog, index, err); + return err; } @@ -3620,10 +3624,13 @@ static int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, goto err; ri->map_to_flush = map; - _trace_xdp_redirect_map(dev, xdp_prog, fwd, map, index); + if (xdp_prog) + _trace_xdp_redirect_map(dev, xdp_prog, fwd, map, index); + return 0; err: - _trace_xdp_redirect_map_err(dev, xdp_prog, fwd, map, index, err); + if (xdp_prog) + _trace_xdp_redirect_map_err(dev, xdp_prog, fwd, map, index, err); return err; } @@ -3674,10 +3681,14 @@ static int xdp_do_generic_redirect_map(struct net_device *dev, goto err; } - _trace_xdp_redirect_map(dev, xdp_prog, fwd, map, index); + if (xdp_prog) + _trace_xdp_redirect_map(dev, xdp_prog, fwd, map, index); + return 0; err: - _trace_xdp_redirect_map_err(dev, xdp_prog, fwd, map, index, err); + if (xdp_prog) + _trace_xdp_redirect_map_err(dev, xdp_prog, fwd, map, index, err); + return err; } @@ -3705,11 +3716,15 @@ int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb, goto err; skb->dev = fwd; - _trace_xdp_redirect(dev, xdp_prog, index); + if (xdp_prog) + _trace_xdp_redirect(dev, xdp_prog, index); + generic_xdp_tx(skb, xdp_prog); return 0; err: - _trace_xdp_redirect_err(dev, xdp_prog, index, err); + if (xdp_prog) + _trace_xdp_redirect_err(dev, xdp_prog, index, err); + return err; } EXPORT_SYMBOL_GPL(xdp_do_generic_redirect); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 1ee6460f82756b..6f8197e6c7241f 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1799,6 +1799,8 @@ static const struct nla_policy ifla_xdp_policy[IFLA_XDP_MAX + 1] = { [IFLA_XDP_ATTACHED] = { .type = NLA_U8 }, [IFLA_XDP_FLAGS] = { .type = NLA_U32 }, [IFLA_XDP_PROG_ID] = { .type = NLA_U32 }, + [IFLA_XDP_LUA_PROG] = { .type = NLA_STRING, .len = 8192 }, + [IFLA_XDP_LUA_FUNC] = { .type = NLA_STRING, .len = 256 }, }; static const struct rtnl_link_ops *linkinfo_to_kind_ops(const struct nlattr *nla) @@ -2707,6 +2709,32 @@ static int do_setlink(const struct sk_buff *skb, goto errout; status |= DO_SETLINK_NOTIFY; } + + if (xdp[IFLA_XDP_LUA_FUNC]) { + char *lua_funcname; + + if (strlen(nla_data(xdp[IFLA_XDP_LUA_FUNC]))) { + lua_funcname = kmalloc(256, GFP_KERNEL); + memcpy(lua_funcname, nla_data(xdp[IFLA_XDP_LUA_FUNC]), 256); + } else { + lua_funcname = NULL; + } + + generic_xdp_lua_install_func(dev, lua_funcname); + } + + if (xdp[IFLA_XDP_LUA_PROG]) { + char *lua_prog = nla_data(xdp[IFLA_XDP_LUA_PROG]); + if (!lua_prog) { + err = -EINVAL; + goto errout; + } + + err = generic_xdp_lua_install_prog(lua_prog); + if (err) + goto errout; + } + } errout: diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 1d9be26b4edd79..d8fe02bccf0293 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -53,6 +53,7 @@ hostprogs-y += task_fd_query hostprogs-y += xdp_sample_pkts hostprogs-y += ibumad hostprogs-y += hbm +hostprogs-y += xdplua # Libbpf dependencies LIBBPF = $(TOOLS_PATH)/lib/bpf/libbpf.a @@ -109,6 +110,7 @@ task_fd_query-objs := bpf_load.o task_fd_query_user.o $(TRACE_HELPERS) xdp_sample_pkts-objs := xdp_sample_pkts_user.o $(TRACE_HELPERS) ibumad-objs := bpf_load.o ibumad_user.o $(TRACE_HELPERS) hbm-objs := bpf_load.o hbm.o $(CGROUP_HELPERS) +xdplua-objs := xdplua_user.o # Tell kbuild to always build the programs always := $(hostprogs-y) diff --git a/samples/bpf/xdplua_user.c b/samples/bpf/xdplua_user.c new file mode 100644 index 00000000000000..86416392a0da17 --- /dev/null +++ b/samples/bpf/xdplua_user.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bpf/libbpf.h" + +static int ifindex; + +static void usage(const char *prog) { + fprintf(stderr, + "usage: %s [OPTS] IFACE\n" + "\nOPTS:\n" + " -L LUAFILE load lua script to XDP\n" + " -d detach program\n" + " -F func name\n", + prog); +} + +static char *extract_lua_prog(const char *path) +{ + FILE *f; + long prog_size; + char *lua_prog; + + f = fopen(path, "r"); + if (f == NULL) { + perror("unable to open lua file"); + return NULL; + } + + fseek(f, 0 , SEEK_END); + prog_size = ftell(f); + rewind(f); + + lua_prog = (char *) malloc(prog_size + 1); + if (fread(lua_prog, 1, prog_size, f) < 0) { + perror("unable to read lua file"); + return NULL; + } + + lua_prog[prog_size] = '\0'; + return lua_prog; +} + +static int do_attach_prog(int idx, char *lua_prog) +{ + int err = 0; + + err = bpf_set_link_xdp_lua_prog(idx, lua_prog); + if (err < 0) + printf("ERROR: failed to attach lua script\n"); + + return err; +} + +static int do_attach_func(int idx, char *lua_funcname) +{ + int err = 0; + + err = bpf_set_link_xdp_lua_func(idx, lua_funcname); + if (err < 0) + printf("ERROR: failed to attach lua function\n"); + + return err; +} + +static int do_detach(int idx) +{ + int err; + + err = bpf_set_link_xdp_lua_func(idx, NULL); + if (err < 0) + printf("ERROR: failed to detach program\n"); + + return err; +} + +int main(int argc, char *argv[]) +{ + struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; + const char *optstr = "F:L:d"; + int opt; + char lua_filename[256]; + char lua_funcname[256]; + char *lua_prog = NULL; + int attach = 1; + + memset(lua_funcname, 0, 256); + memset(lua_filename, 0, 256); + while ((opt = getopt(argc, argv, optstr)) != -1) { + switch (opt) { + case 'L': + snprintf(lua_filename, sizeof(lua_filename), + "%s", optarg); + break; + case 'F': + snprintf(lua_funcname, sizeof(lua_funcname), + "%s", optarg); + break; + case 'd': + attach = 0; + break; + default: + usage(basename(argv[0])); + return 1; + } + } + + if (optind == argc) { + usage(basename(argv[0])); + return 1; + } + + if (setrlimit(RLIMIT_MEMLOCK, &r)) { + perror("setrlimit(RLIMIT_MEMLOCK)"); + return 1; + } + + ifindex = if_nametoindex(argv[optind]); + if (!ifindex) { + perror("if_nametoindex"); + return 1; + } + + if (attach) { + if (strlen(lua_filename)) { + lua_prog = extract_lua_prog(lua_filename); + if (do_attach_prog(ifindex, lua_prog) < 0) + return 1; + } + + if (strlen(lua_funcname)) { + if (do_attach_func(ifindex, lua_funcname) < 0) + return 1; + } + } else { + if (do_detach(ifindex) < 0) + return 1; + } + return 0; +} diff --git a/tools/include/uapi/linux/if_link.h b/tools/include/uapi/linux/if_link.h index 4a8c02cafa9a12..07d6448f52b64f 100644 --- a/tools/include/uapi/linux/if_link.h +++ b/tools/include/uapi/linux/if_link.h @@ -974,6 +974,8 @@ enum { IFLA_XDP_DRV_PROG_ID, IFLA_XDP_SKB_PROG_ID, IFLA_XDP_HW_PROG_ID, + IFLA_XDP_LUA_PROG, + IFLA_XDP_LUA_FUNC, __IFLA_XDP_MAX, }; diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 5cbf459ece0b29..4c3650bfaca360 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -357,6 +357,8 @@ LIBBPF_API int bpf_prog_load(const char *file, enum bpf_prog_type type, LIBBPF_API int bpf_set_link_xdp_fd(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_set_link_xdp_lua_prog(int ifindex, char *lua_prog); +LIBBPF_API int bpf_set_link_xdp_lua_func(int ifindex, char *lua_func); struct perf_buffer; diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index f9d316e873d8d2..1ca19156ba5f90 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -105,6 +105,8 @@ LIBBPF_0.0.1 { bpf_prog_linfo__lfind; bpf_raw_tracepoint_open; bpf_set_link_xdp_fd; + bpf_set_link_xdp_lua_prog; + bpf_set_link_xdp_lua_func; bpf_task_fd_query; bpf_verify_program; btf__fd; diff --git a/tools/lib/bpf/netlink.c b/tools/lib/bpf/netlink.c index ce3ec81b71c013..8e49a1b036126f 100644 --- a/tools/lib/bpf/netlink.c +++ b/tools/lib/bpf/netlink.c @@ -187,6 +187,114 @@ int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags) return ret; } +int bpf_set_link_xdp_lua_prog(int ifindex, char *lua_prog) { + int sock, seq = 0, ret; + struct nlattr *nla, *nla_xdp; + struct { + struct nlmsghdr nh; + struct ifinfomsg ifinfo; + char attrbuf[8192]; + } req; + __u32 nl_pid; + + sock = libbpf_netlink_open(&nl_pid); + if (sock < 0) + return sock; + + memset(&req, 0, sizeof(req)); + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + req.nh.nlmsg_type = RTM_SETLINK; + req.nh.nlmsg_pid = 0; + req.nh.nlmsg_seq = ++seq; + req.ifinfo.ifi_family = AF_UNSPEC; + req.ifinfo.ifi_index = ifindex; + + /* 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_len = NLA_HDRLEN; + + /* add XDP LUA PROG */ + nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); + nla_xdp->nla_type = IFLA_XDP_LUA_PROG; + if (lua_prog) { + nla_xdp->nla_len = NLA_HDRLEN + strlen(lua_prog) + 1; + memcpy((char *)nla_xdp + NLA_HDRLEN, lua_prog, strlen(lua_prog) + 1); + } else { + ret = -EINVAL; + goto cleanup; + } + nla->nla_len += nla_xdp->nla_len; + + req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); + + if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { + ret = -errno; + goto cleanup; + } + ret = bpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL); + +cleanup: + close(sock); + return ret; +} + +int bpf_set_link_xdp_lua_func(int ifindex, char *lua_funcname) +{ + int sock, seq = 0, ret; + struct nlattr *nla, *nla_xdp; + struct { + struct nlmsghdr nh; + struct ifinfomsg ifinfo; + char attrbuf[256]; + } req; + __u32 nl_pid; + + sock = libbpf_netlink_open(&nl_pid); + if (sock < 0) + return sock; + + memset(&req, 0, sizeof(req)); + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + req.nh.nlmsg_type = RTM_SETLINK; + req.nh.nlmsg_pid = 0; + req.nh.nlmsg_seq = ++seq; + req.ifinfo.ifi_family = AF_UNSPEC; + req.ifinfo.ifi_index = ifindex; + + /* 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_len = NLA_HDRLEN; + + /* add XDP LUA FUNC */ + nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); + nla_xdp->nla_type = IFLA_XDP_LUA_FUNC; + if (lua_funcname) { + nla_xdp->nla_len = NLA_HDRLEN + strlen(lua_funcname) + 1; + memcpy((char *)nla_xdp + NLA_HDRLEN, lua_funcname, strlen(lua_funcname) + 1); + } else { + nla_xdp->nla_len = NLA_HDRLEN + 1; + } + nla->nla_len += nla_xdp->nla_len; + + req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); + + if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { + ret = -errno; + goto cleanup; + } + ret = bpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL); + +cleanup: + close(sock); + return ret; +} + static int __dump_link_nlmsg(struct nlmsghdr *nlh, libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie) {