diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000000000..3b57405d4b5084 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,15 @@ +[submodule "lib/lunatik"] + path = lib/lunatik + url = https://github.com/luainkernel/lunatik.git +[submodule "lib/luadata"] + path = lib/luadata + url = https://github.com/luainkernel/luadata +[submodule "lib/luaxdp"] + path = lib/luaxdp + url = https://github.com/luainkernel/luaxdp.git +[submodule "lib/luarcu"] + path = lib/luarcu + url = https://github.com/luainkernel/luarcu +[submodule "lib/luaunpack"] + path = lib/luaunpack + url = https://github.com/VictorNogueiraRio/luaunpack.git diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d837dad24b4ce5..d2f7b0f57d4dc8 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3587,6 +3587,10 @@ 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); +#ifdef CONFIG_XDP_LUA +int generic_xdp_lua_install_prog(char *lua_prog); +#endif /* CONFIG_XDP_LUA */ + 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/net/xdp.h b/include/net/xdp.h index 76b95256c26649..7e896f31feec5d 100644 --- a/include/net/xdp.h +++ b/include/net/xdp.h @@ -63,6 +63,19 @@ struct xdp_rxq_info { struct xdp_mem_info mem; } ____cacheline_aligned; /* perf critical, avoid false-sharing */ +#ifdef CONFIG_XDP_LUA +struct xdplua_create_work { + char lua_script[8192]; + struct lua_State *L; + struct work_struct work; + spinlock_t lock; + bool init; +}; + +DECLARE_PER_CPU(struct xdplua_create_work, luaworks); +#endif /* CONFIG_XDP_LUA */ + + struct xdp_buff { void *data; void *data_end; @@ -70,6 +83,10 @@ struct xdp_buff { void *data_hard_start; unsigned long handle; struct xdp_rxq_info *rxq; +#ifdef CONFIG_XDP_LUA + struct sk_buff *skb; + struct lua_State *L; +#endif /* CONFIG_XDP_LUA */ }; struct xdp_frame { diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 66917a4eba2716..8bf2fd858c3f4e 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -2226,7 +2226,24 @@ union bpf_attr { FN(get_current_cgroup_id), \ FN(get_local_storage), \ FN(sk_select_reuseport), \ - FN(skb_ancestor_cgroup_id), + FN(skb_ancestor_cgroup_id), \ + /* #ifdef CONFIG_XDP_LUA */ \ + FN(lua_dataref), \ + FN(lua_dataunref), \ + FN(lua_pcall), \ + FN(lua_tostring), \ + FN(lua_pop), \ + FN(lua_pushinteger), \ + FN(lua_pushlightuserdata), \ + FN(lua_pushlstring), \ + FN(lua_pushmap), \ + FN(lua_pushskb), \ + FN(lua_pushstring), \ + FN(lua_toboolean), \ + FN(lua_tointeger), \ + FN(lua_newpacket), \ + FN(lua_type), + /* #endif CONFIG_XDP_LUA */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 43391e2d1153ad..45a39ccb3032b2 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -945,6 +945,9 @@ enum { IFLA_XDP_DRV_PROG_ID, IFLA_XDP_SKB_PROG_ID, IFLA_XDP_HW_PROG_ID, +#ifdef CONFIG_XDP_LUA + IFLA_XDP_LUA_PROG, +#endif /* CONFIG_XDP_LUA */ __IFLA_XDP_MAX, }; diff --git a/init/Kconfig b/init/Kconfig index 1e234e2f1cba7a..ea5c89ce390876 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1147,6 +1147,12 @@ config HAVE_PCSPKR_PLATFORM config BPF bool +config LUNATIK + bool "Lunatik" + default n + help + Support for the Lua interpreter + menuconfig EXPERT bool "Configure standard kernel features (expert users)" # Unhide debug options, to make the on-by-default options visible diff --git a/lib/Makefile b/lib/Makefile index 42387644681094..5d1cc009a5a017 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -270,3 +270,12 @@ obj-$(CONFIG_GENERIC_LIB_LSHRDI3) += lshrdi3.o obj-$(CONFIG_GENERIC_LIB_MULDI3) += muldi3.o obj-$(CONFIG_GENERIC_LIB_CMPDI2) += cmpdi2.o obj-$(CONFIG_GENERIC_LIB_UCMPDI2) += ucmpdi2.o + +subdir-ccflags-y += -I$(srctree)/lib/lunatik/lua \ + -I$(srctree)/lib/luadata/ -I$(srctree)/lib/luaunpack/ \ + -D_KERNEL +obj-$(CONFIG_LUNATIK) += lunatik/ +obj-$(CONFIG_LUADATA) += luadata/ +obj-$(CONFIG_LUAXDP) += luaxdp/ +obj-$(CONFIG_LUARCU) += luarcu/ +obj-$(CONFIG_LUAUNPACK) += luaunpack/ diff --git a/lib/luadata b/lib/luadata new file mode 160000 index 00000000000000..21137a054a8281 --- /dev/null +++ b/lib/luadata @@ -0,0 +1 @@ +Subproject commit 21137a054a828123deea403b106a87e9e8d2795d diff --git a/lib/luarcu b/lib/luarcu new file mode 160000 index 00000000000000..bc563d5245afc8 --- /dev/null +++ b/lib/luarcu @@ -0,0 +1 @@ +Subproject commit bc563d5245afc8024dd3f47fd8da404a781e0215 diff --git a/lib/luaunpack b/lib/luaunpack new file mode 160000 index 00000000000000..0589fc254bedaa --- /dev/null +++ b/lib/luaunpack @@ -0,0 +1 @@ +Subproject commit 0589fc254bedaa648042710a539a1db8cfa3445d diff --git a/lib/luaxdp b/lib/luaxdp new file mode 160000 index 00000000000000..be34f8706d3c96 --- /dev/null +++ b/lib/luaxdp @@ -0,0 +1 @@ +Subproject commit be34f8706d3c96e1cdb61015c37bc0dd83aeb75b diff --git a/lib/lunatik b/lib/lunatik new file mode 160000 index 00000000000000..fb7ef47c930b54 --- /dev/null +++ b/lib/lunatik @@ -0,0 +1 @@ +Subproject commit fb7ef47c930b544804f544654302dac673a6ca48 diff --git a/net/core/Makefile b/net/core/Makefile index 80175e6a2eb871..6193c9338ccaf4 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -8,6 +8,10 @@ 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 = -Ilib/lunatik/lua/ -D_KERNEL \ + -Ilib/luadata/ +CFLAGS_filter.o = -Ilib/lunatik/lua/ -D_KERNEL \ + -Ilib/luadata/ -Ilib/luaunpack/ 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 93243479085fb1..dbe5b3797b9bd0 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -72,6 +72,14 @@ * - netif_rx() feedback */ +#ifdef CONFIG_XDP_LUA +#include +#include +#include +#include +#include +#endif /* CONFIG_XDP_LUA */ + #include #include #include @@ -155,6 +163,9 @@ static DEFINE_SPINLOCK(ptype_lock); static DEFINE_SPINLOCK(offload_lock); +#ifdef CONFIG_XDP_LUA +DEFINE_PER_CPU(struct xdplua_create_work, luaworks); +#endif 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; @@ -4279,6 +4290,9 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb, u32 metalen, act = XDP_DROP; int hlen, off; u32 mac_len; +#ifdef CONFIG_XDP_LUA + struct xdplua_create_work *lw; +#endif /* CONFIG_XDP_LUA */ /* Reinjected packets coming from act_mirred or similar should * not get XDP generic processing. @@ -4320,9 +4334,22 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb, rxqueue = netif_get_rxqueue(skb); xdp->rxq = &rxqueue->xdp_rxq; +#ifdef CONFIG_XDP_LUA + lw = this_cpu_ptr(&luaworks); + + xdp->skb = skb; + xdp->L = lw->L; +#endif /* CONFIG_XDP_LUA */ act = bpf_prog_run_xdp(xdp_prog, xdp); +#ifdef CONFIG_XDP_LUA + if (lw->init) { + lw->init = false; + spin_unlock(&lw->lock); + } +#endif /* CONFIG_XDP_LUA */ + off = xdp->data - orig_data; if (off > 0) __skb_pull(skb, off); @@ -5088,6 +5115,35 @@ static int generic_xdp_install(struct net_device *dev, struct netdev_bpf *xdp) return ret; } +#ifdef CONFIG_XDP_LUA + +static void per_cpu_xdp_lua_install(struct work_struct *w) { + int this_cpu = smp_processor_id(); + struct xdplua_create_work *lw = + container_of(w, struct xdplua_create_work, work); + + spin_lock_bh(&lw->lock); + if (luaL_dostring(lw->L, lw->lua_script)) { + pr_err(KERN_INFO "error: %s\nOn cpu: %d\n", + lua_tostring(lw->L, -1), this_cpu); + } + spin_unlock_bh(&lw->lock); +} + +int generic_xdp_lua_install_prog(char *lua_script) +{ + int cpu; + struct xdplua_create_work *lw; + + for_each_possible_cpu(cpu) { + lw = per_cpu_ptr(&luaworks, cpu); + strcpy(lw->lua_script, lua_script); + schedule_work_on(cpu, &lw->work); + } + return 0; +} +#endif /* CONFIG_XDP_LUA */ + static int netif_receive_skb_internal(struct sk_buff *skb) { int ret; @@ -9595,6 +9651,9 @@ static int __init net_dev_init(void) for_each_possible_cpu(i) { struct work_struct *flush = per_cpu_ptr(&flush_works, i); struct softnet_data *sd = &per_cpu(softnet_data, i); +#ifdef CONFIG_XDP_LUA + struct xdplua_create_work *lw = per_cpu_ptr(&luaworks, i); +#endif INIT_WORK(flush, flush_backlog); @@ -9614,6 +9673,19 @@ static int __init net_dev_init(void) init_gro_hash(&sd->backlog); sd->backlog.poll = process_backlog; sd->backlog.weight = weight_p; +#ifdef CONFIG_XDP_LUA + lw->L = luaL_newstate(); + WARN_ON(!lw->L); + + if (!lw->L) + continue; + + luaL_openlibs(lw->L); + luaL_requiref(lw->L, "data", luaopen_data, 1); + lua_pop(lw->L, 1); + + INIT_WORK(&lw->work, per_cpu_xdp_lua_install); +#endif /* CONFIG_XDP_LUA */ } dev_boot_phase = 0; diff --git a/net/core/filter.c b/net/core/filter.c index 5e00f2b85a5681..af202adec86183 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -69,6 +69,12 @@ #include #include +#ifdef CONFIG_XDP_LUA +#include +#include +#include +#endif /* CONFIG_XDP_LUA */ + /** * sk_filter_trim_cap - run a packet through a socket filter * @sk: sock associated with &sk_buff @@ -4787,6 +4793,277 @@ static const struct bpf_func_proto bpf_lwt_seg6_adjust_srh_proto = { }; #endif /* CONFIG_IPV6_SEG6_BPF */ +#ifdef CONFIG_XDP_LUA +static inline void verify_and_lock(void) { + struct xdplua_create_work *lw; + + lw = this_cpu_ptr(&luaworks); + if (!lw->init) { + lw->init = true; + spin_lock(&lw->lock); + } +} + +BPF_CALL_2(bpf_lua_dataref, struct xdp_buff *, ctx, int, offset) { + if (offset + ctx->data < ctx->data_end) { + int data_ref; + + verify_and_lock(); + data_ref = ldata_newref(ctx->L, ctx->data + offset, + ctx->data_end - ctx->data - offset); + return data_ref; + } + + return -1; +} + +static const struct bpf_func_proto bpf_lua_dataref_proto = { + .func = bpf_lua_dataref, + .gpl_only = false, + .pkt_access = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg1_type = ARG_ANYTHING, +}; + +BPF_CALL_2(bpf_lua_dataunref, struct xdp_buff *, ctx, int, data_ref) { + verify_and_lock(); + ldata_unref(ctx->L, data_ref); + return 0; +} + +static const struct bpf_func_proto bpf_lua_dataunref_proto = { + .func = bpf_lua_dataunref, + .gpl_only = false, + .pkt_access = false, + .ret_type = RET_VOID, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +}; + +BPF_CALL_4(bpf_lua_pcall, struct xdp_buff *, ctx, char *, funcname, + int, num_args, int, num_rets) { + int base; + + verify_and_lock(); + + base = lua_gettop(ctx->L) - num_args; + if (lua_getglobal(ctx->L, funcname) != LUA_TFUNCTION) { + pr_err("function %s not found\n", funcname); + num_rets = 0; + goto clean_state; + } + + lua_insert(ctx->L, base + 1); + if (lua_pcall(ctx->L, num_args, num_rets, 0)) { + pr_err("%s\n", lua_tostring(ctx->L, -1)); + num_rets = 0; + goto clean_state; + } + + base += num_rets; + +clean_state: + lua_settop(ctx->L, base); + return num_rets; +} + +static const struct bpf_func_proto bpf_lua_pcall_proto = { + .func = bpf_lua_pcall, + .gpl_only = false, + .pkt_access = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = RET_INTEGER, + .arg4_type = RET_INTEGER, +}; + +BPF_CALL_2(bpf_lua_pop, struct xdp_buff *, ctx, int, index) { + verify_and_lock(); + lua_pop(ctx->L, index); + return 0; +} + +static const struct bpf_func_proto bpf_lua_pop_proto = { + .func = bpf_lua_pop, + .gpl_only = false, + .pkt_access = false, + .ret_type = RET_VOID, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +}; + +BPF_CALL_2(bpf_lua_pushinteger, struct xdp_buff *, ctx, lua_Integer, integer) { + verify_and_lock(); + lua_pushinteger(ctx->L, integer); + return 0; +} + +static const struct bpf_func_proto bpf_lua_pushinteger_proto = { + .func = bpf_lua_pushinteger, + .gpl_only = false, + .pkt_access = false, + .ret_type = RET_VOID, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +}; + +BPF_CALL_2(bpf_lua_pushlightuserdata, struct xdp_buff *, ctx, void *, ptr) { + verify_and_lock(); + lua_pushlightuserdata(ctx->L, ptr); + return 0; +} + +static const struct bpf_func_proto bpf_lua_pushlightuserdata_proto = { + .func = bpf_lua_pushlightuserdata, + .gpl_only = false, + .pkt_access = false, + .ret_type = RET_VOID, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +}; + +BPF_CALL_3(bpf_lua_pushlstring, struct xdp_buff *, ctx, const char *, str, size_t, len) { + verify_and_lock(); + lua_pushlstring(ctx->L, str, len); + return 0; +} + +static const struct bpf_func_proto bpf_lua_pushlstring_proto = { + .func = bpf_lua_pushlstring, + .gpl_only = false, + .pkt_access = false, + .ret_type = RET_VOID, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, +}; + +BPF_CALL_2(bpf_lua_pushmap, struct xdp_buff *, ctx, struct bpf_map *, map) { + verify_and_lock(); + lua_pushlightuserdata(ctx->L, map); + return 0; +} + +static const struct bpf_func_proto bpf_lua_pushmap_proto = { + .func = bpf_lua_pushmap, + .gpl_only = false, + .pkt_access = false, + .ret_type = RET_VOID, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +}; + +BPF_CALL_1(bpf_lua_pushskb, struct xdp_buff *, ctx) { + verify_and_lock(); + lua_pushlightuserdata(ctx->L, ctx->skb); + return 0; +} + +static const struct bpf_func_proto bpf_lua_pushskb_proto = { + .func = bpf_lua_pushskb, + .gpl_only = false, + .pkt_access = false, + .ret_type = RET_VOID, + .arg1_type = ARG_PTR_TO_CTX, +}; + +BPF_CALL_2(bpf_lua_pushstring, struct xdp_buff *, ctx, const char *, str) { + verify_and_lock(); + lua_pushstring(ctx->L, str); + return 0; +} + +static const struct bpf_func_proto bpf_lua_pushstring_proto = { + .func = bpf_lua_pushstring, + .gpl_only = false, + .pkt_access = false, + .ret_type = RET_VOID, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +}; + +BPF_CALL_2(bpf_lua_toboolean, struct xdp_buff *, ctx, int, index) { + verify_and_lock(); + return lua_toboolean(ctx->L, index); +} + +static const struct bpf_func_proto bpf_lua_toboolean_proto = { + .func = bpf_lua_toboolean, + .gpl_only = false, + .pkt_access = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +}; + +BPF_CALL_2(bpf_lua_tointeger, struct xdp_buff *, ctx, int, index) { + verify_and_lock(); + return lua_tointeger(ctx->L, index); +} + +static const struct bpf_func_proto bpf_lua_tointeger_proto = { + .func = bpf_lua_tointeger, + .gpl_only = false, + .pkt_access = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +}; + +BPF_CALL_4(bpf_lua_tostring, struct xdp_buff *, ctx, char *, str, u32, size, int, index) { + if (lua_isstring(ctx->L, index)) { + strncpy(str, lua_tostring(ctx->L, index), size); + return 1; + } + + return 0; +} + +static const struct bpf_func_proto bpf_lua_tostring_proto = { + .func = bpf_lua_tostring, + .gpl_only = false, + .pkt_access = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_PTR_TO_UNINIT_MEM, + .arg3_type = ARG_CONST_SIZE, + .arg4_type = ARG_ANYTHING, +}; + +BPF_CALL_2(bpf_lua_newpacket, struct xdp_buff *, ctx, int, offset) { + if (offset + ctx->data < ctx->data_end) { + return lunpack_newpacket(ctx->L, ctx->data + offset, + ctx->data_end - ctx->data - offset); + } + + return -EINVAL; +} + +static const struct bpf_func_proto bpf_lua_newpacket_proto = { + .func = bpf_lua_newpacket, + .gpl_only = false, + .pkt_access = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +}; + +BPF_CALL_2(bpf_lua_type, struct xdp_buff *, ctx, int, index) { + return lua_type(ctx->L, index); +} + +static const struct bpf_func_proto bpf_lua_type_proto = { + .func = bpf_lua_type, + .gpl_only = false, + .pkt_access = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +}; +#endif /* CONFIG_XDP_LUA */ + bool bpf_helper_changes_pkt_data(void *func) { if (func == bpf_skb_vlan_push || @@ -4842,6 +5119,38 @@ bpf_base_func_proto(enum bpf_func_id func_id) if (capable(CAP_SYS_ADMIN)) return bpf_get_trace_printk_proto(); /* else: fall through */ +#ifdef CONFIG_XDP_LUA + case BPF_FUNC_lua_dataref: + return &bpf_lua_dataref_proto; + case BPF_FUNC_lua_dataunref: + return &bpf_lua_dataunref_proto; + case BPF_FUNC_lua_pcall: + return &bpf_lua_pcall_proto; + case BPF_FUNC_lua_pop: + return &bpf_lua_pop_proto; + case BPF_FUNC_lua_pushinteger: + return &bpf_lua_pushinteger_proto; + case BPF_FUNC_lua_pushlightuserdata: + return &bpf_lua_pushlightuserdata_proto; + case BPF_FUNC_lua_pushlstring: + return &bpf_lua_pushlstring_proto; + case BPF_FUNC_lua_pushmap: + return &bpf_lua_pushmap_proto; + case BPF_FUNC_lua_pushskb: + return &bpf_lua_pushskb_proto; + case BPF_FUNC_lua_pushstring: + return &bpf_lua_pushstring_proto; + case BPF_FUNC_lua_toboolean: + return &bpf_lua_toboolean_proto; + case BPF_FUNC_lua_tostring: + return &bpf_lua_tostring_proto; + case BPF_FUNC_lua_tointeger: + return &bpf_lua_tointeger_proto; + case BPF_FUNC_lua_newpacket: + return &bpf_lua_newpacket_proto; + case BPF_FUNC_lua_type: + return &bpf_lua_type_proto; +#endif /* CONFIG_XDP_LUA */ default: return NULL; } diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 37c7936124e618..9b1b67995bf82b 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1790,6 +1790,9 @@ 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 }, +#ifdef CONFIG_XDP_LUA + [IFLA_XDP_LUA_PROG] = { .type = NLA_STRING, .len = 8192 }, +#endif /* CONFIG_XDP_LUA */ }; static const struct rtnl_link_ops *linkinfo_to_kind_ops(const struct nlattr *nla) @@ -2628,6 +2631,21 @@ static int do_setlink(const struct sk_buff *skb, goto errout; status |= DO_SETLINK_NOTIFY; } + +#ifdef CONFIG_XDP_LUA + 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; + } +#endif /* CONFIG_XDP_LUA */ + } errout: diff --git a/net/xdp/Kconfig b/net/xdp/Kconfig index 90e4a7152854ff..f91e344dafe76c 100644 --- a/net/xdp/Kconfig +++ b/net/xdp/Kconfig @@ -5,3 +5,10 @@ config XDP_SOCKETS help XDP sockets allows a channel between XDP programs and userspace applications. + +config XDP_LUA + bool "XDP LUA" + depends on BPF_SYSCALL && LUNATIK + default n + help + Support for Lua scripts inside XDP diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 36f9f41d094b2a..a44e56600230b4 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -53,6 +53,8 @@ hostprogs-y += xdpsock hostprogs-y += xdp_fwd hostprogs-y += task_fd_query hostprogs-y += xdp_sample_pkts +# CONFIG_XDP_LUA +hostprogs-y += xdplua # Libbpf dependencies LIBBPF = $(TOOLS_PATH)/lib/bpf/libbpf.a @@ -109,6 +111,8 @@ xdpsock-objs := xdpsock_user.o xdp_fwd-objs := xdp_fwd_user.o 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) +# CONFIG_XDP_LUA +xdplua-objs := xdplua_user.o # Tell kbuild to always build the programs always := $(hostprogs-y) @@ -166,6 +170,8 @@ always += xdpsock_kern.o always += xdp_fwd_kern.o always += task_fd_query_kern.o always += xdp_sample_pkts_kern.o +# CONFIG_XDP_LUA +always += xdplua_map_kern.o KBUILD_HOSTCFLAGS += -I$(objtree)/usr/include KBUILD_HOSTCFLAGS += -I$(srctree)/tools/lib/ diff --git a/samples/bpf/map.lua b/samples/bpf/map.lua new file mode 100644 index 00000000000000..aa6e733424a8d2 --- /dev/null +++ b/samples/bpf/map.lua @@ -0,0 +1,22 @@ +-- +-- Copyright (C) 2019-2020 Victor Nogueira +-- +-- This program is free software; you can redistribute it and/or +-- modify it under the terms of the GNU General Public License as +-- published by the Free Software Foundation version 2. +-- +-- This program is distributed "as is" WITHOUT ANY WARRANTY of any +-- kind, whether express or implied; without even the implied warranty +-- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +xdp = require'xdp' + +function lookup(map) + local val = xdp.map_lookup(map, 1) + print('val', val) +end + +function update(map) + xdp.map_update(map, 1, 3) +end diff --git a/samples/bpf/xdplua_map_kern.c b/samples/bpf/xdplua_map_kern.c new file mode 100644 index 00000000000000..d68d20732bb754 --- /dev/null +++ b/samples/bpf/xdplua_map_kern.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019-2020 Victor Nogueira + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#define KBUILD_MODNAME "foo" +#include +#include "bpf_helpers.h" + +struct bpf_map_def SEC("maps") test_map = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(int), + .value_size = sizeof(int), + .max_entries = 20, +}; + +SEC("xdp_lua_test_map") +int xdp_lua_test_map_prog(struct xdp_md *ctx) +{ + char lookupname[] = "lookup"; + char updatename[] = "update"; + + bpf_lua_pushmap(ctx, &test_map); + bpf_lua_pcall(ctx, updatename, 1, 0); + + bpf_lua_pushmap(ctx, &test_map); + bpf_lua_pcall(ctx, lookupname, 1, 0); + + return XDP_PASS; +} + +char _license[] SEC("license") = "GPL"; diff --git a/samples/bpf/xdplua_user.c b/samples/bpf/xdplua_user.c new file mode 100644 index 00000000000000..e6c5f9da114177 --- /dev/null +++ b/samples/bpf/xdplua_user.c @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2019-2020 Victor Nogueira + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bpf/libbpf.h" +#include "bpf/bpf.h" + +#include "bpf_util.h" + +static int ifindex = 0; + +static void usage(const char *prog) { + fprintf(stderr, "usage: %s [OPTS]\n" + "\nOPTS:\n" + " -d detach program\n" + " -s lua script path\n" + " -p eBPF program path\n" + " -i iface\n" + " -m monitor\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 xopen lua file"); + return NULL; + } + + fseek(f, 0 , SEEK_END); + prog_size = ftell(f); + rewind(f); + + lua_prog = (char *) malloc(prog_size + 1); + memset(lua_prog, 0, 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'; + fclose(f); + return lua_prog; +} + +static int do_attach_ebpf(int idx, int fd, const char *name) +{ + int err; + + err = bpf_set_link_xdp_fd(idx, fd, XDP_FLAGS_SKB_MODE); + if (err < 0) + fprintf(stderr, "ERROR: failed to attach program to %s\n", name); + + return err; +} + +static int do_attach_lua(const char *name, char *lua_prog) +{ + int err; + + err = bpf_set_link_xdp_lua_prog(lua_prog); + if (err < 0) + fprintf(stderr, "ERROR: failed to attach lua script to %s\n", name); + + return err; +} + +static int do_detach(int idx, const char *name) +{ + int err; + + err = bpf_set_link_xdp_fd(idx, -1, XDP_FLAGS_SKB_MODE); + if (err < 0) + fprintf(stderr, "ERROR: failed to detach program from %s\n", name); + + return err; +} + +static void poll(int map_fd, int interval) { + long cnt; + unsigned int key = 0; + + while(1) { + bpf_map_lookup_elem(map_fd, &key, &cnt); + printf("pkt count: %lu\n", cnt); + sleep(interval); + } +} + +int main(int argc, char *argv[]) +{ + struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; + char lua_filename[256]; + char filename[256]; + struct bpf_object *obj; + int opt, prog_fd; + int rx_cnt_map_fd; + int detach = 0, attach_lua = 0, attach_ebpf = 0, monitor = 0; + char *lua_prog = NULL; + const char *optstr = "s:p:i:dm"; + struct bpf_prog_load_attr prog_load_attr = { + .prog_type = BPF_PROG_TYPE_XDP, + }; + struct bpf_map *map; + + memset(lua_filename, 0, 256); + memset(filename, 0, 256); + while ((opt = getopt(argc, argv, optstr)) != -1) { + switch (opt) { + case 's': + snprintf(lua_filename, sizeof(lua_filename), + "%s", optarg); + attach_lua = 1; + break; + case 'p': + snprintf(filename, sizeof(filename), + "%s", optarg); + attach_ebpf = 1; + break; + case 'd': + detach = 1; + break; + case 'i': + ifindex = if_nametoindex(optarg); + break; + case 'm': + monitor = 1; + break; + default: + usage(basename(argv[0])); + return 1; + } + } + + if (attach_ebpf || detach) { + if (!ifindex) { + printf("ERROR: invalid interface name"); + return 1; + } + } + + if (setrlimit(RLIMIT_MEMLOCK, &r)) { + perror("ERROR: setrlimit(RLIMIT_MEMLOCK)"); + return 1; + } + + if (detach) { + if (do_detach(ifindex, lua_filename) < 0) + return 1; + + return 0; + } + + if (attach_ebpf) { + prog_load_attr.file = filename; + + if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) + return 1; + + if (!prog_fd) { + printf("ERROR: failed to load_bpf_file\n"); + return 1; + } + + if (do_attach_ebpf(ifindex, prog_fd, lua_filename) < 0) + return 1; + + } + + if (attach_lua) { + lua_prog = extract_lua_prog(lua_filename); + if (!lua_prog) + return 1; + + if (do_attach_lua(lua_filename, lua_prog) < 0) { + free(lua_prog); + return 1; + } + + free(lua_prog); + } + + if (monitor) { + map = bpf_object__find_map_by_name(obj, "rx_cnt"); + rx_cnt_map_fd = bpf_map__fd(map); + + poll(rx_cnt_map_fd, 1); + } + return 0; +} diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 66917a4eba2716..8bf2fd858c3f4e 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -2226,7 +2226,24 @@ union bpf_attr { FN(get_current_cgroup_id), \ FN(get_local_storage), \ FN(sk_select_reuseport), \ - FN(skb_ancestor_cgroup_id), + FN(skb_ancestor_cgroup_id), \ + /* #ifdef CONFIG_XDP_LUA */ \ + FN(lua_dataref), \ + FN(lua_dataunref), \ + FN(lua_pcall), \ + FN(lua_tostring), \ + FN(lua_pop), \ + FN(lua_pushinteger), \ + FN(lua_pushlightuserdata), \ + FN(lua_pushlstring), \ + FN(lua_pushmap), \ + FN(lua_pushskb), \ + FN(lua_pushstring), \ + FN(lua_toboolean), \ + FN(lua_tointeger), \ + FN(lua_newpacket), \ + FN(lua_type), + /* #endif CONFIG_XDP_LUA */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/tools/include/uapi/linux/if_link.h b/tools/include/uapi/linux/if_link.h index 43391e2d1153ad..9318bac55ac60b 100644 --- a/tools/include/uapi/linux/if_link.h +++ b/tools/include/uapi/linux/if_link.h @@ -945,6 +945,9 @@ enum { IFLA_XDP_DRV_PROG_ID, IFLA_XDP_SKB_PROG_ID, IFLA_XDP_HW_PROG_ID, +/* #ifdef CONFIG_XDP_LUA */ + IFLA_XDP_LUA_PROG, +/* #endif CONFIG_XDP_LUA */ __IFLA_XDP_MAX, }; diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 60aa4ca8b2c51b..7af691667a961b 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -620,6 +620,125 @@ int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags) return ret; } +/* #ifdef CONFIG_XDPLUA */ +int bpf_set_link_xdp_lua_prog(char *lua_prog) +{ + struct sockaddr_nl sa; + int sock, seq = 0, len, ret = -1; + char buf[4096]; + struct nlattr *nla, *nla_xdp; + struct { + struct nlmsghdr nh; + struct ifinfomsg ifinfo; + char attrbuf[64]; + } req; + struct nlmsghdr *nh; + struct nlmsgerr *err; + socklen_t addrlen; + int one = 1; + + memset(&sa, 0, sizeof(sa)); + sa.nl_family = AF_NETLINK; + + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) { + return -errno; + } + + if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, + &one, sizeof(one)) < 0) { + fprintf(stderr, "Netlink error reporting not supported\n"); + } + + if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { + ret = -errno; + goto cleanup; + } + + addrlen = sizeof(sa); + if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) { + ret = -errno; + goto cleanup; + } + + if (addrlen != sizeof(sa)) { + ret = -LIBBPF_ERRNO__INTERNAL; + goto cleanup; + } + + 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 = 1; + + /* 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; + } + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + ret = -errno; + goto cleanup; + } + + for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); + nh = NLMSG_NEXT(nh, len)) { + if (nh->nlmsg_pid != sa.nl_pid) { + ret = -LIBBPF_ERRNO__WRNGPID; + goto cleanup; + } + if (nh->nlmsg_seq != seq) { + ret = -LIBBPF_ERRNO__INVSEQ; + goto cleanup; + } + switch (nh->nlmsg_type) { + case NLMSG_ERROR: + err = (struct nlmsgerr *)NLMSG_DATA(nh); + if (!err->error) + continue; + ret = err->error; + nla_dump_errormsg(nh); + goto cleanup; + case NLMSG_DONE: + break; + default: + break; + } + } + + ret = 0; + +cleanup: + close(sock); + return ret; +} +/* #endif CONFIG_XDPLUA */ + int bpf_load_btf(void *btf, __u32 btf_size, char *log_buf, __u32 log_buf_size, bool do_log) { diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 96c55fac54c314..1b88e4b5634b74 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -284,6 +284,9 @@ int bpf_prog_load(const char *file, enum bpf_prog_type type, struct bpf_object **pobj, int *prog_fd); int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags); +/* #ifdef CONFIG_XDP_LUA */ +int bpf_set_link_xdp_lua_prog(char *lua_prog); +/* #endif CONFIG_XDP_LUA */ enum bpf_perf_event_ret { LIBBPF_PERF_EVENT_DONE = 0, diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h index e4be7730222dff..0aa1c86eba7d67 100644 --- a/tools/testing/selftests/bpf/bpf_helpers.h +++ b/tools/testing/selftests/bpf/bpf_helpers.h @@ -144,6 +144,41 @@ static unsigned long long (*bpf_skb_cgroup_id)(void *ctx) = static unsigned long long (*bpf_skb_ancestor_cgroup_id)(void *ctx, int level) = (void *) BPF_FUNC_skb_ancestor_cgroup_id; +/* #ifdef CONFIG_XDP_LUA */ +static int (*bpf_lua_dataref)(void *ctx, int offset) = + (void *)BPF_FUNC_lua_dataref; +static void (*bpf_lua_dataunref)(void *ctx, int data_ref) = + (void *)BPF_FUNC_lua_dataunref; +static int (*bpf_lua_pcall)(void *ctx, char *funcname, int num_args, + int num_rets) = + (void *) BPF_FUNC_lua_pcall; +static int (*bpf_lua_tostring)(void *ctx, char *sslsni, u32 size, int index) = + (void *)BPF_FUNC_lua_tostring; +static void (*bpf_lua_pop)(void *ctx, int index) = + (void *)BPF_FUNC_lua_pop; +static void (*bpf_lua_pushinteger)(void *ctx, int num) = + (void *)BPF_FUNC_lua_pushinteger; +static void (*bpf_lua_pushlightuserdata)(void *ctx, void *ptr) = + (void *)BPF_FUNC_lua_pushlightuserdata; +static void (*bpf_lua_pushlstring)(void *ctx, const char *, + size_t len) = + (void *)BPF_FUNC_lua_pushlstring; +static void (*bpf_lua_pushmap)(void *ctx, void *map) = + (void *)BPF_FUNC_lua_pushmap; +static void (*bpf_lua_pushskb)(void *ctx) = + (void *)BPF_FUNC_lua_pushskb; +static void (*bpf_lua_pushstring)(void *ctx, const char *) = + (void *)BPF_FUNC_lua_pushstring; +static int (*bpf_lua_toboolean)(void *ctx, int index) = + (void *)BPF_FUNC_lua_toboolean; +static int (*bpf_lua_tointeger)(void *ctx, int index) = + (void *)BPF_FUNC_lua_tointeger; +static int (*bpf_lua_newpacket)(void *ctx, int offset) = + (void *)BPF_FUNC_lua_newpacket; +static int (*bpf_lua_type)(void *ctx, int index) = + (void *)BPF_FUNC_lua_type; +/* #endif CONFIG_XDP_LUA */ + /* llvm builtin functions that eBPF C program may use to * emit BPF_LD_ABS and BPF_LD_IND instructions */