From 634949aef8ffbd504fe6337a4668de4b9b8b3941 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Wed, 25 Oct 2017 15:06:30 -0400 Subject: [PATCH 1/2] lua: Initial Lua import This code sets up the ability to use lua inside of FRR. Signed-off-by: Donald Sharp --- configure.ac | 12 +++++ lib/lua.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/lua.h | 79 ++++++++++++++++++++++++++++++ lib/subdir.am | 2 + 4 files changed, 223 insertions(+) create mode 100644 lib/lua.c create mode 100644 lib/lua.h diff --git a/configure.ac b/configure.ac index 32d5756a61..4b848594d9 100755 --- a/configure.ac +++ b/configure.ac @@ -203,7 +203,15 @@ elif test "x${enable_dev_build}" = "xyes"; then AC_C_FLAG([-g3]) AC_C_FLAG([-O0]) fi + if test "x${enable_lua}" = "xyes"; then + AC_CHECK_LIB([lua], [lua_newstate], + [LIBS="$LIBS -llua"]) + AC_DEFINE(HAVE_LUA,,Lua enabled for development) + fi else + if test "x${enable_lua}" = "xyes"; then + AC_MSG_ERROR([Lua is not meant to be built/used outside of development at this time]) + fi if test "z$orig_cflags" = "z"; then AC_C_FLAG([-g]) AC_C_FLAG([-Os], [ @@ -454,6 +462,9 @@ fi AC_ARG_ENABLE([dev_build], AS_HELP_STRING([--enable-dev-build], [build for development])) +AC_ARG_ENABLE([lua], + AS_HELP_STRING([--enable-lua], [Build Lua scripting])) + if test x"${enable_time_check}" != x"no" ; then if test x"${enable_time_check}" = x"yes" -o x"${enable_time_check}" = x ; then AC_DEFINE(CONSUMED_TIME_CHECK,5000000,Consumed Time Check) @@ -1126,6 +1137,7 @@ if test x"$LIBM" = x ; then AC_MSG_WARN([Unable to find working pow function - bgpd may not link]) fi LIBS="$TMPLIBS" + AC_SUBST(LIBM) AC_CHECK_FUNCS([ppoll], [ diff --git a/lib/lua.c b/lib/lua.c new file mode 100644 index 0000000000..3d701a9364 --- /dev/null +++ b/lib/lua.c @@ -0,0 +1,130 @@ +/* + * This file defines the lua interface into + * FRRouting. + * + * Copyright (C) 2016 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FreeRangeRouting (FRR). + * + * FRR 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; either version 2, or (at your option) any later version. + * + * FRR is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with FRR; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include + +#include + +#if defined(HAVE_LUA) +#include "prefix.h" +#include "lua.h" +#include "log.h" + +static int lua_zlog_debug(lua_State *L) +{ + int debug_lua = 1; + const char *string = lua_tostring(L, 1); + + if (debug_lua) + zlog_debug("%s", string); + + return 0; +} + +const char *get_string(lua_State *L, const char *key) +{ + const char *str; + + lua_pushstring(L, key); + lua_gettable(L, -2); + + str = (const char *)lua_tostring(L, -1); + lua_pop(L, 1); + + return str; +} + +int get_integer(lua_State *L, const char *key) +{ + int result; + + lua_pushstring(L, key); + lua_gettable(L, -2); + + result = lua_tointeger(L, -1); + lua_pop(L, 1); + + return result; +} + +static void *lua_alloc(void *ud, void *ptr, size_t osize, + size_t nsize) +{ + (void)ud; (void)osize; /* not used */ + if (nsize == 0) { + free(ptr); + return NULL; + } else + return realloc(ptr, nsize); +} + +lua_State *lua_initialize(const char *file) +{ + int status; + lua_State *L = lua_newstate(lua_alloc, NULL); + + zlog_debug("Newstate: %p", L); + luaL_openlibs(L); + zlog_debug("Opened lib"); + status = luaL_loadfile(L, file); + if (status) { + zlog_debug("Failure to open %s %d", file, status); + lua_close(L); + return NULL; + } + + lua_pcall(L, 0, LUA_MULTRET, 0); + zlog_debug("Setting global function"); + lua_pushcfunction(L, lua_zlog_debug); + lua_setglobal(L, "zlog_debug"); + + return L; +} + +void lua_setup_prefix_table(lua_State *L, const struct prefix *prefix) +{ + char buffer[100]; + + lua_newtable(L); + lua_pushstring(L, prefix2str(prefix, buffer, 100)); + lua_setfield(L, -2, "route"); + lua_pushinteger(L, prefix->family); + lua_setfield(L, -2, "family"); + lua_setglobal(L, "prefix"); +} + +enum lua_rm_status lua_run_rm_rule(lua_State *L, const char *rule) +{ + int status; + + lua_getglobal(L, rule); + status = lua_pcall(L, 0, 1, 0); + if (status) { + zlog_debug("Executing Failure with function: %s: %d", + rule, status); + return LUA_RM_FAILURE; + } + + status = lua_tonumber(L, -1); + return status; +} +#endif diff --git a/lib/lua.h b/lib/lua.h new file mode 100644 index 0000000000..8020a22711 --- /dev/null +++ b/lib/lua.h @@ -0,0 +1,79 @@ +/* + * This file defines the lua interface into + * FRRouting. + * + * Copyright (C) 2016 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FreeRangeRouting (FRR). + * + * FRR 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; either version 2, or (at your option) any later version. + * + * FRR is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with FRR; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef __LUA_H__ +#define __LUA_H__ + +#if defined(HAVE_LUA) + +#include +#include +#include + +/* + * These functions are helper functions that + * try to glom some of the lua_XXX functionality + * into what we actually need, instead of having + * to make multiple calls to set up what + * we want + */ +enum lua_rm_status { + /* + * Script function run failure. This will translate into a + * deny + */ + LUA_RM_FAILURE = 0, + /* + * No Match was found for the route map function + */ + LUA_RM_NOMATCH, + /* + * Match was found but no changes were made to the + * incoming data. + */ + LUA_RM_MATCH, + /* + * Match was found and data was modified, so + * figure out what changed + */ + LUA_RM_MATCH_AND_CHANGE, +}; + +/* + * Open up the lua.scr file and parse + * initial global values, if any. + */ +lua_State *lua_initialize(const char *file); + +void lua_setup_prefix_table(lua_State *L, const struct prefix *prefix); + +enum lua_rm_status lua_run_rm_rule(lua_State *L, const char *rule); + +/* + * Get particular string/integer information + * from a table. It is *assumed* that + * the table has already been selected + */ +const char *get_string(lua_State *L, const char *key); +int get_integer(lua_State *L, const char *key); +#endif +#endif diff --git a/lib/subdir.am b/lib/subdir.am index 499bb94920..6dc2fc529e 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -82,6 +82,7 @@ lib_libfrr_la_SOURCES = \ lib/workqueue.c \ lib/zclient.c \ lib/logicalrouter.c \ + lib/lua.c \ # end vtysh_scan += \ @@ -190,6 +191,7 @@ pkginclude_HEADERS += \ lib/zclient.h \ lib/zebra.h \ lib/logicalrouter.h \ + lib/lua.h \ lib/pbr.h \ # end From 1d7c7ace3c08fd1986143f37063114594fb91069 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 7 Nov 2017 09:14:32 -0500 Subject: [PATCH 2/2] bgpd: Add lua match command Please note this is a Proof of Concept and not actually something that is ready to commit at this point. The file tools/lua.scr contains some documentation on how we expect it to work currently. Additionally not all bgp values have been hooked up into the ability to lua script yet. There is still significant work to be done here: 1) Add the ability to pass in more data and to adjust the return values as appropriate. To set it up: 1) copy tools/lua.scr into /etc/frr (or whereever the config directory is ) 2) Create a route-map match command: ! router bgp 55 neighbor 10.50.11.116 remote-as external ! address-family ipv4 unicast neighbor 10.50.11.116 route-map TEST in exit-address-family ! route-map TEST permit 10 match command mooey ! 3) In the lua.scr file make sure that you have a function named 'mooey' ( as the above example does ): function mooey () zlog_debug(string.format("Family: %d: %s %d ifindex: %d aspath: %s localpref: %d", prefix.family, prefix.route, nexthop.metric, nexthop.ifindex, nexthop.aspath, nexthop.localpref)) nexthop.metric = 33 nexthop.localpref = 13 return 3 end This example script modifies the metric and localpref currently. I've also provided a zlog_debug function in lua to allow some simple debugging. Signed-off-by: Donald Sharp --- bgpd/bgp_routemap.c | 128 ++++++++++++++++++++++++++++++++++++++++++++ tools/lua.scr | 29 ++++++++++ 2 files changed, 157 insertions(+) create mode 100644 tools/lua.scr diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 0b4355805c..8e7c0a69dd 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -28,6 +28,7 @@ #include "plist.h" #include "memory.h" #include "log.h" +#include "lua.h" #ifdef HAVE_LIBPCREPOSIX #include #else @@ -330,6 +331,102 @@ struct route_map_rule_cmd route_match_peer_cmd = {"peer", route_match_peer, route_match_peer_compile, route_match_peer_free}; +#if defined(HAVE_LUA) +static route_map_result_t route_match_command(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) +{ + int status = RMAP_NOMATCH; + u_int32_t locpref = 0; + u_int32_t newlocpref = 0; + enum lua_rm_status lrm_status; + struct bgp_info *info = (struct bgp_info *)object; + lua_State *L = lua_initialize("/etc/frr/lua.scr"); + + if (L == NULL) + return status; + + /* + * Setup the prefix information to pass in + */ + lua_setup_prefix_table(L, prefix); + + zlog_debug("Set up prefix table"); + /* + * Setup the bgp_info information + */ + lua_newtable(L); + lua_pushinteger(L, info->attr->med); + lua_setfield(L, -2, "metric"); + lua_pushinteger(L, info->attr->nh_ifindex); + lua_setfield(L, -2, "ifindex"); + lua_pushstring(L, info->attr->aspath->str); + lua_setfield(L, -2, "aspath"); + lua_pushinteger(L, info->attr->local_pref); + lua_setfield(L, -2, "localpref"); + zlog_debug("%s %d", info->attr->aspath->str, info->attr->nh_ifindex); + lua_setglobal(L, "nexthop"); + + zlog_debug("Set up nexthop information"); + /* + * Run the rule + */ + lrm_status = lua_run_rm_rule(L, rule); + switch (lrm_status) { + case LUA_RM_FAILURE: + zlog_debug("RM_FAILURE"); + break; + case LUA_RM_NOMATCH: + zlog_debug("RM_NOMATCH"); + break; + case LUA_RM_MATCH_AND_CHANGE: + zlog_debug("MATCH AND CHANGE"); + lua_getglobal(L, "nexthop"); + info->attr->med = get_integer(L, "metric"); + /* + * This needs to be abstraced with the set function + */ + if (info->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) + locpref = info->attr->local_pref; + newlocpref = get_integer(L, "localpref"); + if (newlocpref != locpref) { + info->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF); + info->attr->local_pref = newlocpref; + } + status = RMAP_MATCH; + break; + case LUA_RM_MATCH: + zlog_debug("MATCH ONLY"); + status = RMAP_MATCH; + break; + } + lua_close(L); + return status; +} + +static void *route_match_command_compile(const char *arg) +{ + char *command; + + command = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); + return command; +} + +static void +route_match_command_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +struct route_map_rule_cmd route_match_command_cmd = { + "command", + route_match_command, + route_match_command_compile, + route_match_command_free +}; +#endif + /* `match ip address IP_ACCESS_LIST' */ /* Match function should return 1 if match is success else return @@ -3356,6 +3453,30 @@ DEFUN (no_match_peer, RMAP_EVENT_MATCH_DELETED); } +#if defined(HAVE_LUA) +DEFUN (match_command, + match_command_cmd, + "match command WORD", + MATCH_STR + "Run a command to match\n" + "The command to run\n") +{ + return bgp_route_match_add(vty, "command", argv[2]->arg, + RMAP_EVENT_FILTER_ADDED); +} + +DEFUN (no_match_command, + no_match_command_cmd, + "no match command WORD", + NO_STR + MATCH_STR + "Run a command to match\n" + "The command to run\n") +{ + return bgp_route_match_delete(vty, "command", argv[3]->arg, + RMAP_EVENT_FILTER_DELETED); +} +#endif /* match probability */ DEFUN (match_probability, @@ -4671,6 +4792,9 @@ void bgp_route_map_init(void) route_map_install_match(&route_match_peer_cmd); route_map_install_match(&route_match_local_pref_cmd); +#if defined(HAVE_LUA) + route_map_install_match(&route_match_command_cmd); +#endif route_map_install_match(&route_match_ip_address_cmd); route_map_install_match(&route_match_ip_next_hop_cmd); route_map_install_match(&route_match_ip_route_source_cmd); @@ -4804,6 +4928,10 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &no_set_ipv6_nexthop_prefer_global_cmd); install_element(RMAP_NODE, &set_ipv6_nexthop_peer_cmd); install_element(RMAP_NODE, &no_set_ipv6_nexthop_peer_cmd); +#if defined(HAVE_LUA) + install_element(RMAP_NODE, &match_command_cmd); + install_element(RMAP_NODE, &no_match_command_cmd); +#endif } void bgp_route_map_terminate(void) diff --git a/tools/lua.scr b/tools/lua.scr new file mode 100644 index 0000000000..db6bf7d36b --- /dev/null +++ b/tools/lua.scr @@ -0,0 +1,29 @@ +-- Route map functionality +-- +-- There are no parameters passed in. +-- Currently we set two global tables +-- prefix +-- .family = The v4 or v6 family we are working in +-- .route = the A.B.C.D/X or Z:A:B::D/X string +-- nexthop +-- .metric = The metric we are going to use +-- .ifindex = The outgoing interface +-- .aspath = The aspath of the route +-- .localpref = The localpref value +-- +-- Valid Return Codes: +-- 0 - Some sort of processing failure +-- This turns into a implicit DENY +-- 1 - No match was found, turns into a DENY +-- 2 - Match found, turns into a PERMIT +-- 3 - Match found and data structures changed, turns into a PERMIT +-- and a reread of the prefix and nexthop tables +function mooey () + zlog_debug(string.format("Family: %d: %s %d ifindex: %d aspath: %s localpref: %d", + prefix.family, prefix.route, + nexthop.metric, nexthop.ifindex, nexthop.aspath, nexthop.localpref)) + + nexthop.metric = 33 + nexthop.localpref = 13 + return 3 +end