mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-29 13:51:53 +00:00
commit
c0b6ef23f7
@ -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"
|
||||
@ -510,6 +511,10 @@ int main(int argc, char **argv)
|
||||
/* Initializations. */
|
||||
bgp_vrf_init();
|
||||
|
||||
#ifdef HAVE_SCRIPTING
|
||||
bgp_script_init();
|
||||
#endif
|
||||
|
||||
hook_register(routing_conf_event,
|
||||
routing_control_plane_protocols_name_validate);
|
||||
|
||||
|
@ -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"
|
||||
@ -337,99 +338,138 @@ static const struct route_map_rule_cmd route_match_peer_cmd = {
|
||||
route_match_peer_free
|
||||
};
|
||||
|
||||
#if defined(HAVE_LUA)
|
||||
#ifdef HAVE_SCRIPTING
|
||||
|
||||
enum frrlua_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,
|
||||
};
|
||||
|
||||
static enum route_map_cmd_result_t
|
||||
route_match_command(void *rule, const struct prefix *prefix, void *object)
|
||||
route_match_script(void *rule, const struct prefix *prefix, void *object)
|
||||
{
|
||||
int status = RMAP_NOMATCH;
|
||||
u_int32_t locpref = 0;
|
||||
u_int32_t newlocpref = 0;
|
||||
enum lua_rm_status lrm_status;
|
||||
const char *scriptname = rule;
|
||||
struct bgp_path_info *path = (struct bgp_path_info *)object;
|
||||
lua_State *L = lua_initialize("/etc/frr/lua.scr");
|
||||
|
||||
if (L == NULL)
|
||||
return status;
|
||||
struct frrscript *fs = frrscript_load(scriptname, NULL);
|
||||
|
||||
/*
|
||||
* Setup the prefix information to pass in
|
||||
*/
|
||||
lua_setup_prefix_table(L, prefix);
|
||||
if (!fs) {
|
||||
zlog_err("Issue loading script rule; defaulting to no match");
|
||||
return RMAP_NOMATCH;
|
||||
}
|
||||
|
||||
zlog_debug("Set up prefix table");
|
||||
/*
|
||||
* 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");
|
||||
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;
|
||||
|
||||
zlog_debug("Set up nexthop information");
|
||||
/*
|
||||
* Run the rule
|
||||
*/
|
||||
lrm_status = lua_run_rm_rule(L, rule);
|
||||
switch (lrm_status) {
|
||||
/* 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]);
|
||||
|
||||
int status = RMAP_NOMATCH;
|
||||
|
||||
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 = 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 = 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
|
||||
|
||||
#endif /* HAVE_SCRIPTING */
|
||||
|
||||
/* `match ip address IP_ACCESS_LIST' */
|
||||
|
||||
@ -4096,30 +4136,29 @@ 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",
|
||||
#ifdef HAVE_SCRIPTING
|
||||
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,
|
||||
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
|
||||
#endif /* HAVE_SCRIPTING */
|
||||
|
||||
/* match probability */
|
||||
DEFUN (match_probability,
|
||||
@ -5633,8 +5672,8 @@ 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);
|
||||
#ifdef HAVE_SCRIPTING
|
||||
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);
|
||||
@ -5798,9 +5837,8 @@ 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);
|
||||
#ifdef HAVE_SCRIPTING
|
||||
install_element(RMAP_NODE, &match_script_cmd);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
192
bgpd/bgp_script.c
Normal file
192
bgpd/bgp_script.c
Normal file
@ -0,0 +1,192 @@
|
||||
/* 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>
|
||||
|
||||
#ifdef HAVE_SCRIPTING
|
||||
|
||||
#include "bgpd.h"
|
||||
#include "bgp_script.h"
|
||||
#include "bgp_debug.h"
|
||||
#include "bgp_aspath.h"
|
||||
#include "frratomic.h"
|
||||
#include "frrscript.h"
|
||||
#include "frrlua.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);
|
||||
}
|
||||
|
||||
#endif /* HAVE_SCRIPTING */
|
34
bgpd/bgp_script.h
Normal file
34
bgpd/bgp_script.h
Normal file
@ -0,0 +1,34 @@
|
||||
/* 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
|
||||
*/
|
||||
#ifndef __BGP_SCRIPT__
|
||||
#define __BGP_SCRIPT__
|
||||
|
||||
#include <zebra.h>
|
||||
|
||||
#ifdef HAVE_SCRIPTING
|
||||
|
||||
/*
|
||||
* Initialize scripting stuff.
|
||||
*/
|
||||
void bgp_script_init(void);
|
||||
|
||||
#endif /* HAVE_SCRIPTING */
|
||||
|
||||
#endif /* __BGP_SCRIPT__ */
|
@ -96,6 +96,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 \
|
||||
@ -175,6 +176,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 \
|
||||
|
35
configure.ac
35
configure.ac
@ -138,6 +138,12 @@ AC_ARG_WITH([moduledir], [AS_HELP_STRING([--with-moduledir=DIR], [module directo
|
||||
])
|
||||
AC_SUBST([moduledir], [$moduledir])
|
||||
|
||||
AC_ARG_WITH([scriptdir], [AS_HELP_STRING([--with-scriptdir=DIR], [script directory (${sysconfdir}/scripts)])], [
|
||||
scriptdir="$withval"
|
||||
], [
|
||||
scriptdir="\${sysconfdir}/scripts"
|
||||
])
|
||||
AC_SUBST([scriptdir], [$scriptdir])
|
||||
|
||||
AC_ARG_WITH([yangmodelsdir], [AS_HELP_STRING([--with-yangmodelsdir=DIR], [yang models directory (${datarootdir}/yang)])], [
|
||||
yangmodelsdir="$withval"
|
||||
@ -274,24 +280,22 @@ if test "$enable_clang_coverage" = "yes"; then
|
||||
])
|
||||
fi
|
||||
|
||||
if test "$enable_scripting" = "yes"; then
|
||||
AX_PROG_LUA([5.3])
|
||||
AX_LUA_HEADERS
|
||||
AX_LUA_LIBS([
|
||||
AC_DEFINE([HAVE_SCRIPTING], [1], [Have support for scripting])
|
||||
LIBS="$LIBS $LUA_LIB"
|
||||
])
|
||||
fi
|
||||
|
||||
if test "$enable_dev_build" = "yes"; then
|
||||
AC_DEFINE([DEV_BUILD], [1], [Build for development])
|
||||
if test "$orig_cflags" = ""; then
|
||||
AC_C_FLAG([-g3])
|
||||
AC_C_FLAG([-O0])
|
||||
fi
|
||||
if test "$enable_lua" = "yes"; then
|
||||
AX_PROG_LUA([5.3])
|
||||
AX_LUA_HEADERS
|
||||
AX_LUA_LIBS([
|
||||
AC_DEFINE([HAVE_LUA], [1], [Have support for Lua interpreter])
|
||||
LIBS="$LIBS $LUA_LIB"
|
||||
])
|
||||
fi
|
||||
else
|
||||
if test "$enable_lua" = "yes"; then
|
||||
AC_MSG_ERROR([Lua is not meant to be built/used outside of development at this time])
|
||||
fi
|
||||
if test "$orig_cflags" = ""; then
|
||||
AC_C_FLAG([-g])
|
||||
AC_C_FLAG([-O2])
|
||||
@ -697,8 +701,8 @@ 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]))
|
||||
AC_ARG_ENABLE([scripting],
|
||||
AS_HELP_STRING([--enable-scripting], [Build with scripting support]))
|
||||
|
||||
AC_ARG_ENABLE([netlink-debug],
|
||||
AS_HELP_STRING([--disable-netlink-debug], [pretty print netlink debug messages]))
|
||||
@ -2446,19 +2450,23 @@ CFG_SBIN="$sbindir"
|
||||
CFG_STATE="$frr_statedir"
|
||||
CFG_MODULE="$moduledir"
|
||||
CFG_YANGMODELS="$yangmodelsdir"
|
||||
CFG_SCRIPT="$scriptdir"
|
||||
for I in 1 2 3 4 5 6 7 8 9 10; do
|
||||
eval CFG_SYSCONF="\"$CFG_SYSCONF\""
|
||||
eval CFG_SBIN="\"$CFG_SBIN\""
|
||||
eval CFG_STATE="\"$CFG_STATE\""
|
||||
eval CFG_MODULE="\"$CFG_MODULE\""
|
||||
eval CFG_YANGMODELS="\"$CFG_YANGMODELS\""
|
||||
eval CFG_SCRIPT="\"$CFG_SCRIPT\""
|
||||
done
|
||||
AC_SUBST([CFG_SYSCONF])
|
||||
AC_SUBST([CFG_SBIN])
|
||||
AC_SUBST([CFG_STATE])
|
||||
AC_SUBST([CFG_MODULE])
|
||||
AC_SUBST([CFG_SCRIPT])
|
||||
AC_SUBST([CFG_YANGMODELS])
|
||||
AC_DEFINE_UNQUOTED([MODULE_PATH], ["$CFG_MODULE"], [path to modules])
|
||||
AC_DEFINE_UNQUOTED([SCRIPT_PATH], ["$CFG_SCRIPT"], [path to scripts])
|
||||
AC_DEFINE_UNQUOTED([YANG_MODELS_PATH], ["$CFG_YANGMODELS"], [path to YANG data models])
|
||||
AC_DEFINE_UNQUOTED([WATCHFRR_SH_PATH], ["${CFG_SBIN%/}/watchfrr.sh"], [path to watchfrr.sh])
|
||||
|
||||
@ -2582,6 +2590,7 @@ state file directory : ${frr_statedir}
|
||||
config file directory : `eval echo \`echo ${sysconfdir}\``
|
||||
example directory : `eval echo \`echo ${exampledir}\``
|
||||
module directory : ${CFG_MODULE}
|
||||
script directory : ${CFG_SCRIPT}
|
||||
user to run as : ${enable_user}
|
||||
group to run as : ${enable_group}
|
||||
group for vty sockets : ${enable_vty_group}
|
||||
|
3
debian/control
vendored
3
debian/control
vendored
@ -29,7 +29,8 @@ Build-Depends: bison,
|
||||
python3-dev,
|
||||
python3-pytest <!nocheck>,
|
||||
python3-sphinx,
|
||||
texinfo (>= 4.7)
|
||||
texinfo (>= 4.7),
|
||||
liblua5.3-dev <pkg.frr.lua>
|
||||
Standards-Version: 4.5.0.3
|
||||
Homepage: https://www.frrouting.org/
|
||||
Vcs-Browser: https://github.com/FRRouting/frr/tree/debian/master
|
||||
|
7
debian/rules
vendored
7
debian/rules
vendored
@ -29,6 +29,12 @@ else
|
||||
CONF_SYSTEMD=--enable-systemd=no
|
||||
endif
|
||||
|
||||
ifeq ($(filter pkg.frr.lua,$(DEB_BUILD_PROFILES)),)
|
||||
CONF_LUA=--disable-scripting
|
||||
else
|
||||
CONF_LUA=--enable-scripting
|
||||
endif
|
||||
|
||||
export PYTHON=python3
|
||||
|
||||
%:
|
||||
@ -49,6 +55,7 @@ override_dh_auto_configure:
|
||||
\
|
||||
$(CONF_SYSTEMD) \
|
||||
$(CONF_RPKI) \
|
||||
$(CONF_LUA) \
|
||||
--with-libpam \
|
||||
--enable-doc \
|
||||
--enable-doc-html \
|
||||
|
@ -15,6 +15,6 @@ Library Facilities (libfrr)
|
||||
hooks
|
||||
cli
|
||||
modules
|
||||
lua
|
||||
scripting
|
||||
|
||||
|
||||
|
@ -1,65 +0,0 @@
|
||||
.. _lua:
|
||||
|
||||
Lua
|
||||
===
|
||||
|
||||
Lua is currently experimental within FRR and has very limited
|
||||
support. If you would like to compile FRR with Lua you must
|
||||
follow these steps:
|
||||
|
||||
1. Installation of Relevant Libraries
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
apt-get install lua5.3 liblua5-3 liblua5.3-dev
|
||||
|
||||
These are the Debian libraries that are needed. There should
|
||||
be equivalent RPM's that can be found
|
||||
|
||||
2. Compilation
|
||||
|
||||
Configure needs these options
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
./configure --enable-dev-build --enable-lua <all other interesting options>
|
||||
|
||||
Typically you just include the two new enable lines to build with it.
|
||||
|
||||
3. Using Lua
|
||||
|
||||
* Copy tools/lua.scr into /etc/frr
|
||||
|
||||
* Create a route-map match command
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
!
|
||||
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
|
||||
!
|
||||
|
||||
* In the lua.scr file make sure that you have a function named 'mooey'
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
function mooey ()
|
||||
zlog_debug(string.format("afi: %d: %s %d ifdx: %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
|
||||
|
||||
4. General Comments
|
||||
|
||||
Please be aware that this is extremely experimental and needs a ton of work
|
||||
to get this up into a state that is usable.
|
433
doc/developer/scripting.rst
Normal file
433
doc/developer/scripting.rst
Normal file
@ -0,0 +1,433 @@
|
||||
.. _scripting:
|
||||
|
||||
Scripting
|
||||
=========
|
||||
|
||||
.. seealso:: User docs for scripting
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
FRR has the ability to call Lua scripts to perform calculations, make
|
||||
decisions, or otherwise extend builtin behavior with arbitrary user code. This
|
||||
is implemented using the standard Lua C bindings. The supported version of Lua
|
||||
is 5.3.
|
||||
|
||||
C objects may be passed into Lua and Lua objects may be retrieved by C code via
|
||||
a marshalling system. In this way, arbitrary data from FRR may be passed to
|
||||
scripts. It is possible to pass C functions as well.
|
||||
|
||||
The Lua environment is isolated from the C environment; user scripts cannot
|
||||
access FRR's address space unless explicitly allowed by FRR.
|
||||
|
||||
For general information on how Lua is used to extend C, refer to Part IV of
|
||||
"Programming in Lua".
|
||||
|
||||
https://www.lua.org/pil/contents.html#24
|
||||
|
||||
|
||||
Design
|
||||
------
|
||||
|
||||
Why Lua
|
||||
^^^^^^^
|
||||
|
||||
Lua is designed to be embedded in C applications. It is very small; the
|
||||
standard library is 220K. It is relatively fast. It has a simple, minimal
|
||||
syntax that is relatively easy to learn and can be understood by someone with
|
||||
little to no programming experience. Moreover it is widely used to add
|
||||
scripting capabilities to applications. In short it is designed for this task.
|
||||
|
||||
Reasons against supporting multiple scripting languages:
|
||||
|
||||
- Each language would require different FFI methods, and specifically
|
||||
different object encoders; a lot of code
|
||||
- Languages have different capabilities that would have to be brought to
|
||||
parity with each other; a lot of work
|
||||
- Languages have vastly different performance characteristics; this would
|
||||
create alot of basically unfixable issues, and result in a single de facto
|
||||
standard scripting language (the fastest)
|
||||
- Each language would need a dedicated maintainer for the above reasons;
|
||||
this is pragmatically difficult
|
||||
- Supporting multiple languages fractures the community and limits the audience
|
||||
with which a given script can be shared
|
||||
|
||||
General
|
||||
^^^^^^^
|
||||
|
||||
FRR's concept of a script is somewhat abstracted away from the fact that it is
|
||||
Lua underneath. A script in has two things:
|
||||
|
||||
- name
|
||||
- state
|
||||
|
||||
In code:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct frrscript {
|
||||
/* Script name */
|
||||
char *name;
|
||||
|
||||
/* Lua state */
|
||||
struct lua_State *L;
|
||||
};
|
||||
|
||||
|
||||
``name`` is simply a string. Everything else is in ``state``, which is itself a
|
||||
Lua library object (``lua_State``). This is an opaque struct that is
|
||||
manipulated using ``lua_*`` functions. The basic ones are imported from
|
||||
``lua.h`` and the rest are implemented within FRR to fill our use cases. The
|
||||
thing to remember is that all operations beyond the initial loading the script
|
||||
take place on this opaque state object.
|
||||
|
||||
There are four basic actions that can be done on a script:
|
||||
|
||||
- load
|
||||
- execute
|
||||
- query state
|
||||
- unload
|
||||
|
||||
They are typically done in this order.
|
||||
|
||||
|
||||
Loading
|
||||
^^^^^^^
|
||||
|
||||
A snippet of Lua code is referred to as a "chunk". These are simply text. FRR
|
||||
presently assumes chunks are located in individual files specific to one task.
|
||||
These files are stored in the scripts directory and must end in ``.lua``.
|
||||
|
||||
A script object is created by loading a script. This is done with
|
||||
``frrscript_load()``. This function takes the name of the script and an
|
||||
optional callback function. The string ".lua" is appended to the script name,
|
||||
and the resultant filename is looked for in the scripts directory.
|
||||
|
||||
For example, to load ``/etc/frr/scripts/bingus.lua``:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct frrscript *fs = frrscript_load("bingus", NULL);
|
||||
|
||||
During loading the script is validated for syntax and its initial environment
|
||||
is setup. By default this does not include the Lua standard library; there are
|
||||
security issues to consider, though for practical purposes untrusted users
|
||||
should not be able to write the scripts directory anyway. If desired the Lua
|
||||
standard library may be added to the script environment using
|
||||
``luaL_openlibs(fs->L)`` after loading the script. Further information on
|
||||
setting up the script environment is in the Lua manual.
|
||||
|
||||
|
||||
Executing
|
||||
^^^^^^^^^
|
||||
|
||||
After loading, scripts may be executed. A script may take input in the form of
|
||||
variable bindings set in its environment prior to being run, and may provide
|
||||
results by setting the value of variables. Arbitrary C values may be
|
||||
transferred into the script environment, including functions.
|
||||
|
||||
A typical execution call looks something like this:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct frrscript *fs = frrscript_load(...);
|
||||
|
||||
int status_ok = 0, status_fail = 1;
|
||||
struct prefix p = ...;
|
||||
|
||||
struct frrscript_env env[] = {
|
||||
{"integer", "STATUS_FAIL", &status_fail},
|
||||
{"integer", "STATUS_OK", &status_ok},
|
||||
{"prefix", "myprefix", &p},
|
||||
{}};
|
||||
|
||||
int result = frrscript_call(fs, env);
|
||||
|
||||
|
||||
To execute a loaded script, we need to define the inputs. These inputs are
|
||||
passed by binding values to variable names that will be accessible within the
|
||||
Lua environment. Basically, all communication with the script takes place via
|
||||
global variables within the script, and to provide inputs we predefine globals
|
||||
before the script runs. This is done by passing ``frrscript_call()`` an array
|
||||
of ``struct frrscript_env``. Each struct has three fields. The first identifies
|
||||
the type of the value being passed; more on this later. The second defines the
|
||||
name of the global variable within the script environment to bind the third
|
||||
argument (the value) to.
|
||||
|
||||
The script is then executed and returns a general status code. In the success
|
||||
case this will be 0, otherwise it will be nonzero. The script itself does not
|
||||
determine this code, it is provided by the Lua interpreter.
|
||||
|
||||
|
||||
Querying State
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
When a chunk is executed, its state at exit is preserved and can be inspected.
|
||||
|
||||
After running a script, results may be retrieved by querying the script's
|
||||
state. Again this is done by retrieving the values of global variables, which
|
||||
are known to the script author to be "output" variables.
|
||||
|
||||
A result is retrieved like so:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct frrscript_env myresult = {"string", "myresult"};
|
||||
|
||||
char *myresult = frrscript_get_result(fs, &myresult);
|
||||
|
||||
... do something ...
|
||||
|
||||
XFREE(MTYPE_TMP, myresult);
|
||||
|
||||
|
||||
As with arguments, results are retrieved by providing a ``struct
|
||||
frrscript_env`` specifying a type and a global name. No value is necessary, nor
|
||||
is it modified by ``frrscript_get_result()``. That function simply extracts the
|
||||
requested value from the script state and returns it.
|
||||
|
||||
In most cases the returned value will be allocated with ``MTYPE_TMP`` and will
|
||||
need to be freed after use.
|
||||
|
||||
|
||||
Unloading
|
||||
^^^^^^^^^
|
||||
|
||||
To destroy a script and its associated state:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
frrscript_unload(fs);
|
||||
|
||||
Values returned by ``frrscript_get_result`` are still valid after the script
|
||||
they were retrieved from is unloaded.
|
||||
|
||||
Note that you must unload and then load the script if you want to reset its
|
||||
state, for example to run it again with different inputs. Otherwise the state
|
||||
from the previous run carries over into subsequent runs.
|
||||
|
||||
|
||||
.. _marshalling:
|
||||
|
||||
Marshalling
|
||||
^^^^^^^^^^^
|
||||
|
||||
Earlier sections glossed over the meaning of the type name field in ``struct
|
||||
frrscript_env`` and how data is passed between C and Lua. Lua, as a dynamically
|
||||
typed, garbage collected language, cannot directly use C values without some
|
||||
kind of marshalling / unmarshalling system to translate types between the two
|
||||
runtimes.
|
||||
|
||||
Lua communicates with C code using a stack. C code wishing to provide data to
|
||||
Lua scripts must provide a function that marshalls the C data into a Lua
|
||||
representation and pushes it on the stack. C code wishing to retrieve data from
|
||||
Lua must provide a corresponding unmarshalling function that retrieves a Lua
|
||||
value from the stack and converts it to the corresponding C type. These two
|
||||
functions, together with a chosen name of the type they operate on, are
|
||||
referred to as ``codecs`` in FRR.
|
||||
|
||||
A codec is defined as:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
typedef void (*encoder_func)(lua_State *, const void *);
|
||||
typedef void *(*decoder_func)(lua_State *, int);
|
||||
|
||||
struct frrscript_codec {
|
||||
const char *typename;
|
||||
encoder_func encoder;
|
||||
decoder_func decoder;
|
||||
};
|
||||
|
||||
A typename string and two function pointers.
|
||||
|
||||
``typename`` can be anything you want. For example, for the combined types of
|
||||
``struct prefix`` and its equivalent in Lua I have chosen the name ``prefix``.
|
||||
There is no restriction on naming here, it is just a human name used as a key
|
||||
and specified when passing and retrieving values.
|
||||
|
||||
``encoder`` is a function that takes a ``lua_State *`` and a C type and pushes
|
||||
onto the Lua stack a value representing the C type. For C structs, the usual
|
||||
case, this will typically be a Lua table (tables are the only datastructure Lua
|
||||
has). For example, here is the encoder function for ``struct prefix``:
|
||||
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void lua_pushprefix(lua_State *L, const struct prefix *prefix)
|
||||
{
|
||||
char buffer[PREFIX_STRLEN];
|
||||
|
||||
zlog_debug("frrlua: pushing prefix table");
|
||||
|
||||
lua_newtable(L);
|
||||
lua_pushstring(L, prefix2str(prefix, buffer, PREFIX_STRLEN));
|
||||
lua_setfield(L, -2, "network");
|
||||
lua_pushinteger(L, prefix->prefixlen);
|
||||
lua_setfield(L, -2, "length");
|
||||
lua_pushinteger(L, prefix->family);
|
||||
lua_setfield(L, -2, "family");
|
||||
}
|
||||
|
||||
This function pushes a single value onto the Lua stack. It is a table whose equivalent in Lua is:
|
||||
|
||||
.. code-block::
|
||||
|
||||
{ ["network"] = "1.2.3.4/24", ["prefixlen"] = 24, ["family"] = 2 }
|
||||
|
||||
|
||||
``decoder`` does the reverse; it takes a ``lua_State *`` and an index into the
|
||||
stack, and unmarshalls a Lua value there into the corresponding C type. Again
|
||||
for ``struct prefix``:
|
||||
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void *lua_toprefix(lua_State *L, int idx)
|
||||
{
|
||||
struct prefix *p = XCALLOC(MTYPE_TMP, sizeof(struct prefix));
|
||||
|
||||
lua_getfield(L, idx, "network");
|
||||
str2prefix(lua_tostring(L, -1), p);
|
||||
lua_pop(L, 1);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
By convention these functions should be called ``lua_to*``, as this is the
|
||||
naming convention used by the Lua C library for the basic types e.g.
|
||||
``lua_tointeger`` and ``lua_tostring``.
|
||||
|
||||
The returned data must always be copied off the stack and the copy must be
|
||||
allocated with ``MTYPE_TMP``. This way it is possible to unload the script
|
||||
(destroy the state) without invalidating any references to values stored in it.
|
||||
|
||||
To register a new type with its corresponding encoding functions:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct frrscript_codec frrscript_codecs_lib[] = {
|
||||
{.typename = "prefix",
|
||||
.encoder = (encoder_func)lua_pushprefix,
|
||||
.decoder = lua_toprefix},
|
||||
{.typename = "sockunion",
|
||||
.encoder = (encoder_func)lua_pushsockunion,
|
||||
.decoder = lua_tosockunion},
|
||||
...
|
||||
{}};
|
||||
|
||||
frrscript_register_type_codecs(frrscript_codecs_lib);
|
||||
|
||||
From this point on the type names are available to be used when calling any
|
||||
script and getting its results.
|
||||
|
||||
.. note::
|
||||
|
||||
Marshalled types are not restricted to simple values like integers, strings
|
||||
and tables. It is possible to marshall a type such that the resultant object
|
||||
in Lua is an actual object-oriented object, complete with methods that call
|
||||
back into defined C functions. See the Lua manual for how to do this; for a
|
||||
code example, look at how zlog is exported into the script environment.
|
||||
|
||||
|
||||
Script Environment
|
||||
------------------
|
||||
|
||||
Logging
|
||||
^^^^^^^
|
||||
|
||||
For convenience, script environments are populated by default with a ``log``
|
||||
object which contains methods corresponding to each of the ``zlog`` levels:
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
log.info("info")
|
||||
log.warn("warn")
|
||||
log.error("error")
|
||||
log.notice("notice")
|
||||
log.debug("debug")
|
||||
|
||||
The log messages will show up in the daemon's log output.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
For a complete code example involving passing custom types, retrieving results,
|
||||
and doing complex calculations in Lua, look at the implementation of the
|
||||
``match script SCRIPT`` command for BGP routemaps. This example calls into a
|
||||
script with a route prefix and attributes received from a peer and expects the
|
||||
script to return a match / no match / match and update result.
|
||||
|
||||
An example script to use with this follows. This script matches, does not match
|
||||
or updates a route depending on how many BGP UPDATE messages the peer has
|
||||
received when the script is called, simply as a demonstration of what can be
|
||||
accomplished with scripting.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
|
||||
-- Example route map matching
|
||||
-- author: qlyoung
|
||||
--
|
||||
-- The following variables are available to us:
|
||||
-- log
|
||||
-- logging library, with the usual functions
|
||||
-- prefix
|
||||
-- the route under consideration
|
||||
-- attributes
|
||||
-- the route's attributes
|
||||
-- peer
|
||||
-- the peer which received this route
|
||||
-- RM_FAILURE
|
||||
-- status code in case of failure
|
||||
-- RM_NOMATCH
|
||||
-- status code for no match
|
||||
-- RM_MATCH
|
||||
-- status code for match
|
||||
-- RM_MATCH_AND_CHANGE
|
||||
-- status code for match-and-set
|
||||
--
|
||||
-- We need to set the following out values:
|
||||
-- action
|
||||
-- Set to the appropriate status code to indicate what we did
|
||||
-- attributes
|
||||
-- Setting fields on here will propagate them back up to the caller if
|
||||
-- 'action' is set to RM_MATCH_AND_CHANGE.
|
||||
|
||||
|
||||
log.info("Evaluating route " .. prefix.network .. " from peer " .. peer.remote_id.string)
|
||||
|
||||
function on_match (prefix, attrs)
|
||||
log.info("Match")
|
||||
action = RM_MATCH
|
||||
end
|
||||
|
||||
function on_nomatch (prefix, attrs)
|
||||
log.info("No match")
|
||||
action = RM_NOMATCH
|
||||
end
|
||||
|
||||
function on_match_and_change (prefix, attrs)
|
||||
action = RM_MATCH_AND_CHANGE
|
||||
log.info("Match and change")
|
||||
attrs["metric"] = attrs["metric"] + 7
|
||||
end
|
||||
|
||||
special_routes = {
|
||||
["172.16.10.4/24"] = on_match,
|
||||
["172.16.13.1/8"] = on_nomatch,
|
||||
["192.168.0.24/8"] = on_match_and_change,
|
||||
}
|
||||
|
||||
|
||||
if special_routes[prefix.network] then
|
||||
special_routes[prefix.network](prefix, attributes)
|
||||
elseif peer.stats.update_in % 3 == 0 then
|
||||
on_match(prefix, attributes)
|
||||
elseif peer.stats.update_in % 2 == 0 then
|
||||
on_nomatch(prefix, attributes)
|
||||
else
|
||||
on_match_and_change(prefix, attributes)
|
||||
end
|
||||
|
@ -37,7 +37,6 @@ dev_RSTFILES = \
|
||||
doc/developer/lists.rst \
|
||||
doc/developer/locking.rst \
|
||||
doc/developer/logging.rst \
|
||||
doc/developer/lua.rst \
|
||||
doc/developer/memtypes.rst \
|
||||
doc/developer/modules.rst \
|
||||
doc/developer/next-hop-tracking.rst \
|
||||
@ -52,6 +51,7 @@ dev_RSTFILES = \
|
||||
doc/developer/path-internals.rst \
|
||||
doc/developer/path.rst \
|
||||
doc/developer/rcu.rst \
|
||||
doc/developer/scripting.rst \
|
||||
doc/developer/static-linking.rst \
|
||||
doc/developer/tracing.rst \
|
||||
doc/developer/testing.rst \
|
||||
|
@ -29,6 +29,7 @@ Basics
|
||||
ipv6
|
||||
kernel
|
||||
snmp
|
||||
scripting
|
||||
.. modules
|
||||
|
||||
#########
|
||||
|
@ -362,6 +362,10 @@ options from the list below.
|
||||
|
||||
Set hardcoded rpaths in the executable [default=yes].
|
||||
|
||||
.. option:: --enable-scripting
|
||||
|
||||
Enable Lua scripting [default=no].
|
||||
|
||||
You may specify any combination of the above options to the configure
|
||||
script. By default, the executables are placed in :file:`/usr/local/sbin`
|
||||
and the configuration files in :file:`/usr/local/etc`. The :file:`/usr/local/`
|
||||
@ -382,6 +386,10 @@ options to the configuration script.
|
||||
Configure zebra to use `dir` for local state files, such as pid files and
|
||||
unix sockets.
|
||||
|
||||
.. option:: --with-scriptdir <dir>
|
||||
|
||||
Look for Lua scripts in ``dir`` [``prefix``/etc/frr/scripts].
|
||||
|
||||
.. option:: --with-yangmodelsdir <dir>
|
||||
|
||||
Look for YANG modules in `dir` [`prefix`/share/yang]. Note that the FRR
|
||||
|
28
doc/user/scripting.rst
Normal file
28
doc/user/scripting.rst
Normal file
@ -0,0 +1,28 @@
|
||||
.. _scripting:
|
||||
|
||||
*********
|
||||
Scripting
|
||||
*********
|
||||
|
||||
The behavior of FRR may be extended or customized using its built-in scripting
|
||||
capabilities.
|
||||
|
||||
Some configuration commands accept the name of a Lua script to call to perform
|
||||
some task or make some decision. These scripts have their environments
|
||||
populated with some set of inputs, and are expected to populate some set of
|
||||
output variables, which are read by FRR after the script completes. The names
|
||||
and expected contents of these scripts are documented alongside the commands
|
||||
that support them.
|
||||
|
||||
These scripts live in :file:`/etc/frr/scripts/` by default. This is
|
||||
configurable at compile time via ``--with-scriptdir``. It may be
|
||||
overriden at runtime with the ``--scriptdir`` daemon option.
|
||||
|
||||
In order to use scripting, FRR must be built with ``--enable-scripting``.
|
||||
|
||||
.. note::
|
||||
|
||||
Scripts are typically loaded just-in-time. This means you can change the
|
||||
contents of a script that is in use without restarting FRR. Not all
|
||||
scripting locations may behave this way; refer to the documentation for the
|
||||
particular location.
|
@ -35,6 +35,7 @@ user_RSTFILES = \
|
||||
doc/user/routemap.rst \
|
||||
doc/user/routeserver.rst \
|
||||
doc/user/rpki.rst \
|
||||
doc/user/scripting.rst \
|
||||
doc/user/setup.rst \
|
||||
doc/user/sharp.rst \
|
||||
doc/user/snmp.rst \
|
||||
|
@ -49,6 +49,8 @@
|
||||
#include "northbound_cli.h"
|
||||
#include "network.h"
|
||||
|
||||
#include "frrscript.h"
|
||||
|
||||
DEFINE_MTYPE_STATIC(LIB, HOST, "Host config")
|
||||
DEFINE_MTYPE(LIB, COMPLETION, "Completion item")
|
||||
|
||||
@ -2303,6 +2305,30 @@ done:
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
|
||||
DEFUN(script,
|
||||
script_cmd,
|
||||
"script SCRIPT",
|
||||
"Test command - execute a script\n"
|
||||
"Script name (same as filename in /etc/frr/scripts/\n")
|
||||
{
|
||||
struct prefix p;
|
||||
str2prefix("1.2.3.4/24", &p);
|
||||
|
||||
struct frrscript *fs = frrscript_load(argv[1]->arg, NULL);
|
||||
|
||||
if (fs == NULL) {
|
||||
vty_out(vty, "Script '/etc/frr/scripts/%s.lua' not found\n",
|
||||
argv[1]->arg);
|
||||
} else {
|
||||
int ret = frrscript_call(fs, NULL);
|
||||
vty_out(vty, "Script result: %d\n", ret);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Set config filename. Called from vty.c */
|
||||
void host_config_set(const char *filename)
|
||||
{
|
||||
@ -2397,6 +2423,10 @@ void cmd_init(int terminal)
|
||||
install_element(VIEW_NODE, &echo_cmd);
|
||||
install_element(VIEW_NODE, &autocomplete_cmd);
|
||||
install_element(VIEW_NODE, &find_cmd);
|
||||
#if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
|
||||
install_element(VIEW_NODE, &script_cmd);
|
||||
#endif
|
||||
|
||||
|
||||
install_element(ENABLE_NODE, &config_end_cmd);
|
||||
install_element(ENABLE_NODE, &config_disable_cmd);
|
||||
|
@ -279,6 +279,29 @@ extern "C" {
|
||||
|
||||
#define array_size(ar) (sizeof(ar) / sizeof(ar[0]))
|
||||
|
||||
/* Some insane macros to count number of varargs to a functionlike macro */
|
||||
#define PP_ARG_N( \
|
||||
_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
|
||||
_11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \
|
||||
_21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \
|
||||
_31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \
|
||||
_41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \
|
||||
_51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \
|
||||
_61, _62, _63, N, ...) N
|
||||
|
||||
#define PP_RSEQ_N() \
|
||||
62, 61, 60, \
|
||||
59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \
|
||||
49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \
|
||||
39, 38, 37, 36, 35, 34, 33, 32, 31, 30, \
|
||||
29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \
|
||||
19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \
|
||||
9, 8, 7, 6, 5, 4, 3, 2, 1, 0
|
||||
|
||||
#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
|
||||
#define PP_NARG(...) PP_NARG_(_, ##__VA_ARGS__, PP_RSEQ_N())
|
||||
|
||||
|
||||
/* sigh. this is so ugly, it overflows and wraps to being nice again.
|
||||
*
|
||||
* printfrr() supports "%Ld" for <int64_t>, whatever that is typedef'd to.
|
||||
|
411
lib/frrlua.c
411
lib/frrlua.c
@ -2,57 +2,43 @@
|
||||
* This file defines the lua interface into
|
||||
* FRRouting.
|
||||
*
|
||||
* Copyright (C) 2016 Cumulus Networks, Inc.
|
||||
* Donald Sharp
|
||||
* Copyright (C) 2016-2019 Cumulus Networks, Inc.
|
||||
* Donald Sharp, Quentin Young
|
||||
*
|
||||
* This file is part of FRRouting (FRR).
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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 FRR; see the file COPYING. If not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
* 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>
|
||||
|
||||
#if defined(HAVE_LUA)
|
||||
#ifdef HAVE_SCRIPTING
|
||||
|
||||
#include "prefix.h"
|
||||
#include "frrlua.h"
|
||||
#include "log.h"
|
||||
#include "buffer.h"
|
||||
|
||||
static int lua_zlog_debug(lua_State *L)
|
||||
{
|
||||
int debug_lua = 1;
|
||||
const char *string = lua_tostring(L, 1);
|
||||
/* Lua stuff */
|
||||
|
||||
if (debug_lua)
|
||||
zlog_debug("%s", string);
|
||||
/*
|
||||
* FRR convenience functions.
|
||||
*
|
||||
* This section has convenience functions used to make interacting with the Lua
|
||||
* stack easier.
|
||||
*/
|
||||
|
||||
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 frrlua_table_get_integer(lua_State *L, const char *key)
|
||||
{
|
||||
int result;
|
||||
|
||||
@ -65,65 +51,316 @@ int get_integer(lua_State *L, const char *key)
|
||||
return result;
|
||||
}
|
||||
|
||||
static void *lua_alloc(void *ud, void *ptr, size_t osize,
|
||||
size_t nsize)
|
||||
/*
|
||||
* Encoders.
|
||||
*
|
||||
* This section has functions that convert internal FRR datatypes into Lua
|
||||
* datatypes.
|
||||
*/
|
||||
|
||||
void lua_pushprefix(lua_State *L, const struct prefix *prefix)
|
||||
{
|
||||
(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];
|
||||
char buffer[PREFIX_STRLEN];
|
||||
|
||||
lua_newtable(L);
|
||||
lua_pushstring(L, prefix2str(prefix, buffer, 100));
|
||||
lua_setfield(L, -2, "route");
|
||||
lua_pushstring(L, prefix2str(prefix, buffer, PREFIX_STRLEN));
|
||||
lua_setfield(L, -2, "network");
|
||||
lua_pushinteger(L, prefix->prefixlen);
|
||||
lua_setfield(L, -2, "length");
|
||||
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)
|
||||
void *lua_toprefix(lua_State *L, int idx)
|
||||
{
|
||||
int status;
|
||||
struct prefix *p = XCALLOC(MTYPE_TMP, sizeof(struct prefix));
|
||||
|
||||
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;
|
||||
lua_getfield(L, idx, "network");
|
||||
str2prefix(lua_tostring(L, -1), p);
|
||||
lua_pop(L, 1);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void lua_pushinterface(lua_State *L, const struct interface *ifp)
|
||||
{
|
||||
lua_newtable(L);
|
||||
lua_pushstring(L, ifp->name);
|
||||
lua_setfield(L, -2, "name");
|
||||
lua_pushinteger(L, ifp->ifindex);
|
||||
lua_setfield(L, -2, "ifindex");
|
||||
lua_pushinteger(L, ifp->status);
|
||||
lua_setfield(L, -2, "status");
|
||||
lua_pushinteger(L, ifp->flags);
|
||||
lua_setfield(L, -2, "flags");
|
||||
lua_pushinteger(L, ifp->metric);
|
||||
lua_setfield(L, -2, "metric");
|
||||
lua_pushinteger(L, ifp->speed);
|
||||
lua_setfield(L, -2, "speed");
|
||||
lua_pushinteger(L, ifp->mtu);
|
||||
lua_setfield(L, -2, "mtu");
|
||||
lua_pushinteger(L, ifp->mtu6);
|
||||
lua_setfield(L, -2, "mtu6");
|
||||
lua_pushinteger(L, ifp->bandwidth);
|
||||
lua_setfield(L, -2, "bandwidth");
|
||||
lua_pushinteger(L, ifp->link_ifindex);
|
||||
lua_setfield(L, -2, "link_ifindex");
|
||||
lua_pushinteger(L, ifp->ll_type);
|
||||
lua_setfield(L, -2, "linklayer_type");
|
||||
}
|
||||
|
||||
void *lua_tointerface(lua_State *L, int idx)
|
||||
{
|
||||
struct interface *ifp = XCALLOC(MTYPE_TMP, sizeof(struct interface));
|
||||
|
||||
lua_getfield(L, idx, "name");
|
||||
strlcpy(ifp->name, lua_tostring(L, -1), sizeof(ifp->name));
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, idx, "ifindex");
|
||||
ifp->ifindex = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, idx, "status");
|
||||
ifp->status = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, idx, "flags");
|
||||
ifp->flags = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, idx, "metric");
|
||||
ifp->metric = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, idx, "speed");
|
||||
ifp->speed = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, idx, "mtu");
|
||||
ifp->mtu = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, idx, "mtu6");
|
||||
ifp->mtu6 = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, idx, "bandwidth");
|
||||
ifp->bandwidth = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, idx, "link_ifindex");
|
||||
ifp->link_ifindex = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, idx, "linklayer_type");
|
||||
ifp->ll_type = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
return ifp;
|
||||
}
|
||||
|
||||
void lua_pushinaddr(lua_State *L, const struct in_addr *addr)
|
||||
{
|
||||
char buf[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, addr, buf, sizeof(buf));
|
||||
|
||||
lua_newtable(L);
|
||||
lua_pushinteger(L, addr->s_addr);
|
||||
lua_setfield(L, -2, "value");
|
||||
lua_pushstring(L, buf);
|
||||
lua_setfield(L, -2, "string");
|
||||
}
|
||||
|
||||
void *lua_toinaddr(lua_State *L, int idx)
|
||||
{
|
||||
struct in_addr *inaddr = XCALLOC(MTYPE_TMP, sizeof(struct in_addr));
|
||||
|
||||
lua_getfield(L, idx, "value");
|
||||
inaddr->s_addr = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
return inaddr;
|
||||
}
|
||||
|
||||
|
||||
void lua_pushin6addr(lua_State *L, const struct in6_addr *addr)
|
||||
{
|
||||
char buf[INET6_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET6, addr, buf, sizeof(buf));
|
||||
|
||||
lua_newtable(L);
|
||||
lua_pushlstring(L, (const char *)addr->s6_addr, 16);
|
||||
lua_setfield(L, -2, "value");
|
||||
lua_pushstring(L, buf);
|
||||
lua_setfield(L, -2, "string");
|
||||
}
|
||||
|
||||
void *lua_toin6addr(lua_State *L, int idx)
|
||||
{
|
||||
struct in6_addr *in6addr = XCALLOC(MTYPE_TMP, sizeof(struct in6_addr));
|
||||
|
||||
lua_getfield(L, idx, "string");
|
||||
inet_pton(AF_INET6, lua_tostring(L, -1), in6addr);
|
||||
lua_pop(L, 1);
|
||||
|
||||
return in6addr;
|
||||
}
|
||||
|
||||
void lua_pushsockunion(lua_State *L, const union sockunion *su)
|
||||
{
|
||||
char buf[SU_ADDRSTRLEN];
|
||||
sockunion2str(su, buf, sizeof(buf));
|
||||
|
||||
lua_newtable(L);
|
||||
lua_pushlstring(L, (const char *)sockunion_get_addr(su),
|
||||
sockunion_get_addrlen(su));
|
||||
lua_setfield(L, -2, "value");
|
||||
lua_pushstring(L, buf);
|
||||
lua_setfield(L, -2, "string");
|
||||
}
|
||||
|
||||
void *lua_tosockunion(lua_State *L, int idx)
|
||||
{
|
||||
union sockunion *su = XCALLOC(MTYPE_TMP, sizeof(union sockunion));
|
||||
|
||||
lua_getfield(L, idx, "string");
|
||||
str2sockunion(lua_tostring(L, -1), su);
|
||||
|
||||
return su;
|
||||
}
|
||||
|
||||
void lua_pushtimet(lua_State *L, const time_t *time)
|
||||
{
|
||||
lua_pushinteger(L, *time);
|
||||
}
|
||||
|
||||
void *lua_totimet(lua_State *L, int idx)
|
||||
{
|
||||
time_t *t = XCALLOC(MTYPE_TMP, sizeof(time_t));
|
||||
|
||||
*t = lua_tointeger(L, idx);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
void lua_pushintegerp(lua_State *L, const long long *num)
|
||||
{
|
||||
lua_pushinteger(L, *num);
|
||||
}
|
||||
|
||||
void *lua_tointegerp(lua_State *L, int idx)
|
||||
{
|
||||
int isnum;
|
||||
long long *num = XCALLOC(MTYPE_TMP, sizeof(long long));
|
||||
|
||||
*num = lua_tonumberx(L, idx, &isnum);
|
||||
assert(isnum);
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
void *lua_tostringp(lua_State *L, int idx)
|
||||
{
|
||||
char *string = XSTRDUP(MTYPE_TMP, lua_tostring(L, idx));
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
/*
|
||||
* Logging.
|
||||
*
|
||||
* Lua-compatible wrappers for FRR logging functions.
|
||||
*/
|
||||
static const char *frrlua_log_thunk(lua_State *L)
|
||||
{
|
||||
int nargs;
|
||||
|
||||
nargs = lua_gettop(L);
|
||||
assert(nargs == 1);
|
||||
|
||||
return lua_tostring(L, 1);
|
||||
}
|
||||
|
||||
static int frrlua_log_debug(lua_State *L)
|
||||
{
|
||||
zlog_debug("%s", frrlua_log_thunk(L));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int frrlua_log_info(lua_State *L)
|
||||
{
|
||||
zlog_info("%s", frrlua_log_thunk(L));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int frrlua_log_notice(lua_State *L)
|
||||
{
|
||||
zlog_notice("%s", frrlua_log_thunk(L));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int frrlua_log_warn(lua_State *L)
|
||||
{
|
||||
zlog_warn("%s", frrlua_log_thunk(L));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int frrlua_log_error(lua_State *L)
|
||||
{
|
||||
zlog_err("%s", frrlua_log_thunk(L));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const luaL_Reg log_funcs[] = {
|
||||
{"debug", frrlua_log_debug},
|
||||
{"info", frrlua_log_info},
|
||||
{"notice", frrlua_log_notice},
|
||||
{"warn", frrlua_log_warn},
|
||||
{"error", frrlua_log_error},
|
||||
{},
|
||||
};
|
||||
|
||||
void frrlua_export_logging(lua_State *L)
|
||||
{
|
||||
lua_newtable(L);
|
||||
luaL_setfuncs(L, log_funcs, 0);
|
||||
lua_setglobal(L, "log");
|
||||
}
|
||||
|
||||
/*
|
||||
* Debugging.
|
||||
*/
|
||||
|
||||
char *frrlua_stackdump(lua_State *L)
|
||||
{
|
||||
int top = lua_gettop(L);
|
||||
|
||||
char tmpbuf[64];
|
||||
struct buffer *buf = buffer_new(4098);
|
||||
|
||||
for (int i = 1; i <= top; i++) {
|
||||
int t = lua_type(L, i);
|
||||
|
||||
switch (t) {
|
||||
case LUA_TSTRING: /* strings */
|
||||
snprintf(tmpbuf, sizeof(tmpbuf), "\"%s\"\n",
|
||||
lua_tostring(L, i));
|
||||
buffer_putstr(buf, tmpbuf);
|
||||
break;
|
||||
case LUA_TBOOLEAN: /* booleans */
|
||||
snprintf(tmpbuf, sizeof(tmpbuf), "%s\n",
|
||||
lua_toboolean(L, i) ? "true" : "false");
|
||||
buffer_putstr(buf, tmpbuf);
|
||||
break;
|
||||
case LUA_TNUMBER: /* numbers */
|
||||
snprintf(tmpbuf, sizeof(tmpbuf), "%g\n",
|
||||
lua_tonumber(L, i));
|
||||
buffer_putstr(buf, tmpbuf);
|
||||
break;
|
||||
default: /* other values */
|
||||
snprintf(tmpbuf, sizeof(tmpbuf), "%s\n",
|
||||
lua_typename(L, t));
|
||||
buffer_putstr(buf, tmpbuf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
status = lua_tonumber(L, -1);
|
||||
return status;
|
||||
char *result = XSTRDUP(MTYPE_TMP, buffer_getstr(buf));
|
||||
|
||||
buffer_free(buf);
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAVE_SCRIPTING */
|
||||
|
209
lib/frrlua.h
209
lib/frrlua.h
@ -1,88 +1,173 @@
|
||||
/*
|
||||
* This file defines the lua interface into
|
||||
* FRRouting.
|
||||
* Copyright (C) 2016-2019 Cumulus Networks, Inc.
|
||||
* Donald Sharp, Quentin Young
|
||||
*
|
||||
* Copyright (C) 2016 Cumulus Networks, Inc.
|
||||
* Donald Sharp
|
||||
* 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 file is part of FRRouting (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.
|
||||
* 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 FRR; see the file COPYING. If not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
* 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
|
||||
*/
|
||||
#ifndef __LUA_H__
|
||||
#define __LUA_H__
|
||||
#ifndef __FRRLUA_H__
|
||||
#define __FRRLUA_H__
|
||||
|
||||
#if defined(HAVE_LUA)
|
||||
#include <zebra.h>
|
||||
|
||||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
#include "lauxlib.h"
|
||||
#ifdef HAVE_SCRIPTING
|
||||
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
#include "prefix.h"
|
||||
#include "frrscript.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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
|
||||
* Converts a prefix to a Lua value and pushes it on the stack.
|
||||
*/
|
||||
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,
|
||||
};
|
||||
void lua_pushprefix(lua_State *L, const struct prefix *prefix);
|
||||
|
||||
/*
|
||||
* Open up the lua.scr file and parse
|
||||
* initial global values, if any.
|
||||
* Converts the Lua value at idx to a prefix.
|
||||
*
|
||||
* Returns:
|
||||
* struct prefix allocated with MTYPE_TMP
|
||||
*/
|
||||
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);
|
||||
void *lua_toprefix(lua_State *L, int idx);
|
||||
|
||||
/*
|
||||
* Get particular string/integer information
|
||||
* from a table. It is *assumed* that
|
||||
* the table has already been selected
|
||||
* Converts an interface to a Lua value and pushes it on the stack.
|
||||
*/
|
||||
const char *get_string(lua_State *L, const char *key);
|
||||
int get_integer(lua_State *L, const char *key);
|
||||
void lua_pushinterface(lua_State *L, const struct interface *ifp);
|
||||
|
||||
/*
|
||||
* Converts the Lua value at idx to an interface.
|
||||
*
|
||||
* Returns:
|
||||
* struct interface allocated with MTYPE_TMP. This interface is not hooked
|
||||
* to anything, nor is it inserted in the global interface tree.
|
||||
*/
|
||||
void *lua_tointerface(lua_State *L, int idx);
|
||||
|
||||
/*
|
||||
* Converts an in_addr to a Lua value and pushes it on the stack.
|
||||
*/
|
||||
void lua_pushinaddr(lua_State *L, const struct in_addr *addr);
|
||||
|
||||
/*
|
||||
* Converts the Lua value at idx to an in_addr.
|
||||
*
|
||||
* Returns:
|
||||
* struct in_addr allocated with MTYPE_TMP.
|
||||
*/
|
||||
void *lua_toinaddr(lua_State *L, int idx);
|
||||
|
||||
/*
|
||||
* Converts an in6_addr to a Lua value and pushes it on the stack.
|
||||
*/
|
||||
void lua_pushin6addr(lua_State *L, const struct in6_addr *addr);
|
||||
|
||||
/*
|
||||
* Converts the Lua value at idx to an in6_addr.
|
||||
*
|
||||
* Returns:
|
||||
* struct in6_addr allocated with MTYPE_TMP.
|
||||
*/
|
||||
void *lua_toin6addr(lua_State *L, int idx);
|
||||
|
||||
/*
|
||||
* Converts a time_t to a Lua value and pushes it on the stack.
|
||||
*/
|
||||
void lua_pushtimet(lua_State *L, const time_t *time);
|
||||
|
||||
/*
|
||||
* Converts the Lua value at idx to a time_t.
|
||||
*
|
||||
* Returns:
|
||||
* time_t allocated with MTYPE_TMP.
|
||||
*/
|
||||
void *lua_totimet(lua_State *L, int idx);
|
||||
|
||||
/*
|
||||
* Converts a sockunion to a Lua value and pushes it on the stack.
|
||||
*/
|
||||
void lua_pushsockunion(lua_State *L, const union sockunion *su);
|
||||
|
||||
/*
|
||||
* Converts the Lua value at idx to a sockunion.
|
||||
*
|
||||
* Returns:
|
||||
* sockunion allocated with MTYPE_TMP.
|
||||
*/
|
||||
void *lua_tosockunion(lua_State *L, int idx);
|
||||
|
||||
/*
|
||||
* Converts an int to a Lua value and pushes it on the stack.
|
||||
*/
|
||||
void lua_pushintegerp(lua_State *L, const long long *num);
|
||||
|
||||
/*
|
||||
* Converts the Lua value at idx to an int.
|
||||
*
|
||||
* Returns:
|
||||
* int allocated with MTYPE_TMP.
|
||||
*/
|
||||
void *lua_tointegerp(lua_State *L, int idx);
|
||||
|
||||
/*
|
||||
* Pop string.
|
||||
*
|
||||
* Sets *string to a copy of the string at the top of the stack. The copy is
|
||||
* allocated with MTYPE_TMP and the caller is responsible for freeing it.
|
||||
*/
|
||||
void *lua_tostringp(lua_State *L, int idx);
|
||||
|
||||
/*
|
||||
* Retrieve an integer from table on the top of the stack.
|
||||
*
|
||||
* key
|
||||
* Key of string value in table
|
||||
*/
|
||||
int frrlua_table_get_integer(lua_State *L, const char *key);
|
||||
|
||||
/*
|
||||
* Exports a new table containing bindings to FRR zlog functions into the
|
||||
* global namespace.
|
||||
*
|
||||
* From Lua, these functions may be accessed as:
|
||||
*
|
||||
* - log.debug()
|
||||
* - log.info()
|
||||
* - log.warn()
|
||||
* - log.error()
|
||||
*
|
||||
* They take a single string argument.
|
||||
*/
|
||||
void frrlua_export_logging(lua_State *L);
|
||||
|
||||
/*
|
||||
* Dump Lua stack to a string.
|
||||
*
|
||||
* Return value must be freed with XFREE(MTYPE_TMP, ...);
|
||||
*/
|
||||
char *frrlua_stackdump(lua_State *L);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
#endif /* HAVE_SCRIPTING */
|
||||
|
||||
#endif /* __FRRLUA_H__ */
|
||||
|
272
lib/frrscript.c
Normal file
272
lib/frrscript.c
Normal file
@ -0,0 +1,272 @@
|
||||
/* 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>
|
||||
|
||||
#ifdef HAVE_SCRIPTING
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <lua.h>
|
||||
|
||||
#include "frrscript.h"
|
||||
#include "frrlua.h"
|
||||
#include "memory.h"
|
||||
#include "hash.h"
|
||||
#include "log.h"
|
||||
|
||||
|
||||
DEFINE_MTYPE_STATIC(LIB, SCRIPT, "Scripting");
|
||||
|
||||
/* Codecs */
|
||||
|
||||
struct frrscript_codec frrscript_codecs_lib[] = {
|
||||
{.typename = "integer",
|
||||
.encoder = (encoder_func)lua_pushintegerp,
|
||||
.decoder = lua_tointegerp},
|
||||
{.typename = "string",
|
||||
.encoder = (encoder_func)lua_pushstring,
|
||||
.decoder = lua_tostringp},
|
||||
{.typename = "prefix",
|
||||
.encoder = (encoder_func)lua_pushprefix,
|
||||
.decoder = lua_toprefix},
|
||||
{.typename = "interface",
|
||||
.encoder = (encoder_func)lua_pushinterface,
|
||||
.decoder = lua_tointerface},
|
||||
{.typename = "in_addr",
|
||||
.encoder = (encoder_func)lua_pushinaddr,
|
||||
.decoder = lua_toinaddr},
|
||||
{.typename = "in6_addr",
|
||||
.encoder = (encoder_func)lua_pushin6addr,
|
||||
.decoder = lua_toin6addr},
|
||||
{.typename = "sockunion",
|
||||
.encoder = (encoder_func)lua_pushsockunion,
|
||||
.decoder = lua_tosockunion},
|
||||
{.typename = "time_t",
|
||||
.encoder = (encoder_func)lua_pushtimet,
|
||||
.decoder = lua_totimet},
|
||||
{}};
|
||||
|
||||
/* Type codecs */
|
||||
|
||||
struct hash *codec_hash;
|
||||
char scriptdir[MAXPATHLEN];
|
||||
|
||||
static unsigned int codec_hash_key(const void *data)
|
||||
{
|
||||
const struct frrscript_codec *c = data;
|
||||
|
||||
return string_hash_make(c->typename);
|
||||
}
|
||||
|
||||
static bool codec_hash_cmp(const void *d1, const void *d2)
|
||||
{
|
||||
const struct frrscript_codec *e1 = d1;
|
||||
const struct frrscript_codec *e2 = d2;
|
||||
|
||||
return strmatch(e1->typename, e2->typename);
|
||||
}
|
||||
|
||||
static void *codec_alloc(void *arg)
|
||||
{
|
||||
struct frrscript_codec *tmp = arg;
|
||||
|
||||
struct frrscript_codec *e =
|
||||
XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript_codec));
|
||||
e->typename = XSTRDUP(MTYPE_SCRIPT, tmp->typename);
|
||||
e->encoder = tmp->encoder;
|
||||
e->decoder = tmp->decoder;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void codec_free(struct codec *c)
|
||||
{
|
||||
XFREE(MTYPE_TMP, c->typename);
|
||||
XFREE(MTYPE_TMP, c);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Generic script APIs */
|
||||
|
||||
int frrscript_call(struct frrscript *fs, struct frrscript_env *env)
|
||||
{
|
||||
struct frrscript_codec c = {};
|
||||
const void *arg;
|
||||
const char *bindname;
|
||||
|
||||
/* Encode script arguments */
|
||||
for (int i = 0; env && env[i].val != NULL; i++) {
|
||||
bindname = env[i].name;
|
||||
c.typename = env[i].typename;
|
||||
arg = env[i].val;
|
||||
|
||||
struct frrscript_codec *codec = hash_lookup(codec_hash, &c);
|
||||
assert(codec && "No encoder for type");
|
||||
codec->encoder(fs->L, arg);
|
||||
|
||||
lua_setglobal(fs->L, bindname);
|
||||
}
|
||||
|
||||
int ret = lua_pcall(fs->L, 0, 0, 0);
|
||||
|
||||
switch (ret) {
|
||||
case LUA_OK:
|
||||
break;
|
||||
case LUA_ERRRUN:
|
||||
zlog_err("Script '%s' runtime error: %s", fs->name,
|
||||
lua_tostring(fs->L, -1));
|
||||
break;
|
||||
case LUA_ERRMEM:
|
||||
zlog_err("Script '%s' memory error: %s", fs->name,
|
||||
lua_tostring(fs->L, -1));
|
||||
break;
|
||||
case LUA_ERRERR:
|
||||
zlog_err("Script '%s' error handler error: %s", fs->name,
|
||||
lua_tostring(fs->L, -1));
|
||||
break;
|
||||
case LUA_ERRGCMM:
|
||||
zlog_err("Script '%s' garbage collector error: %s", fs->name,
|
||||
lua_tostring(fs->L, -1));
|
||||
break;
|
||||
default:
|
||||
zlog_err("Script '%s' unknown error: %s", fs->name,
|
||||
lua_tostring(fs->L, -1));
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret != LUA_OK) {
|
||||
lua_pop(fs->L, 1);
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
/* LUA_OK is 0, so we can just return lua_pcall's result directly */
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *frrscript_get_result(struct frrscript *fs,
|
||||
const struct frrscript_env *result)
|
||||
{
|
||||
void *r;
|
||||
struct frrscript_codec c = {.typename = result->typename};
|
||||
|
||||
struct frrscript_codec *codec = hash_lookup(codec_hash, &c);
|
||||
assert(codec && "No encoder for type");
|
||||
|
||||
if (!codec->decoder) {
|
||||
zlog_err("No script decoder for type '%s'", result->typename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lua_getglobal(fs->L, result->name);
|
||||
r = codec->decoder(fs->L, -1);
|
||||
lua_pop(fs->L, 1);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void frrscript_register_type_codec(struct frrscript_codec *codec)
|
||||
{
|
||||
struct frrscript_codec c = *codec;
|
||||
|
||||
if (hash_lookup(codec_hash, &c)) {
|
||||
zlog_backtrace(LOG_ERR);
|
||||
assert(!"Type codec double-registered.");
|
||||
}
|
||||
|
||||
assert(hash_get(codec_hash, &c, codec_alloc));
|
||||
}
|
||||
|
||||
void frrscript_register_type_codecs(struct frrscript_codec *codecs)
|
||||
{
|
||||
for (int i = 0; codecs[i].typename != NULL; i++)
|
||||
frrscript_register_type_codec(&codecs[i]);
|
||||
}
|
||||
|
||||
struct frrscript *frrscript_load(const char *name,
|
||||
int (*load_cb)(struct frrscript *))
|
||||
{
|
||||
struct frrscript *fs = XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript));
|
||||
|
||||
fs->name = XSTRDUP(MTYPE_SCRIPT, name);
|
||||
fs->L = luaL_newstate();
|
||||
frrlua_export_logging(fs->L);
|
||||
|
||||
char fname[MAXPATHLEN];
|
||||
snprintf(fname, sizeof(fname), "%s/%s.lua", scriptdir, fs->name);
|
||||
|
||||
int ret = luaL_loadfile(fs->L, fname);
|
||||
|
||||
switch (ret) {
|
||||
case LUA_OK:
|
||||
break;
|
||||
case LUA_ERRSYNTAX:
|
||||
zlog_err("Failed loading script '%s': syntax error: %s", fname,
|
||||
lua_tostring(fs->L, -1));
|
||||
break;
|
||||
case LUA_ERRMEM:
|
||||
zlog_err("Failed loading script '%s': out-of-memory error: %s",
|
||||
fname, lua_tostring(fs->L, -1));
|
||||
break;
|
||||
case LUA_ERRGCMM:
|
||||
zlog_err(
|
||||
"Failed loading script '%s': garbage collector error: %s",
|
||||
fname, lua_tostring(fs->L, -1));
|
||||
break;
|
||||
case LUA_ERRFILE:
|
||||
zlog_err("Failed loading script '%s': file read error: %s",
|
||||
fname, lua_tostring(fs->L, -1));
|
||||
break;
|
||||
default:
|
||||
zlog_err("Failed loading script '%s': unknown error: %s", fname,
|
||||
lua_tostring(fs->L, -1));
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret != LUA_OK)
|
||||
goto fail;
|
||||
|
||||
if (load_cb && (*load_cb)(fs) != 0)
|
||||
goto fail;
|
||||
|
||||
return fs;
|
||||
fail:
|
||||
frrscript_unload(fs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void frrscript_unload(struct frrscript *fs)
|
||||
{
|
||||
lua_close(fs->L);
|
||||
XFREE(MTYPE_SCRIPT, fs->name);
|
||||
XFREE(MTYPE_SCRIPT, fs);
|
||||
}
|
||||
|
||||
void frrscript_init(const char *sd)
|
||||
{
|
||||
codec_hash = hash_create(codec_hash_key, codec_hash_cmp,
|
||||
"Lua type encoders");
|
||||
|
||||
strlcpy(scriptdir, sd, sizeof(scriptdir));
|
||||
|
||||
/* Register core library types */
|
||||
frrscript_register_type_codecs(frrscript_codecs_lib);
|
||||
}
|
||||
|
||||
#endif /* HAVE_SCRIPTING */
|
138
lib/frrscript.h
Normal file
138
lib/frrscript.h
Normal file
@ -0,0 +1,138 @@
|
||||
/* 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
|
||||
*/
|
||||
#ifndef __FRRSCRIPT_H__
|
||||
#define __FRRSCRIPT_H__
|
||||
|
||||
#include <zebra.h>
|
||||
|
||||
#ifdef HAVE_SCRIPTING
|
||||
|
||||
#include <lua.h>
|
||||
#include "frrlua.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*encoder_func)(lua_State *, const void *);
|
||||
typedef void *(*decoder_func)(lua_State *, int);
|
||||
|
||||
struct frrscript_codec {
|
||||
const char *typename;
|
||||
encoder_func encoder;
|
||||
decoder_func decoder;
|
||||
};
|
||||
|
||||
struct frrscript {
|
||||
/* Script name */
|
||||
char *name;
|
||||
|
||||
/* Lua state */
|
||||
struct lua_State *L;
|
||||
};
|
||||
|
||||
struct frrscript_env {
|
||||
/* Value type */
|
||||
const char *typename;
|
||||
|
||||
/* Binding name */
|
||||
const char *name;
|
||||
|
||||
/* Value */
|
||||
const void *val;
|
||||
};
|
||||
|
||||
/*
|
||||
* Create new FRR script.
|
||||
*/
|
||||
struct frrscript *frrscript_load(const char *name,
|
||||
int (*load_cb)(struct frrscript *));
|
||||
|
||||
/*
|
||||
* Destroy FRR script.
|
||||
*/
|
||||
void frrscript_unload(struct frrscript *fs);
|
||||
|
||||
/*
|
||||
* Register a Lua codec for a type.
|
||||
*
|
||||
* tname
|
||||
* Name of type; e.g., "peer", "ospf_interface", etc. Chosen at will.
|
||||
*
|
||||
* codec(s)
|
||||
* Function pointer to codec struct. Encoder function should push a Lua
|
||||
* table representing the passed argument - which will have the C type
|
||||
* associated with the chosen 'tname' to the provided stack. The decoder
|
||||
* function should pop a value from the top of the stack and return a heap
|
||||
* chunk containing that value. Allocations should be made with MTYPE_TMP.
|
||||
*
|
||||
* If using the plural function variant, pass a NULL-terminated array.
|
||||
*
|
||||
*/
|
||||
void frrscript_register_type_codec(struct frrscript_codec *codec);
|
||||
void frrscript_register_type_codecs(struct frrscript_codec *codecs);
|
||||
|
||||
/*
|
||||
* Initialize scripting subsystem. Call this before anything else.
|
||||
*
|
||||
* scriptdir
|
||||
* Directory in which to look for scripts
|
||||
*/
|
||||
void frrscript_init(const char *scriptdir);
|
||||
|
||||
|
||||
/*
|
||||
* Call script.
|
||||
*
|
||||
* fs
|
||||
* The script to call; this is obtained from frrscript_load().
|
||||
*
|
||||
* env
|
||||
* The script's environment. Specify this as an array of frrscript_env.
|
||||
*
|
||||
* Returns:
|
||||
* 0 if the script ran successfully, nonzero otherwise.
|
||||
*/
|
||||
int frrscript_call(struct frrscript *fs, struct frrscript_env *env);
|
||||
|
||||
|
||||
/*
|
||||
* Get result from finished script.
|
||||
*
|
||||
* fs
|
||||
* The script. This script must have been run already.
|
||||
*
|
||||
* result
|
||||
* The result to extract from the script.
|
||||
* This reuses the frrscript_env type, but only the typename and name fields
|
||||
* need to be set. The value is returned directly.
|
||||
*
|
||||
* Returns:
|
||||
* The script result of the specified name and type, or NULL.
|
||||
*/
|
||||
void *frrscript_get_result(struct frrscript *fs,
|
||||
const struct frrscript_env *result);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* HAVE_SCRIPTING */
|
||||
|
||||
#endif /* __FRRSCRIPT_H__ */
|
16
lib/libfrr.c
16
lib/libfrr.c
@ -43,6 +43,7 @@
|
||||
#include "frrcu.h"
|
||||
#include "frr_pthread.h"
|
||||
#include "defaults.h"
|
||||
#include "frrscript.h"
|
||||
|
||||
DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm))
|
||||
DEFINE_HOOK(frr_very_late_init, (struct thread_master * tm), (tm))
|
||||
@ -55,6 +56,7 @@ char frr_vtydir[256];
|
||||
const char frr_dbdir[] = DAEMON_DB_DIR;
|
||||
#endif
|
||||
const char frr_moduledir[] = MODULE_PATH;
|
||||
const char frr_scriptdir[] = SCRIPT_PATH;
|
||||
|
||||
char frr_protoname[256] = "NONE";
|
||||
char frr_protonameinst[256] = "NONE";
|
||||
@ -100,6 +102,7 @@ static void opt_extend(const struct optspec *os)
|
||||
#define OPTION_DB_FILE 1006
|
||||
#define OPTION_LOGGING 1007
|
||||
#define OPTION_LIMIT_FDS 1008
|
||||
#define OPTION_SCRIPTDIR 1009
|
||||
|
||||
static const struct option lo_always[] = {
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
@ -110,6 +113,7 @@ static const struct option lo_always[] = {
|
||||
{"pathspace", required_argument, NULL, 'N'},
|
||||
{"vty_socket", required_argument, NULL, OPTION_VTYSOCK},
|
||||
{"moduledir", required_argument, NULL, OPTION_MODULEDIR},
|
||||
{"scriptdir", required_argument, NULL, OPTION_SCRIPTDIR},
|
||||
{"log", required_argument, NULL, OPTION_LOG},
|
||||
{"log-level", required_argument, NULL, OPTION_LOGLEVEL},
|
||||
{"tcli", no_argument, NULL, OPTION_TCLI},
|
||||
@ -126,6 +130,7 @@ static const struct optspec os_always = {
|
||||
" -N, --pathspace Insert prefix into config & socket paths\n"
|
||||
" --vty_socket Override vty socket path\n"
|
||||
" --moduledir Override modules directory\n"
|
||||
" --scriptdir Override scripts directory\n"
|
||||
" --log Set Logging to stdout, syslog, or file:<name>\n"
|
||||
" --log-level Set Logging Level to use, debug, info, warn, etc\n"
|
||||
" --tcli Use transaction-based CLI\n"
|
||||
@ -533,6 +538,14 @@ static int frr_opt(int opt)
|
||||
}
|
||||
di->module_path = optarg;
|
||||
break;
|
||||
case OPTION_SCRIPTDIR:
|
||||
if (di->script_path) {
|
||||
fprintf(stderr, "--scriptdir option specified more than once!\n");
|
||||
errors++;
|
||||
break;
|
||||
}
|
||||
di->script_path = optarg;
|
||||
break;
|
||||
case OPTION_TCLI:
|
||||
di->cli_mode = FRR_CLI_TRANSACTIONAL;
|
||||
break;
|
||||
@ -717,6 +730,9 @@ struct thread_master *frr_init(void)
|
||||
lib_cmd_init();
|
||||
|
||||
frr_pthread_init();
|
||||
#ifdef HAVE_SCRIPTING
|
||||
frrscript_init(di->script_path ? di->script_path : frr_scriptdir);
|
||||
#endif
|
||||
|
||||
log_ref_init();
|
||||
log_ref_vty_init();
|
||||
|
@ -81,6 +81,7 @@ struct frr_daemon_info {
|
||||
#endif
|
||||
const char *vty_path;
|
||||
const char *module_path;
|
||||
const char *script_path;
|
||||
|
||||
const char *pathspace;
|
||||
bool zpathspace;
|
||||
@ -162,6 +163,7 @@ extern char frr_zclientpath[256];
|
||||
extern const char frr_sysconfdir[];
|
||||
extern char frr_vtydir[256];
|
||||
extern const char frr_moduledir[];
|
||||
extern const char frr_scriptdir[];
|
||||
|
||||
extern char frr_protoname[];
|
||||
extern char frr_protonameinst[];
|
||||
|
@ -26,6 +26,7 @@ lib_libfrr_la_SOURCES = \
|
||||
lib/filter_nb.c \
|
||||
lib/frrcu.c \
|
||||
lib/frrlua.c \
|
||||
lib/frrscript.c \
|
||||
lib/frr_pthread.c \
|
||||
lib/frrstr.c \
|
||||
lib/getopt.c \
|
||||
@ -185,6 +186,7 @@ pkginclude_HEADERS += \
|
||||
lib/filter.h \
|
||||
lib/freebsd-queue.h \
|
||||
lib/frrlua.h \
|
||||
lib/frrscript.h \
|
||||
lib/frr_pthread.h \
|
||||
lib/frratomic.h \
|
||||
lib/frrcu.h \
|
||||
|
Loading…
Reference in New Issue
Block a user