bgpd: update routemap scripting example

- Change from "match command <foo>" to "match script <script>"
- Use new scripting API

Signed-off-by: Quentin Young <qlyoung@nvidia.com>
This commit is contained in:
Quentin Young 2020-11-29 16:01:59 -05:00
parent 1a3a91e211
commit b4becb063f
5 changed files with 319 additions and 101 deletions

View File

@ -60,6 +60,7 @@
#include "bgpd/bgp_keepalives.h" #include "bgpd/bgp_keepalives.h"
#include "bgpd/bgp_network.h" #include "bgpd/bgp_network.h"
#include "bgpd/bgp_errors.h" #include "bgpd/bgp_errors.h"
#include "bgpd/bgp_script.h"
#include "lib/routing_nb.h" #include "lib/routing_nb.h"
#include "bgpd/bgp_nb.h" #include "bgpd/bgp_nb.h"
#include "bgpd/bgp_evpn_mh.h" #include "bgpd/bgp_evpn_mh.h"
@ -506,6 +507,8 @@ int main(int argc, char **argv)
/* Initializations. */ /* Initializations. */
bgp_vrf_init(); bgp_vrf_init();
bgp_script_init();
hook_register(routing_conf_event, hook_register(routing_conf_event,
routing_control_plane_protocols_name_validate); routing_control_plane_protocols_name_validate);

View File

