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_network.h"
#include "bgpd/bgp_errors.h"
#include "bgpd/bgp_script.h"
#include "lib/routing_nb.h"
#include "bgpd/bgp_nb.h"
#include "bgpd/bgp_evpn_mh.h"
@ -506,6 +507,8 @@ int main(int argc, char **argv)
/* Initializations. */
bgp_vrf_init();
bgp_script_init();
hook_register(routing_conf_event,
routing_control_plane_protocols_name_validate);

View File

@ -65,6 +65,7 @@
#include "bgpd/bgp_flowspec_util.h"
#include "bgpd/bgp_encap_types.h"
#include "bgpd/bgp_mpath.h"
#include "bgpd/bgp_script.h"
#ifdef ENABLE_BGP_VNC
#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 {
/*
* Script function run failure. This will translate into a
* deny
* Script function run failure. This will translate into a deny
*/
LUA_RM_FAILURE = 0,
/*
@ -350,122 +350,123 @@ enum frrlua_rm_status {
*/
LUA_RM_NOMATCH,
/*
* Match was found but no changes were made to the
* incoming data.
* 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
* Match was found and data was modified, so figure out what changed
*/
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);
status = lua_pcall(L, 0, 1, 0);
if (status) {
zlog_debug("Executing Failure with function: %s: %d",
rule, status);
return LUA_RM_FAILURE;
struct frrscript *fs = frrscript_load(scriptname, NULL);
if (!fs) {
zlog_err("Issue loading script rule; defaulting to no match");
return RMAP_NOMATCH;
}
status = lua_tonumber(L, -1);
return status;
}
enum frrlua_rm_status status_failure = LUA_RM_FAILURE,
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;
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)
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) {
switch (*lrm_status) {
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;
case LUA_RM_NOMATCH:
zlog_debug("RM_NOMATCH");
status = RMAP_NOMATCH;
break;
case LUA_RM_MATCH_AND_CHANGE:
zlog_debug("MATCH AND CHANGE");
lua_getglobal(L, "nexthop");
path->attr->med = frrlua_table_get_integer(L, "metric");
/*
* This needs to be abstraced with the set function
*/
status = RMAP_MATCH;
zlog_debug("Updating attribute based on script's values");
uint32_t locpref = 0;
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))
locpref = path->attr->local_pref;
newlocpref = frrlua_table_get_integer(L, "localpref");
if (newlocpref != locpref) {
path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
path->attr->local_pref = newlocpref;
if (locpref != newattr->local_pref) {
SET_FLAG(path->attr->flag,
ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF));
path->attr->local_pref = newattr->local_pref;
}
status = RMAP_MATCH;
aspath_free(newattr->aspath);
XFREE(MTYPE_TMP, newattr);
break;
case LUA_RM_MATCH:
zlog_debug("MATCH ONLY");
status = RMAP_MATCH;
break;
}
lua_close(L);
XFREE(MTYPE_TMP, lrm_status);
frrscript_unload(fs);
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);
return command;
scriptname = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
return scriptname;
}
static void
route_match_command_free(void *rule)
static void route_match_script_free(void *rule)
{
XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
}
static const struct route_map_rule_cmd route_match_command_cmd = {
"command",
route_match_command,
route_match_command_compile,
route_match_command_free
static const struct route_map_rule_cmd route_match_script_cmd = {
"script",
route_match_script,
route_match_script_compile,
route_match_script_free
};
#endif
@ -4134,27 +4135,26 @@ DEFUN (no_match_peer,
}
#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",
DEFUN (match_script,
match_script_cmd,
"[no] match script WORD",
NO_STR
MATCH_STR
"Run a command to match\n"
"The command to run\n")
"Execute script to determine match\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,
RMAP_EVENT_FILTER_DELETED);
bool no = strmatch(argv[0]->text, "no");
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
@ -5671,7 +5671,7 @@ 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);
route_map_install_match(&route_match_script_cmd);
#endif
route_map_install_match(&route_match_ip_address_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, &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);
install_element(RMAP_NODE, &match_script_cmd);
#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_route.c \
bgpd/bgp_routemap.c \
bgpd/bgp_script.c \
bgpd/bgp_table.c \
bgpd/bgp_updgrp.c \
bgpd/bgp_updgrp_adv.c \
@ -174,6 +175,7 @@ noinst_HEADERS += \
bgpd/bgp_rd.h \
bgpd/bgp_regex.h \
bgpd/bgp_route.h \
bgpd/bgp_script.h \
bgpd/bgp_table.h \
bgpd/bgp_updgrp.h \
bgpd/bgp_vpn.h \