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/configure.ac b/configure.ac index 3d17293a94..4d18c7997a 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 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