@ -65,6 +65,7 @@
#include "bgpd/bgp_flowspec_util.h" #include "bgpd/bgp_flowspec_util.h"
#include "bgpd/bgp_encap_types.h" #include "bgpd/bgp_encap_types.h"
#include "bgpd/bgp_mpath.h" #include "bgpd/bgp_mpath.h"
#include "bgpd/bgp_script.h"
#ifdef ENABLE_BGP_VNC #ifdef ENABLE_BGP_VNC
#include "bgpd/rfapi/bgp_rfapi_cfg.h" #include "bgpd/rfapi/bgp_rfapi_cfg.h"
@ -341,8 +342,7 @@ static const struct route_map_rule_cmd route_match_peer_cmd = {
enum frrlua_rm_status { enum frrlua_rm_status {
/* /*
* Script function run failure. This will translate into a * Script function run failure. This will translate into a deny
* deny
*/ */
LUA_RM_FAILURE = 0, LUA_RM_FAILURE = 0,
/* /*
@ -350,122 +350,123 @@ enum frrlua_rm_status {
*/ */
LUA_RM_NOMATCH, LUA_RM_NOMATCH,
/* /*
* Match was found but no changes were made to the * Match was found but no changes were made to the incoming data.
* incoming data.
*/ */
LUA_RM_MATCH, LUA_RM_MATCH,
/* /*
* Match was found and data was modified, so * Match was found and data was modified, so figure out what changed
* figure out what changed
*/ */
LUA_RM_MATCH_AND_CHANGE, LUA_RM_MATCH_AND_CHANGE,
}; };
static enum frrlua_rm_status frrlua_run_rm_rule(lua_State *L, const char *rule) static enum route_map_cmd_result_t
route_match_script(void *rule, const struct prefix *prefix, void *object)
{ {
int status; const char *scriptname = rule;
struct bgp_path_info *path = (struct bgp_path_info *)object;
lua_getglobal(L, rule); struct frrscript *fs = frrscript_load(scriptname, NULL);
status = lua_pcall(L, 0, 1, 0);
if (status) { if (!fs) {
zlog_debug("Executing Failure with function: %s: %d", zlog_err("Issue loading script rule; defaulting to no match");
rule, status); return RMAP_NOMATCH;
return LUA_RM_FAILURE;
} }
status = lua_tonumber(L, -1); enum frrlua_rm_status status_failure = LUA_RM_FAILURE,
return status; status_nomatch = LUA_RM_NOMATCH,
} status_match = LUA_RM_MATCH,
status_match_and_change = LUA_RM_MATCH_AND_CHANGE;
/* Make result values available */
struct frrscript_env env[] = {
{"integer", "RM_FAILURE", &status_failure},
{"integer", "RM_NOMATCH", &status_nomatch},
{"integer", "RM_MATCH", &status_match},
{"integer", "RM_MATCH_AND_CHANGE", &status_match_and_change},
{"integer", "action", &status_failure},
{"prefix", "prefix", prefix},
{"attr", "attributes", path->attr},
{"peer", "peer", path->peer},
{}};
struct frrscript_env results[] = {
{"integer", "action"},
{"attr", "attributes"},
{},
};
int result = frrscript_call(fs, env);
if (result) {
zlog_err("Issue running script rule; defaulting to no match");
return RMAP_NOMATCH;
}
enum frrlua_rm_status *lrm_status =
frrscript_get_result(fs, &results[0]);
static enum route_map_cmd_result_t
route_match_command(void *rule, const struct prefix *prefix, void *object)
{
int status = RMAP_NOMATCH; int status = RMAP_NOMATCH;
u_int32_t locpref = 0;
u_int32_t newlocpref = 0;
enum frrlua_rm_status lrm_status;
struct bgp_path_info *path = (struct bgp_path_info *)object;
lua_State *L = luaL_newstate();;
luaL_openlibs(L);
if (L == NULL) switch (*lrm_status) {
return status;
/*
* Setup the prefix information to pass in
*/
lua_pushprefix(L, prefix);
/*
* Setup the bgp_path_info information
*/
lua_newtable(L);
lua_pushinteger(L, path->attr->med);
lua_setfield(L, -2, "metric");
lua_pushinteger(L, path->attr->nh_ifindex);
lua_setfield(L, -2, "ifindex");
lua_pushstring(L, path->attr->aspath->str);
lua_setfield(L, -2, "aspath");
lua_pushinteger(L, path->attr->local_pref);
lua_setfield(L, -2, "localpref");
zlog_debug("%s %d", path->attr->aspath->str, path->attr->nh_ifindex);
lua_setglobal(L, "nexthop");
zlog_debug("Set up nexthop information");
/*
* Run the rule
*/
lrm_status = frrlua_run_rm_rule(L, rule);
switch (lrm_status) {
case LUA_RM_FAILURE: case LUA_RM_FAILURE:
zlog_debug("RM_FAILURE"); zlog_err(
"Executing route-map match script '%s' failed; defaulting to no match",
scriptname);
status = RMAP_NOMATCH;
break; break;
case LUA_RM_NOMATCH: case LUA_RM_NOMATCH:
zlog_debug("RM_NOMATCH"); status = RMAP_NOMATCH;
break; break;
case LUA_RM_MATCH_AND_CHANGE: case LUA_RM_MATCH_AND_CHANGE:
zlog_debug("MATCH AND CHANGE"); status = RMAP_MATCH;
lua_getglobal(L, "nexthop"); zlog_debug("Updating attribute based on script's values");
path->attr->med = frrlua_table_get_integer(L, "metric");
/* uint32_t locpref = 0;
* This needs to be abstraced with the set function struct attr *newattr = frrscript_get_result(fs, &results[1]);
*/
path->attr->med = newattr->med;
if (path->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) if (path->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
locpref = path->attr->local_pref; locpref = path->attr->local_pref;
newlocpref = frrlua_table_get_integer(L, "localpref"); if (locpref != newattr->local_pref) {
if (newlocpref != locpref) { SET_FLAG(path->attr->flag,
path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF); ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF));
path->attr->local_pref = newlocpref; path->attr->local_pref = newattr->local_pref;
} }
status = RMAP_MATCH;
aspath_free(newattr->aspath);
XFREE(MTYPE_TMP, newattr);
break; break;
case LUA_RM_MATCH: case LUA_RM_MATCH:
zlog_debug("MATCH ONLY");
status = RMAP_MATCH; status = RMAP_MATCH;
break; break;
} }
lua_close(L);
XFREE(MTYPE_TMP, lrm_status);
frrscript_unload(fs);
return status; return status;
} }
static void *route_match_command_compile(const char *arg) static void *route_match_script_compile(const char *arg)
{ {
char *command; char *scriptname;
command = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); scriptname = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
return command;
return scriptname;
} }
static void static void route_match_script_free(void *rule)
route_match_command_free(void *rule)
{ {
XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
} }
static const struct route_map_rule_cmd route_match_command_cmd = { static const struct route_map_rule_cmd route_match_script_cmd = {
"command", "script",
route_match_command, route_match_script,
route_match_command_compile, route_match_script_compile,
route_match_command_free route_match_script_free
}; };
#endif #endif
@ -4134,27 +4135,26 @@ DEFUN (no_match_peer,
} }
#if defined(HAVE_LUA) #if defined(HAVE_LUA)
DEFUN (match_command, DEFUN (match_script,
match_command_cmd, match_script_cmd,
"match command WORD", "[no] match script 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 NO_STR
MATCH_STR MATCH_STR
"Run a command to match\n" "Execute script to determine match\n"
"The command to run\n") "The script name to run, without .lua; e.g. 'myroutemap' to run myroutemap.lua\n")
{ {
return bgp_route_match_delete(vty, "command", argv[3]->arg, bool no = strmatch(argv[0]->text, "no");
RMAP_EVENT_FILTER_DELETED); int i = 0;
argv_find(argv, argc, "WORD", &i);
const char *script = argv[i]->arg;
if (no) {
return bgp_route_match_delete(vty, "script", script,
RMAP_EVENT_FILTER_DELETED);
} else {
return bgp_route_match_add(vty, "script", script,
RMAP_EVENT_FILTER_ADDED);
}
} }
#endif #endif
@ -5671,7 +5671,7 @@ void bgp_route_map_init(void)
route_map_install_match(&route_match_peer_cmd); route_map_install_match(&route_match_peer_cmd);
route_map_install_match(&route_match_local_pref_cmd); route_map_install_match(&route_match_local_pref_cmd);
#if defined(HAVE_LUA) #if defined(HAVE_LUA)
route_map_install_match(&route_match_command_cmd); route_map_install_match(&route_match_script_cmd);
#endif #endif
route_map_install_match(&route_match_ip_address_cmd); 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_next_hop_cmd);
@ -5836,8 +5836,7 @@ void bgp_route_map_init(void)
install_element(RMAP_NODE, &set_ipv6_nexthop_peer_cmd); install_element(RMAP_NODE, &set_ipv6_nexthop_peer_cmd);
install_element(RMAP_NODE, &no_set_ipv6_nexthop_peer_cmd); install_element(RMAP_NODE, &no_set_ipv6_nexthop_peer_cmd);
#if defined(HAVE_LUA) #if defined(HAVE_LUA)
install_element(RMAP_NODE, &match_command_cmd); install_element(RMAP_NODE, &match_script_cmd);
install_element(RMAP_NODE, &no_match_command_cmd);
#endif #endif
} }

