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 6c3f7032e8d9d7..b45e5a0e83e824 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3772,6 +3772,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 40c6d3398458f6..8c38a85ebff132 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 22f235260a3a35..776f4bd891e764 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -2890,6 +2890,106 @@ union bpf_attr { * Obtain the 64bit jiffies * Return * The 64 bit jiffies + * + * int bpf_lua_dataref(void *ctx, int offset) + * Description + * Create a new lua data buffer object pointing to the captured + * packet at the specified offset. The function leaves the new + * object on top of the Lua stack. + * Return + * Data object reference number on success, or -1 in case + * of failure + * + * void bpf_lua_dataunref(void *ctx, int data_ref) + * Description + * Releases the data-object reference, allowing it to be + * garbage-collected + * + * void bpf_lua_pcall(void *ctx, char *funcname, int num_args, int num_rets) + * Description + * Calls Lua function funcname with the given nargs arguments in protected mode + * + * void bpf_lua_pop(void *ctx, int n) + * Description + * Pops n elements from the Lua stack + * + * void bpf_lua_pushinteger(void *ctx, int num) + * Description + * Pushes an integer with value n onto the Lua stack. + * + * void bpf_lua_pushlightuserdata(void *ctx, void *ptr) + * Description + * Pushes a light userdata onto the Lua stack. + * Userdata represent C values in Lua. + * A light userdata represents a pointer, a void*. + * It is a value (like a number): you do not create it, + * it has no individual metatable, and it is not collected + * (as it was never created). + * A light userdata is equal to "any" light userdata with + * the same C address. + * + * void bpf_lua_pushlstring(void *ctx, const char *s, size_t len) + * Description + * Pushes the string pointed to by s with size len onto the stack. + * Lua makes (or reuses) an internal copy of the given string, + * so the memory at s can be freed or reused immediately after the + * function returns. + * The string can contain any binary data, including embedded zeros. + * + * void bpf_lua_pushmap(void *ctx, void *map) + * Description + * Pushes a BPF map onto the Lua stack + * + * void bpf_lua_pushskb(void *ctx) + * Description + * Pushes an SKB structure onto the Lua stack + * + * void bpf_lua_pushstring(void *ctx, const char *s) + * Description + * Pushes the zero-terminated string pointed to by s onto the stack. + * Lua makes (or reuses) an internal copy of the given string, + * so the memory at s can be freed or reused immediately after the + * function returns. + * + * int bpf_lua_toboolean(void *ctx, int index) + * Description + * Converts the Lua value at the given index to a C + * boolean value (0 or 1) + * Return + * 1 if the value in the given index of the Lua stack is + * different from from false or null, otherwise returns 0 + * + * int bpf_lua_tointeger(void *ctx, int index) + * Description + * Converts the Lua value at the given index of the Lua stack + * to the signed integral type lua_Integer. + * Return + * The converted Lua value at the given index, if the value is + * convertible to an integer(see the Lua manual for more details + * on type conversion); otherwise returns 0 + * + * void bpf_lua_tostring(void *ctx, const char *str, u32 size, int index) + * Description + * Converts the Lua value at the given index of the Lua + * stack to a C string and copies size bytes of it to + * value pointed by str + * Return + * 1 if the value at the given index of the Lua stack is a + * string; otherwise it returns 0 + * + * int bpf_lua_type(void *ctx, int index) + * Description + * Obtains the type of the Lua value at the given index + * of the Lua stack + * + * Return + * Type of the value in the given valid index, + * or LUA_TNONE for a non-valid (but acceptable) index. + * The types returned by lua_type are coded by the + * following constants defined in lua.h: LUA_TNIL (0), + * LUA_TNUMBER, LUA_TBOOLEAN, LUA_TSTRING, LUA_TTABLE, + * LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, and + * LUA_TLIGHTUSERDATA. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -3010,7 +3110,24 @@ union bpf_attr { FN(probe_read_kernel_str), \ FN(tcp_send_ack), \ FN(send_signal_thread), \ - FN(jiffies64), + FN(jiffies64), \ + /* #ifdef CONFIG_XDP_LUA */ \ + FN(lua_dataref), \ + FN(lua_dataunref), \ + FN(lua_pcall), \ + 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_tostring), \ + 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 024af2d1d0af40..dfb1208e6942cd 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -984,6 +984,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 ef59c5c36cdb51..bd5a871e36b222 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1307,6 +1307,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 611872c0692693..4b7d3b75bdc196 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -300,3 +300,11 @@ obj-$(CONFIG_OBJAGG) += objagg.o # KUnit tests obj-$(CONFIG_LIST_KUNIT_TEST) += list-test.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..0574a8d89b8670 --- /dev/null +++ b/lib/lunatik @@ -0,0 +1 @@ +Subproject commit 0574a8d89b86709e132fbb3ea5814e247e74e083 diff --git a/net/core/Makefile b/net/core/Makefile index 3e2c378e5f3177..313adb01e8e22e 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 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 500bba8874b007..a0c7ea0dd15460 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -68,6 +68,14 @@ * - netif_rx() feedback */ +#ifdef CONFIG_XDP_LUA +#include +#include +#include +#include +#include +#endif /* CONFIG_XDP_LUA */ + #include #include #include @@ -152,6 +160,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; @@ -4512,6 +4523,9 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb, bool orig_bcast; 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. @@ -4556,9 +4570,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 */ + /* check if bpf_xdp_adjust_head was used */ off = xdp->data - orig_data; if (off) { @@ -5366,6 +5393,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; @@ -10487,6 +10543,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); @@ -10506,6 +10565,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 c180871e606d85..23bd78481fe84e 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -74,6 +74,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 @@ -5849,6 +5855,278 @@ static const struct bpf_func_proto bpf_tcp_gen_syncookie_proto = { #endif /* CONFIG_INET */ +#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_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_CONST_MAP_PTR, +}; + +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_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_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_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_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 || @@ -5925,6 +6203,38 @@ bpf_base_func_proto(enum bpf_func_id func_id) return bpf_get_trace_printk_proto(); case BPF_FUNC_jiffies64: return &bpf_jiffies64_proto; +#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_tointeger: + return &bpf_lua_tointeger_proto; + case BPF_FUNC_lua_newpacket: + return &bpf_lua_newpacket_proto; + case BPF_FUNC_lua_tostring: + return &bpf_lua_tostring_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 e1152f4ffe33ef..3f54d1f118fbea 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1876,6 +1876,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) @@ -2806,6 +2809,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 71af2febe72adf..0c77ed4f41fdd2 100644 --- a/net/xdp/Kconfig +++ b/net/xdp/Kconfig @@ -14,3 +14,10 @@ config XDP_SOCKETS_DIAG help Support for PF_XDP sockets monitoring interface used by the ss tool. If unsure, say Y. + +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 79b0fee6943bbe..b61166a4431769 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -53,6 +53,8 @@ tprogs-y += task_fd_query tprogs-y += xdp_sample_pkts tprogs-y += ibumad tprogs-y += hbm +# CONFIG_XDP_LUA +tprogs-y += xdplua # Libbpf dependencies LIBBPF = $(TOOLS_PATH)/lib/bpf/libbpf.a @@ -109,6 +111,8 @@ 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) +# CONFIG_XDP_LUA +xdplua-objs := xdplua_user.o # Tell kbuild to always build the programs always-y := $(tprogs-y) @@ -170,6 +174,9 @@ always-y += ibumad_kern.o always-y += hbm_out_kern.o always-y += hbm_edt_kern.o always-y += xdpsock_kern.o +# CONFIG_XDP_LUA +always-y += xdplua_map_kern.o +always-y += xdplua_cookie_kern.o ifeq ($(ARCH), arm) # Strip all except -D__LINUX_ARM_ARCH__ option needed to handle linux diff --git a/samples/bpf/checkcookie.lua b/samples/bpf/checkcookie.lua new file mode 100644 index 00000000000000..734e775dfdc696 --- /dev/null +++ b/samples/bpf/checkcookie.lua @@ -0,0 +1,33 @@ +-- +-- Copyright (C) 2020 ring-0 Ltda +-- +-- 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. +-- +cookies = {} + +local function ip2int(ip) + local oct1, oct2, oct3, oct4 = ip:match("(%d+)%.(%d+)%.(%d+)%.(%d+)") + return (oct4 << 24) + (oct3 << 16) + (oct2 << 8) + oct1 +end + +function loadcookie(ip, cookie) + cookies[ip2int(ip)] = cookie +end + +function checkcookie(pkt, ip) + local pattern = 'Cookie:%s*=__xdp=(%d+)%s*' + local cookiepkt = tonumber(string.match(tostring(pkt), pattern)) + + if not cookies[ip] then + return true + end + + return cookies[ip] == cookiepkt and true or false +end 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_cookie_kern.c b/samples/bpf/xdplua_cookie_kern.c new file mode 100644 index 00000000000000..c867192da7b55e --- /dev/null +++ b/samples/bpf/xdplua_cookie_kern.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2020 ring-0 Ltda + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#define HTTPDSTPORT 80 + +static int parse_tcp(struct xdp_md *ctx, void *data, uint64_t tp_off, + void *data_end, __be32 saddr) { + + struct tcphdr *tcp = data + tp_off; + char cookiefunc[] = "checkcookie"; + int data_ref; + + if (tcp + 1 > data_end) + return XDP_PASS; + + if (tcp->dest == htons(HTTPDSTPORT)) { + bool verdict; + data_ref = bpf_lua_dataref(ctx, tp_off + tcp->doff * 4); + if (data_ref < 0) + return XDP_PASS; + + bpf_lua_pushinteger(ctx, saddr); + + bpf_lua_pcall(ctx, cookiefunc, 2, 1); + + verdict = bpf_lua_toboolean(ctx, -1); + + return verdict ? XDP_PASS : XDP_DROP; + } + + return XDP_PASS; +} + +static inline u64 parse_ipv4(struct xdp_md *ctx, void *data, u64 nh_off, void *data_end, __be32 *saddr) +{ + struct iphdr *iph = data + nh_off; + + if (iph + 1 > data_end) + return -1; + + nh_off += iph->ihl * 4; + + *saddr = iph->saddr; + + return nh_off; +} + +SEC("xdplua_cookie") +int xdp_lua_cookie_prog(struct xdp_md *ctx) +{ + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + int rc = XDP_PASS; + struct ethhdr *eth = data; + u16 h_proto; + u64 nh_off; + u64 ip_off; + u_int8_t protonum; + int verdict; + __be32 saddr; + + nh_off = sizeof(*eth); + if (data + nh_off > data_end) + return rc; + + h_proto = eth->h_proto; + if (h_proto != htons(ETH_P_IP)) + return rc; + + ip_off = parse_ipv4(ctx, data, nh_off, data_end, &saddr); + if (ip_off == -1) + return rc; + + return parse_tcp(ctx, data, ip_off, data_end, saddr); +} + +char _license[] SEC("license") = "GPL"; diff --git a/samples/bpf/xdplua_map_kern.c b/samples/bpf/xdplua_map_kern.c new file mode 100644 index 00000000000000..d4acad373e8b4f --- /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 + +struct bpf_map_def SEC("maps") lua_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, &lua_test_map); + bpf_lua_pcall(ctx, updatename, 1, 0); + + bpf_lua_pushmap(ctx, &lua_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..fef4c53356fb0a --- /dev/null +++ b/samples/bpf/xdplua_user.c @@ -0,0 +1,242 @@ +/* + * 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 +#include "bpf/libbpf.h" + +#include "bpf_util.h" + +#define MAXSCRIPTLEN 8192 +#define MAXFILENAMELEN 256 + +static int ifindex = 0; + +static void usage(const char *prog) { + fprintf(stderr, "usage: %s [OPTS]\n" + "\nOPTS:\n" + " -d detach program\n" + " -f lua script path\n" + " -p eBPF program path\n" + " -i iface\n" + " -m monitor\n" + " -s lua script\n" + " -I Interval\n" + " -D Duration\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(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 %d\n", err); + + 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, int duration) { + unsigned int nr_cpus = bpf_num_possible_cpus(); + long cnts[nr_cpus]; + unsigned int key = 0; + + for (int i = 0; i < duration; i++) { + unsigned long cnt = 0; + int i; + bpf_map_lookup_elem(map_fd, &key, cnts); + for (i = 0; i < nr_cpus; ++i) { + cnt += cnts[i]; + } + printf("%lu\n", cnt); + sleep(interval); + } +} + +int main(int argc, char *argv[]) +{ + struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; + char lua_filename[MAXFILENAMELEN]; + char filename[MAXFILENAMELEN]; + char lua_script[MAXSCRIPTLEN]; + struct bpf_object *obj; + int opt, prog_fd; + int rx_cnt_map_fd; + int detach = 0, attach_lua = 0, attach_ebpf = 0, monitor = 0, attach_script = 0, + interval = 1, duration = 1; + + char *lua_prog = NULL; + const char *optstr = "f:p:i:dms:I:D:"; + struct bpf_prog_load_attr prog_load_attr = { + .prog_type = BPF_PROG_TYPE_XDP, + }; + + memset(lua_filename, 0, MAXFILENAMELEN); + memset(filename, 0, MAXFILENAMELEN); + while ((opt = getopt(argc, argv, optstr)) != -1) { + switch (opt) { + case 'f': + 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; + case 's': + snprintf(lua_script, sizeof(lua_script), + "%s", optarg); + attach_script = 1; + break; + case 'I': + interval = atoi(optarg); + break; + case 'D': + duration = atoi(optarg); + 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_prog) < 0) { + free(lua_prog); + return 1; + } + + free(lua_prog); + } + + if (attach_script) + if (do_attach_lua(lua_script) < 0) + return 1; + + if (monitor) { + rx_cnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rx_cnt"); + + poll(rx_cnt_map_fd, interval, duration); + } + return 0; +} diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 22f235260a3a35..ac4265e5d039e9 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -2890,6 +2890,111 @@ union bpf_attr { * Obtain the 64bit jiffies * Return * The 64 bit jiffies + * + * int bpf_lua_dataref(void *ctx, int offset) + * Description + * Create a new lua data buffer object pointing to the captured + * packet at the specified offset. The function leaves the new + * object on top of the Lua stack. + * Return + * Data object reference number on success, or -1 in case + * of failure + * + * void bpf_lua_dataunref(void *ctx, int data_ref) + * Description + * Releases the data-object reference, allowing it to be + * garbage-collected + * + * void bpf_lua_pcall(void *ctx, char *funcname, int num_args, int num_rets) + * Description + * Calls Lua function funcname with the given nargs arguments in protected mode + * + * void bpf_lua_pop(void *ctx, int n) + * Description + * Pops n elements from the Lua stack + * + * void bpf_lua_pushinteger(void *ctx, int num) + * Description + * Pushes an integer with value n onto the Lua stack. + * + * void bpf_lua_pushlightuserdata(void *ctx, void *ptr) + * Description + * Pushes a light userdata onto the Lua stack. + * Userdata represent C values in Lua. + * A light userdata represents a pointer, a void*. + * It is a value (like a number): you do not create it, + * it has no individual metatable, and it is not collected + * (as it was never created). + * A light userdata is equal to "any" light userdata with + * the same C address. + * + * void bpf_lua_pushlstring(void *ctx, const char *s, size_t len) + * Description + * Pushes the string pointed to by s with size len onto the stack. + * Lua makes (or reuses) an internal copy of the given string, + * so the memory at s can be freed or reused immediately after the + * function returns. + * The string can contain any binary data, including embedded zeros. + * + * void bpf_lua_pushmap(void *ctx, void *map) + * Description + * Pushes a BPF map onto the Lua stack + * + * void bpf_lua_pushskb(void *ctx) + * Description + * Pushes an SKB structure onto the Lua stack + * + * void bpf_lua_pushstring(void *ctx, const char *s) + * Description + * Pushes the zero-terminated string pointed to by s onto the stack. + * Lua makes (or reuses) an internal copy of the given string, + * so the memory at s can be freed or reused immediately after the + * function returns. + * + * int bpf_lua_toboolean(void *ctx, int index) + * Description + * Converts the Lua value at the given index to a C + * boolean value (0 or 1) + * Return + * 1 if the value in the given index of the Lua stack is + * different from from false or null, otherwise returns 0 + * + * int bpf_lua_tointeger(void *ctx, int index) + * Description + * Converts the Lua value at the given index of the Lua stack + * to the signed integral type lua_Integer. + * Return + * The converted Lua value at the given index, if the value is + * convertible to an integer(see the Lua manual for more details + * on type conversion); otherwise returns 0 + * + * void bpf_lua_newpacket(void *ctx, int offset) + * Description + * Create new luaunpack user data buffer pointing to + * the captured packet at the specified offset + * + * void bpf_lua_tostring(void *ctx, const char *str, u32 size, int index) + * Description + * Converts the Lua value at the given index of the Lua + * stack to a C string and copies size bytes of it to + * value pointed by str + * Return + * 1 if the value at the given index of the Lua stack is a + * string; otherwise it returns 0 + * + * int bpf_lua_type(void *ctx, int index) + * Description + * Obtains the type of the Lua value at the given index + * of the Lua stack + * + * Return + * Type of the value in the given valid index, + * or LUA_TNONE for a non-valid (but acceptable) index. + * The types returned by lua_type are coded by the + * following constants defined in lua.h: LUA_TNIL (0), + * LUA_TNUMBER, LUA_TBOOLEAN, LUA_TSTRING, LUA_TTABLE, + * LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, and + * LUA_TLIGHTUSERDATA. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -3010,7 +3115,24 @@ union bpf_attr { FN(probe_read_kernel_str), \ FN(tcp_send_ack), \ FN(send_signal_thread), \ - FN(jiffies64), + FN(jiffies64), \ + /* #ifdef CONFIG_XDP_LUA */ \ + FN(lua_dataref), \ + FN(lua_dataunref), \ + FN(lua_pcall), \ + 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_tostring), \ + 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 024af2d1d0af40..738280129e2601 100644 --- a/tools/include/uapi/linux/if_link.h +++ b/tools/include/uapi/linux/if_link.h @@ -984,6 +984,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/libbpf.h b/tools/lib/bpf/libbpf.h index 3fe12c9d1f9232..3c10f45f923a8c 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -439,6 +439,9 @@ 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_get_link_xdp_info(int ifindex, struct xdp_link_info *info, size_t info_size, __u32 flags); +/* #ifdef CONFIG_XDPLUA */ +LIBBPF_API int bpf_set_link_xdp_lua_prog(char *lua_prog); +/* #endif CONFIG_XDPLUA */ struct perf_buffer; diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index b035122142bb9f..931318b213b72c 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -105,6 +105,7 @@ LIBBPF_0.0.1 { bpf_prog_linfo__lfind; bpf_raw_tracepoint_open; bpf_set_link_xdp_fd; + bpf_set_link_xdp_lua_prog; bpf_task_fd_query; bpf_verify_program; btf__fd; diff --git a/tools/lib/bpf/netlink.c b/tools/lib/bpf/netlink.c index 431bd25c6cdb5a..f877eb676b5f5a 100644 --- a/tools/lib/bpf/netlink.c +++ b/tools/lib/bpf/netlink.c @@ -191,6 +191,62 @@ 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) { + 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 = 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; + } + ret = bpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL); + +cleanup: + close(sock); + return ret; +} +/* #endif CONFIG_XDPLUA */ + static int __dump_link_nlmsg(struct nlmsghdr *nlh, libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie) {