188
bgpd/bgp_script.c Normal file
View File

@ -0,0 +1,188 @@
/* BGP scripting foo
* Copyright (C) 2020 NVIDIA Corporation
* Quentin Young
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; see the file COPYING; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <zebra.h>
#include <lua.h>
#include "bgpd.h"
#include "bgp_script.h"
#include "bgp_debug.h"
#include "bgp_aspath.h"
#include "frratomic.h"
#include "frrscript.h"
static void lua_pushpeer(lua_State *L, const struct peer *peer)
{
lua_newtable(L);
lua_pushinteger(L, peer->as);
lua_setfield(L, -2, "remote_as");
lua_pushinteger(L, peer->local_as);
lua_setfield(L, -2, "local_as");
lua_pushinaddr(L, &peer->remote_id);
lua_setfield(L, -2, "remote_id");
lua_pushinaddr(L, &peer->local_id);
lua_setfield(L, -2, "local_id");
lua_pushstring(L, lookup_msg(bgp_status_msg, peer->status, NULL));
lua_setfield(L, -2, "state");
lua_pushstring(L, peer->desc ? peer->desc : "");
lua_setfield(L, -2, "description");
lua_pushtimet(L, &peer->uptime);
lua_setfield(L, -2, "uptime");
lua_pushtimet(L, &peer->readtime);
lua_setfield(L, -2, "last_readtime");
lua_pushtimet(L, &peer->resettime);
lua_setfield(L, -2, "last_resettime");
lua_pushsockunion(L, peer->su_local);
lua_setfield(L, -2, "local_address");
lua_pushsockunion(L, peer->su_remote);
lua_setfield(L, -2, "remote_address");
lua_pushinteger(L, peer->cap);
lua_setfield(L, -2, "capabilities");
lua_pushinteger(L, peer->flags);
lua_setfield(L, -2, "flags");
lua_pushstring(L, peer->password ? peer->password : "");
lua_setfield(L, -2, "password");
/* Nested tables here */
lua_newtable(L);
{
lua_newtable(L);
{
lua_pushinteger(L, peer->holdtime);
lua_setfield(L, -2, "hold");
lua_pushinteger(L, peer->keepalive);
lua_setfield(L, -2, "keepalive");
lua_pushinteger(L, peer->connect);
lua_setfield(L, -2, "connect");
lua_pushinteger(L, peer->routeadv);
lua_setfield(L, -2, "route_advertisement");
}
lua_setfield(L, -2, "configured");
lua_newtable(L);
{
lua_pushinteger(L, peer->v_holdtime);
lua_setfield(L, -2, "hold");
lua_pushinteger(L, peer->v_keepalive);
lua_setfield(L, -2, "keepalive");
lua_pushinteger(L, peer->v_connect);
lua_setfield(L, -2, "connect");
lua_pushinteger(L, peer->v_routeadv);
lua_setfield(L, -2, "route_advertisement");
}
lua_setfield(L, -2, "negotiated");
}
lua_setfield(L, -2, "timers");
lua_newtable(L);
{
lua_pushinteger(L, atomic_load_explicit(&peer->open_in,
memory_order_relaxed));
lua_setfield(L, -2, "open_in");
lua_pushinteger(L, atomic_load_explicit(&peer->open_out,
memory_order_relaxed));
lua_setfield(L, -2, "open_out");
lua_pushinteger(L, atomic_load_explicit(&peer->update_in,
memory_order_relaxed));
lua_setfield(L, -2, "update_in");
lua_pushinteger(L, atomic_load_explicit(&peer->update_out,
memory_order_relaxed));
lua_setfield(L, -2, "update_out");
lua_pushinteger(L, atomic_load_explicit(&peer->update_time,
memory_order_relaxed));
lua_setfield(L, -2, "update_time");
lua_pushinteger(L, atomic_load_explicit(&peer->keepalive_in,
memory_order_relaxed));
lua_setfield(L, -2, "keepalive_in");
lua_pushinteger(L, atomic_load_explicit(&peer->keepalive_out,
memory_order_relaxed));
lua_setfield(L, -2, "keepalive_out");
lua_pushinteger(L, atomic_load_explicit(&peer->notify_in,
memory_order_relaxed));
lua_setfield(L, -2, "notify_in");
lua_pushinteger(L, atomic_load_explicit(&peer->notify_out,
memory_order_relaxed));
lua_setfield(L, -2, "notify_out");
lua_pushinteger(L, atomic_load_explicit(&peer->refresh_in,
memory_order_relaxed));
lua_setfield(L, -2, "refresh_in");
lua_pushinteger(L, atomic_load_explicit(&peer->refresh_out,
memory_order_relaxed));
lua_setfield(L, -2, "refresh_out");
lua_pushinteger(L, atomic_load_explicit(&peer->dynamic_cap_in,
memory_order_relaxed));
lua_setfield(L, -2, "dynamic_cap_in");
lua_pushinteger(L, atomic_load_explicit(&peer->dynamic_cap_out,
memory_order_relaxed));
lua_setfield(L, -2, "dynamic_cap_out");
lua_pushinteger(L, peer->established);
lua_setfield(L, -2, "times_established");
lua_pushinteger(L, peer->dropped);
lua_setfield(L, -2, "times_dropped");
}
lua_setfield(L, -2, "stats");
}
static void lua_pushattr(lua_State *L, const struct attr *attr)
{
lua_newtable(L);
lua_pushinteger(L, attr->med);
lua_setfield(L, -2, "metric");
lua_pushinteger(L, attr->nh_ifindex);
lua_setfield(L, -2, "ifindex");
lua_pushstring(L, attr->aspath->str);
lua_setfield(L, -2, "aspath");
lua_pushinteger(L, attr->local_pref);
lua_setfield(L, -2, "localpref");
}
static void *lua_toattr(lua_State *L, int idx)
{
struct attr *attr = XCALLOC(MTYPE_TMP, sizeof(struct attr));
lua_getfield(L, -1, "metric");
attr->med = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_getfield(L, -1, "ifindex");
attr->nh_ifindex = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_getfield(L, -1, "aspath");
attr->aspath = aspath_str2aspath(lua_tostring(L, -1));
lua_pop(L, 1);
lua_getfield(L, -1, "localpref");
attr->local_pref = lua_tointeger(L, -1);
lua_pop(L, 1);
return attr;
}
struct frrscript_codec frrscript_codecs_bgpd[] = {
{.typename = "peer",
.encoder = (encoder_func)lua_pushpeer,
.decoder = NULL},
{.typename = "attr",
.encoder = (encoder_func)lua_pushattr,
.decoder = lua_toattr},
{}};
void bgp_script_init(void)
{
frrscript_register_type_codecs(frrscript_codecs_bgpd);
}

26
bgpd/bgp_script.h Normal file
View File

@ -0,0 +1,26 @@
/* BGP scripting foo
* Copyright (C) 2020 NVIDIA Corporation
* Quentin Young
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; see the file COPYING; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <zebra.h>
/*
* Initialize scripting stuff.
*/
void bgp_script_init(void);

View File

@ -95,6 +95,7 @@ bgpd_libbgp_a_SOURCES = \
bgpd/bgp_regex.c \ bgpd/bgp_regex.c \
bgpd/bgp_route.c \ bgpd/bgp_route.c \
bgpd/bgp_routemap.c \ bgpd/bgp_routemap.c \
bgpd/bgp_script.c \
bgpd/bgp_table.c \ bgpd/bgp_table.c \
bgpd/bgp_updgrp.c \ bgpd/bgp_updgrp.c \
bgpd/bgp_updgrp_adv.c \ bgpd/bgp_updgrp_adv.c \
@ -174,6 +175,7 @@ noinst_HEADERS += \
bgpd/bgp_rd.h \ bgpd/bgp_rd.h \
bgpd/bgp_regex.h \ bgpd/bgp_regex.h \
bgpd/bgp_route.h \ bgpd/bgp_route.h \
bgpd/bgp_script.h \
bgpd/bgp_table.h \ bgpd/bgp_table.h \
bgpd/bgp_updgrp.h \ bgpd/bgp_updgrp.h \
bgpd/bgp_vpn.h \ bgpd/bgp_vpn.h \