summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/command.c32
-rw-r--r--lib/filter.c257
-rw-r--r--lib/frr_zmq.c24
-rw-r--r--lib/frrlua.c30
-rw-r--r--lib/frrlua.h6
-rw-r--r--lib/frrscript.c205
-rw-r--r--lib/frrscript.h154
-rw-r--r--lib/if.c12
-rw-r--r--lib/if.h3
-rw-r--r--lib/pbr.h6
-rw-r--r--lib/plist.c232
-rw-r--r--lib/routemap.c214
-rw-r--r--lib/vrf.c1
13 files changed, 874 insertions, 302 deletions
diff --git a/lib/command.c b/lib/command.c
index fe17c68a8b..422544b70b 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -2422,28 +2422,30 @@ DEFUN(find,
}
#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")
+DEFUN(script, script_cmd, "script SCRIPT FUNCTION",
+ "Test command - execute a function in a script\n"
+ "Script name (same as filename in /etc/frr/scripts/)\n"
+ "Function name (in the script)\n")
{
struct prefix p;
(void)str2prefix("1.2.3.4/24", &p);
- struct frrscript *fs = frrscript_load(argv[1]->arg, NULL);
+ struct frrscript *fs = frrscript_new(argv[1]->arg);
- if (fs == NULL) {
- vty_out(vty, "Script '/etc/frr/scripts/%s.lua' not found\n",
- argv[1]->arg);
- } else {
- int ret = frrscript_call(fs, ("p", &p));
- char buf[40];
- prefix2str(&p, buf, sizeof(buf));
- vty_out(vty, "p: %s\n", buf);
- vty_out(vty, "Script result: %d\n", ret);
+ if (frrscript_load(fs, argv[2]->arg, NULL)) {
+ vty_out(vty,
+ "/etc/frr/scripts/%s.lua or function '%s' not found\n",
+ argv[1]->arg, argv[2]->arg);
}
+ int ret = frrscript_call(fs, argv[2]->arg, ("p", &p));
+ char buf[40];
+ prefix2str(&p, buf, sizeof(buf));
+ vty_out(vty, "p: %s\n", buf);
+ vty_out(vty, "Script result: %d\n", ret);
+
+ frrscript_delete(fs);
+
return CMD_SUCCESS;
}
#endif
diff --git a/lib/filter.c b/lib/filter.c
index 72a66d85ad..744ea9c480 100644
--- a/lib/filter.c
+++ b/lib/filter.c
@@ -30,6 +30,7 @@
#include "routemap.h"
#include "libfrr.h"
#include "northbound_cli.h"
+#include "json.h"
DEFINE_MTYPE_STATIC(LIB, ACCESS_LIST, "Access List");
DEFINE_MTYPE_STATIC(LIB, ACCESS_LIST_STR, "Access List Str");
@@ -443,71 +444,158 @@ void access_list_filter_add(struct access_list *access,
host A single host address
*/
-static void config_write_access_zebra(struct vty *, struct filter *);
-static void config_write_access_cisco(struct vty *, struct filter *);
+static void config_write_access_zebra(struct vty *, struct filter *,
+ json_object *);
+static void config_write_access_cisco(struct vty *, struct filter *,
+ json_object *);
+
+static const char *filter_type2str(struct filter *filter)
+{
+ if (filter->cisco) {
+ if (filter->u.cfilter.extended)
+ return "Extended";
+ else
+ return "Standard";
+ } else
+ return "Zebra";
+}
/* show access-list command. */
-static int filter_show(struct vty *vty, const char *name, afi_t afi)
+static int filter_show(struct vty *vty, const char *name, afi_t afi,
+ bool use_json)
{
struct access_list *access;
struct access_master *master;
struct filter *mfilter;
struct filter_cisco *filter;
- int write = 0;
+ bool first;
+ json_object *json = NULL;
+ json_object *json_proto = NULL;
master = access_master_get(afi);
- if (master == NULL)
+ if (master == NULL) {
+ if (use_json)
+ vty_out(vty, "{}\n");
return 0;
+ }
+
+ if (use_json)
+ json = json_object_new_object();
/* Print the name of the protocol */
- vty_out(vty, "%s:\n", frr_protoname);
+ if (json) {
+ json_proto = json_object_new_object();
+ json_object_object_add(json, frr_protoname, json_proto);
+ } else
+ vty_out(vty, "%s:\n", frr_protoname);
for (access = master->str.head; access; access = access->next) {
+ json_object *json_acl = NULL;
+ json_object *json_rules = NULL;
+
if (name && strcmp(access->name, name) != 0)
continue;
- write = 1;
+ first = true;
for (mfilter = access->head; mfilter; mfilter = mfilter->next) {
+ json_object *json_rule = NULL;
+
filter = &mfilter->u.cfilter;
- if (write) {
- vty_out(vty, "%s %s access list %s\n",
- mfilter->cisco ? (filter->extended
- ? "Extended"
- : "Standard")
- : "Zebra",
- (afi == AFI_IP)
- ? ("IP")
- : ((afi == AFI_IP6) ? ("IPv6 ")
- : ("MAC ")),
- access->name);
- write = 0;
+ if (first) {
+ const char *type = filter_type2str(mfilter);
+
+ if (json) {
+ json_acl = json_object_new_object();
+ json_object_object_add(json_proto,
+ access->name,
+ json_acl);
+
+ json_object_string_add(json_acl, "type",
+ type);
+ json_object_string_add(json_acl,
+ "addressFamily",
+ afi2str(afi));
+ json_rules = json_object_new_array();
+ json_object_object_add(
+ json_acl, "rules", json_rules);
+ } else {
+ vty_out(vty, "%s %s access list %s\n",
+ type,
+ (afi == AFI_IP)
+ ? ("IP")
+ : ((afi == AFI_IP6)
+ ? ("IPv6 ")
+ : ("MAC ")),
+ access->name);
+ }
+
+ first = false;
}
- vty_out(vty, " seq %" PRId64, mfilter->seq);
- vty_out(vty, " %s%s", filter_type_str(mfilter),
- mfilter->type == FILTER_DENY ? " " : "");
+ if (json) {
+ json_rule = json_object_new_object();
+ json_object_array_add(json_rules, json_rule);
+
+ json_object_int_add(json_rule, "sequenceNumber",
+ mfilter->seq);
+ json_object_string_add(
+ json_rule, "filterType",
+ filter_type_str(mfilter));
+ } else {
+ vty_out(vty, " seq %" PRId64, mfilter->seq);
+ vty_out(vty, " %s%s", filter_type_str(mfilter),
+ mfilter->type == FILTER_DENY ? " "
+ : "");
+ }
if (!mfilter->cisco)
- config_write_access_zebra(vty, mfilter);
+ config_write_access_zebra(vty, mfilter,
+ json_rule);
else if (filter->extended)
- config_write_access_cisco(vty, mfilter);
+ config_write_access_cisco(vty, mfilter,
+ json_rule);
else {
- if (filter->addr_mask.s_addr == 0xffffffff)
- vty_out(vty, " any\n");
- else {
- vty_out(vty, " %pI4", &filter->addr);
+ if (json) {
+ char buf[BUFSIZ];
+
+ json_object_string_add(
+ json_rule, "address",
+ inet_ntop(AF_INET,
+ &filter->addr, buf,
+ sizeof(buf)));
+ json_object_string_add(
+ json_rule, "mask",
+ inet_ntop(AF_INET,
+ &filter->addr_mask,
+ buf, sizeof(buf)));
+ } else {
if (filter->addr_mask.s_addr
- != INADDR_ANY)
- vty_out(vty,
- ", wildcard bits %pI4",
- &filter->addr_mask);
- vty_out(vty, "\n");
+ == 0xffffffff)
+ vty_out(vty, " any\n");
+ else {
+ vty_out(vty, " %pI4",
+ &filter->addr);
+ if (filter->addr_mask.s_addr
+ != INADDR_ANY)
+ vty_out(vty,
+ ", wildcard bits %pI4",
+ &filter->addr_mask);
+ vty_out(vty, "\n");
+ }
}
}
}
}
+
+ if (json) {
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+
return CMD_SUCCESS;
}
@@ -519,7 +607,7 @@ DEFUN (show_mac_access_list,
"mac access lists\n"
"List mac access lists\n")
{
- return filter_show(vty, NULL, AFI_L2VPN);
+ return filter_show(vty, NULL, AFI_L2VPN, false);
}
DEFUN (show_mac_access_list_name,
@@ -530,22 +618,24 @@ DEFUN (show_mac_access_list_name,
"List mac access lists\n"
"mac address\n")
{
- return filter_show(vty, argv[3]->arg, AFI_L2VPN);
+ return filter_show(vty, argv[3]->arg, AFI_L2VPN, false);
}
DEFUN (show_ip_access_list,
show_ip_access_list_cmd,
- "show ip access-list",
+ "show ip access-list [json]",
SHOW_STR
IP_STR
- "List IP access lists\n")
+ "List IP access lists\n"
+ JSON_STR)
{
- return filter_show(vty, NULL, AFI_IP);
+ bool uj = use_json(argc, argv);
+ return filter_show(vty, NULL, AFI_IP, uj);
}
DEFUN (show_ip_access_list_name,
show_ip_access_list_name_cmd,
- "show ip access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD>",
+ "show ip access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD> [json]",
SHOW_STR
IP_STR
"List IP access lists\n"
@@ -553,41 +643,64 @@ DEFUN (show_ip_access_list_name,
"IP extended access list\n"
"IP standard access list (expanded range)\n"
"IP extended access list (expanded range)\n"
- "IP zebra access-list\n")
+ "IP zebra access-list\n"
+ JSON_STR)
{
+ bool uj = use_json(argc, argv);
int idx_acl = 3;
- return filter_show(vty, argv[idx_acl]->arg, AFI_IP);
+ return filter_show(vty, argv[idx_acl]->arg, AFI_IP, uj);
}
DEFUN (show_ipv6_access_list,
show_ipv6_access_list_cmd,
- "show ipv6 access-list",
+ "show ipv6 access-list [json]",
SHOW_STR
IPV6_STR
- "List IPv6 access lists\n")
+ "List IPv6 access lists\n"
+ JSON_STR)
{
- return filter_show(vty, NULL, AFI_IP6);
+ bool uj = use_json(argc, argv);
+ return filter_show(vty, NULL, AFI_IP6, uj);
}
DEFUN (show_ipv6_access_list_name,
show_ipv6_access_list_name_cmd,
- "show ipv6 access-list WORD",
+ "show ipv6 access-list WORD [json]",
SHOW_STR
IPV6_STR
"List IPv6 access lists\n"
- "IPv6 zebra access-list\n")
+ "IPv6 zebra access-list\n"
+ JSON_STR)
{
+ bool uj = use_json(argc, argv);
int idx_word = 3;
- return filter_show(vty, argv[idx_word]->arg, AFI_IP6);
+ return filter_show(vty, argv[idx_word]->arg, AFI_IP6, uj);
}
-static void config_write_access_cisco(struct vty *vty, struct filter *mfilter)
+static void config_write_access_cisco(struct vty *vty, struct filter *mfilter,
+ json_object *json)
{
struct filter_cisco *filter;
filter = &mfilter->u.cfilter;
- if (filter->extended) {
+ if (json) {
+ char buf[BUFSIZ];
+
+ json_object_boolean_add(json, "extended", !!filter->extended);
+ json_object_string_add(
+ json, "sourceAddress",
+ inet_ntop(AF_INET, &filter->addr, buf, sizeof(buf)));
+ json_object_string_add(json, "sourceMask",
+ inet_ntop(AF_INET, &filter->addr_mask,
+ buf, sizeof(buf)));
+ json_object_string_add(
+ json, "destinationAddress",
+ inet_ntop(AF_INET, &filter->mask, buf, sizeof(buf)));
+ json_object_string_add(json, "destinationMask",
+ inet_ntop(AF_INET, &filter->mask_mask,
+ buf, sizeof(buf)));
+ } else {
vty_out(vty, " ip");
if (filter->addr_mask.s_addr == 0xffffffff)
vty_out(vty, " any");
@@ -607,19 +720,11 @@ static void config_write_access_cisco(struct vty *vty, struct filter *mfilter)
vty_out(vty, " %pI4", &filter->mask_mask);
}
vty_out(vty, "\n");
- } else {
- if (filter->addr_mask.s_addr == 0xffffffff)
- vty_out(vty, " any\n");
- else {
- vty_out(vty, " %pI4", &filter->addr);
- if (filter->addr_mask.s_addr != INADDR_ANY)
- vty_out(vty, " %pI4", &filter->addr_mask);
- vty_out(vty, "\n");
- }
}
}
-static void config_write_access_zebra(struct vty *vty, struct filter *mfilter)
+static void config_write_access_zebra(struct vty *vty, struct filter *mfilter,
+ json_object *json)
{
struct filter_zebra *filter;
struct prefix *p;
@@ -628,21 +733,29 @@ static void config_write_access_zebra(struct vty *vty, struct filter *mfilter)
filter = &mfilter->u.zfilter;
p = &filter->prefix;
- if (p->prefixlen == 0 && !filter->exact)
- vty_out(vty, " any");
- else if (p->family == AF_INET6 || p->family == AF_INET)
- vty_out(vty, " %s/%d%s",
- inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ),
- p->prefixlen, filter->exact ? " exact-match" : "");
- else if (p->family == AF_ETHERNET) {
- if (p->prefixlen == 0)
+ if (json) {
+ json_object_string_add(json, "prefix",
+ prefix2str(p, buf, sizeof(buf)));
+ json_object_boolean_add(json, "exact-match", !!filter->exact);
+ } else {
+ if (p->prefixlen == 0 && !filter->exact)
vty_out(vty, " any");
- else
- vty_out(vty, " %s", prefix_mac2str(&(p->u.prefix_eth),
- buf, sizeof(buf)));
- }
+ else if (p->family == AF_INET6 || p->family == AF_INET)
+ vty_out(vty, " %s/%d%s",
+ inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ),
+ p->prefixlen,
+ filter->exact ? " exact-match" : "");
+ else if (p->family == AF_ETHERNET) {
+ if (p->prefixlen == 0)
+ vty_out(vty, " any");
+ else
+ vty_out(vty, " %s",
+ prefix_mac2str(&(p->u.prefix_eth), buf,
+ sizeof(buf)));
+ }
- vty_out(vty, "\n");
+ vty_out(vty, "\n");
+ }
}
static struct cmd_node access_mac_node = {
diff --git a/lib/frr_zmq.c b/lib/frr_zmq.c
index ce52848a25..ea9c828f7c 100644
--- a/lib/frr_zmq.c
+++ b/lib/frr_zmq.c
@@ -17,6 +17,14 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+/*
+ * IF YOU MODIFY THIS FILE PLEASE RUN `make check` and ensure that
+ * the test_zmq.c unit test is still working. There are dependancies
+ * between the two that are extremely fragile. My understanding
+ * is that there is specialized ownership of the cb pointer based
+ * upon what is happening. Those assumptions are supposed to be
+ * tested in the test_zmq.c
+ */
#include <zebra.h>
#include <zmq.h>
@@ -309,8 +317,22 @@ void frrzmq_thread_cancel(struct frrzmq_cb **cb, struct cb_core *core)
core->cancelled = true;
thread_cancel(&core->thread);
+ /*
+ * Looking at this code one would assume that FRR
+ * would want a `!(*cb)->write.thread. This was
+ * attempted in e08165def1c62beee0e87385 but this
+ * change caused `make check` to stop working
+ * which was not noticed because our CI system
+ * does not build with zeromq. Put this back
+ * to the code as written in 2017. e08165de..
+ * was introduced in 2021. So someone was ok
+ * with frrzmq_thread_cancel for 4 years. This will
+ * allow those people doing `make check` to continue
+ * working. In the meantime if the people using
+ * this code see an issue they can fix it
+ */
if ((*cb)->read.cancelled && !(*cb)->read.thread
- && (*cb)->write.cancelled && !(*cb)->write.thread)
+ && (*cb)->write.cancelled && (*cb)->write.thread)
XFREE(MTYPE_ZEROMQ_CB, *cb);
}
diff --git a/lib/frrlua.c b/lib/frrlua.c
index e97e48121c..96d7269440 100644
--- a/lib/frrlua.c
+++ b/lib/frrlua.c
@@ -29,6 +29,8 @@
#include "log.h"
#include "buffer.h"
+DEFINE_MTYPE(LIB, SCRIPT_RES, "Scripting results");
+
/* Lua stuff */
/*
@@ -81,7 +83,7 @@ void lua_decode_prefix(lua_State *L, int idx, struct prefix *prefix)
void *lua_toprefix(lua_State *L, int idx)
{
- struct prefix *p = XCALLOC(MTYPE_TMP, sizeof(struct prefix));
+ struct prefix *p = XCALLOC(MTYPE_SCRIPT_RES, sizeof(struct prefix));
lua_decode_prefix(L, idx, p);
return p;
}
@@ -153,7 +155,8 @@ void lua_decode_interface(lua_State *L, int idx, struct interface *ifp)
}
void *lua_tointerface(lua_State *L, int idx)
{
- struct interface *ifp = XCALLOC(MTYPE_TMP, sizeof(struct interface));
+ struct interface *ifp =
+ XCALLOC(MTYPE_SCRIPT_RES, sizeof(struct interface));
lua_decode_interface(L, idx, ifp);
return ifp;
@@ -183,7 +186,8 @@ void lua_decode_inaddr(lua_State *L, int idx, struct in_addr *inaddr)
void *lua_toinaddr(lua_State *L, int idx)
{
- struct in_addr *inaddr = XCALLOC(MTYPE_TMP, sizeof(struct in_addr));
+ struct in_addr *inaddr =
+ XCALLOC(MTYPE_SCRIPT_RES, sizeof(struct in_addr));
lua_decode_inaddr(L, idx, inaddr);
return inaddr;
}
@@ -213,7 +217,8 @@ void lua_decode_in6addr(lua_State *L, int idx, struct in6_addr *in6addr)
void *lua_toin6addr(lua_State *L, int idx)
{
- struct in6_addr *in6addr = XCALLOC(MTYPE_TMP, sizeof(struct in6_addr));
+ struct in6_addr *in6addr =
+ XCALLOC(MTYPE_SCRIPT_RES, sizeof(struct in6_addr));
lua_decode_in6addr(L, idx, in6addr);
return in6addr;
}
@@ -243,7 +248,8 @@ void lua_decode_sockunion(lua_State *L, int idx, union sockunion *su)
void *lua_tosockunion(lua_State *L, int idx)
{
- union sockunion *su = XCALLOC(MTYPE_TMP, sizeof(union sockunion));
+ union sockunion *su =
+ XCALLOC(MTYPE_SCRIPT_RES, sizeof(union sockunion));
lua_decode_sockunion(L, idx, su);
return su;
@@ -262,7 +268,7 @@ void lua_decode_timet(lua_State *L, int idx, time_t *t)
void *lua_totimet(lua_State *L, int idx)
{
- time_t *t = XCALLOC(MTYPE_TMP, sizeof(time_t));
+ time_t *t = XCALLOC(MTYPE_SCRIPT_RES, sizeof(time_t));
lua_decode_timet(L, idx, t);
return t;
@@ -283,7 +289,7 @@ void lua_decode_integerp(lua_State *L, int idx, long long *num)
void *lua_tointegerp(lua_State *L, int idx)
{
- long long *num = XCALLOC(MTYPE_TMP, sizeof(long long));
+ long long *num = XCALLOC(MTYPE_SCRIPT_RES, sizeof(long long));
lua_decode_integerp(L, idx, num);
return num;
@@ -297,7 +303,7 @@ void lua_decode_stringp(lua_State *L, int idx, char *str)
void *lua_tostringp(lua_State *L, int idx)
{
- char *string = XSTRDUP(MTYPE_TMP, lua_tostring(L, idx));
+ char *string = XSTRDUP(MTYPE_SCRIPT_RES, lua_tostring(L, idx));
return string;
}
@@ -309,6 +315,14 @@ void lua_decode_noop(lua_State *L, int idx, const void *ptr)
{
}
+
+/*
+ * Noop decoder for int.
+ */
+void lua_decode_integer_noop(lua_State *L, int idx, int i)
+{
+}
+
/*
* Logging.
*
diff --git a/lib/frrlua.h b/lib/frrlua.h
index c4de82740c..3e16c82e22 100644
--- a/lib/frrlua.h
+++ b/lib/frrlua.h
@@ -34,6 +34,8 @@
extern "C" {
#endif
+DECLARE_MTYPE(SCRIPT_RES);
+
/*
* gcc-10 is complaining about the wrapper function
* not being compatible with lua_pushstring returning
@@ -162,10 +164,12 @@ void lua_decode_stringp(lua_State *L, int idx, char *str);
void *lua_tostringp(lua_State *L, int idx);
/*
- * No-op decocder
+ * No-op decoders
*/
void lua_decode_noop(lua_State *L, int idx, const void *ptr);
+void lua_decode_integer_noop(lua_State *L, int idx, int i);
+
/*
* Retrieve an integer from table on the top of the stack.
*
diff --git a/lib/frrscript.c b/lib/frrscript.c
index 1a9f3639dd..d00b84ccbb 100644
--- a/lib/frrscript.c
+++ b/lib/frrscript.c
@@ -102,67 +102,136 @@ static void codec_free(struct codec *c)
}
#endif
-/* Generic script APIs */
+/* Lua function hash utils */
-int _frrscript_call(struct frrscript *fs)
+unsigned int lua_function_hash_key(const void *data)
{
+ const struct lua_function_state *lfs = data;
- int ret = lua_pcall(fs->L, 0, 0, 0);
+ return string_hash_make(lfs->name);
+}
+
+bool lua_function_hash_cmp(const void *d1, const void *d2)
+{
+ const struct lua_function_state *lfs1 = d1;
+ const struct lua_function_state *lfs2 = d2;
+
+ return strmatch(lfs1->name, lfs2->name);
+}
+
+void *lua_function_alloc(void *arg)
+{
+ struct lua_function_state *tmp = arg;
+
+ struct lua_function_state *lfs =
+ XCALLOC(MTYPE_SCRIPT, sizeof(struct lua_function_state));
+ lfs->name = tmp->name;
+ lfs->L = tmp->L;
+ return lfs;
+}
+
+static void lua_function_free(struct hash_bucket *b, void *data)
+{
+ struct lua_function_state *lfs = (struct lua_function_state *)b->data;
+ lua_close(lfs->L);
+ XFREE(MTYPE_SCRIPT, lfs);
+}
+
+/* internal frrscript APIs */
+
+int _frrscript_call_lua(struct lua_function_state *lfs, int nargs)
+{
+
+ int ret;
+ ret = lua_pcall(lfs->L, nargs, 1, 0);
switch (ret) {
case LUA_OK:
break;
case LUA_ERRRUN:
- zlog_err("Script '%s' runtime error: %s", fs->name,
- lua_tostring(fs->L, -1));
+ zlog_err("Lua hook call '%s' : runtime error: %s", lfs->name,
+ lua_tostring(lfs->L, -1));
break;
case LUA_ERRMEM:
- zlog_err("Script '%s' memory error: %s", fs->name,
- lua_tostring(fs->L, -1));
+ zlog_err("Lua hook call '%s' : memory error: %s", lfs->name,
+ lua_tostring(lfs->L, -1));
break;
case LUA_ERRERR:
- zlog_err("Script '%s' error handler error: %s", fs->name,
- lua_tostring(fs->L, -1));
+ zlog_err("Lua hook call '%s' : error handler error: %s",
+ lfs->name, lua_tostring(lfs->L, -1));
break;
case LUA_ERRGCMM:
- zlog_err("Script '%s' garbage collector error: %s", fs->name,
- lua_tostring(fs->L, -1));
+ zlog_err("Lua hook call '%s' : garbage collector error: %s",
+ lfs->name, lua_tostring(lfs->L, -1));
break;
default:
- zlog_err("Script '%s' unknown error: %s", fs->name,
- lua_tostring(fs->L, -1));
+ zlog_err("Lua hook call '%s' : unknown error: %s", lfs->name,
+ lua_tostring(lfs->L, -1));
break;
}
if (ret != LUA_OK) {
- lua_pop(fs->L, 1);
+ lua_pop(lfs->L, 1);
goto done;
}
+ if (lua_gettop(lfs->L) != 1) {
+ zlog_err(
+ "Lua hook call '%s': Lua function should return only 1 result",
+ lfs->name);
+ ret = 1;
+ goto done;
+ }
+
+ if (lua_istable(lfs->L, 1) != 1) {
+ zlog_err(
+ "Lua hook call '%s': Lua function should return a Lua table",
+ lfs->name);
+ ret = 1;
+ }
+
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 *frrscript_get_result(struct frrscript *fs, const char *function_name,
+ const char *name,
+ void *(*lua_to)(lua_State *L, int idx))
{
- void *r;
- struct frrscript_codec c = {.typename = result->typename};
+ void *p;
+ struct lua_function_state *lfs;
+ struct lua_function_state lookup = {.name = function_name};
- struct frrscript_codec *codec = hash_lookup(codec_hash, &c);
- assert(codec && "No encoder for type");
+ lfs = hash_lookup(fs->lua_function_hash, &lookup);
- if (!codec->decoder) {
- zlog_err("No script decoder for type '%s'", result->typename);
+ if (lfs == NULL)
+ return NULL;
+
+ /* At this point, the Lua state should have only the returned table.
+ * We will then search the table for the key/value we're interested in.
+ * Then if the value is present (i.e. non-nil), call the lua_to*
+ * decoder.
+ */
+ assert(lua_gettop(lfs->L) == 1);
+ assert(lua_istable(lfs->L, -1) == 1);
+ lua_getfield(lfs->L, -1, name);
+ if (lua_isnil(lfs->L, -1)) {
+ lua_pop(lfs->L, 1);
+ zlog_warn(
+ "frrscript: '%s.lua': '%s': tried to decode '%s' as result but failed",
+ fs->name, function_name, name);
return NULL;
}
+ p = lua_to(lfs->L, 2);
- lua_getglobal(fs->L, result->name);
- r = codec->decoder(fs->L, -1);
- lua_pop(fs->L, 1);
+ /* At the end, the Lua state should be same as it was at the start
+ * i.e. containing soley the returned table.
+ */
+ assert(lua_gettop(lfs->L) == 1);
+ assert(lua_istable(lfs->L, -1) == 1);
- return r;
+ return p;
}
void frrscript_register_type_codec(struct frrscript_codec *codec)
@@ -183,61 +252,99 @@ void frrscript_register_type_codecs(struct frrscript_codec *codecs)
frrscript_register_type_codec(&codecs[i]);
}
-struct frrscript *frrscript_load(const char *name,
- int (*load_cb)(struct frrscript *))
+struct frrscript *frrscript_new(const char *name)
{
struct frrscript *fs = XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript));
fs->name = XSTRDUP(MTYPE_SCRIPT, name);
- fs->L = luaL_newstate();
- frrlua_export_logging(fs->L);
+ fs->lua_function_hash =
+ hash_create(lua_function_hash_key, lua_function_hash_cmp,
+ "Lua function state hash");
+ return fs;
+}
+
+int frrscript_load(struct frrscript *fs, const char *function_name,
+ int (*load_cb)(struct frrscript *))
+{
- char fname[MAXPATHLEN * 2];
- snprintf(fname, sizeof(fname), "%s/%s.lua", scriptdir, fs->name);
+ /* Set up the Lua script */
+ lua_State *L = luaL_newstate();
- int ret = luaL_loadfile(fs->L, fname);
+ frrlua_export_logging(L);
+
+ char script_name[MAXPATHLEN];
+
+ if (snprintf(script_name, sizeof(script_name), "%s/%s.lua", scriptdir,
+ fs->name)
+ >= (int)sizeof(script_name)) {
+ zlog_err("frrscript: path to script %s/%s.lua is too long",
+ scriptdir, fs->name);
+ goto fail;
+ }
+ int ret = luaL_dofile(L, script_name);
switch (ret) {
case LUA_OK:
break;
case LUA_ERRSYNTAX:
- zlog_err("Failed loading script '%s': syntax error: %s", fname,
- lua_tostring(fs->L, -1));
+ zlog_err(
+ "frrscript: failed loading script '%s.lua': syntax error: %s",
+ script_name, lua_tostring(L, -1));
break;
case LUA_ERRMEM:
- zlog_err("Failed loading script '%s': out-of-memory error: %s",
- fname, lua_tostring(fs->L, -1));
+ zlog_err(
+ "frrscript: failed loading script '%s.lua': out-of-memory error: %s",
+ script_name, lua_tostring(L, -1));
break;
case LUA_ERRGCMM:
zlog_err(
- "Failed loading script '%s': garbage collector error: %s",
- fname, lua_tostring(fs->L, -1));
+ "frrscript: failed loading script '%s.lua': garbage collector error: %s",
+ script_name, lua_tostring(L, -1));
break;
case LUA_ERRFILE:
- zlog_err("Failed loading script '%s': file read error: %s",
- fname, lua_tostring(fs->L, -1));
+ zlog_err(
+ "frrscript: failed loading script '%s.lua': file read error: %s",
+ script_name, lua_tostring(L, -1));
break;
default:
- zlog_err("Failed loading script '%s': unknown error: %s", fname,
- lua_tostring(fs->L, -1));
+ zlog_err(
+ "frrscript: failed loading script '%s.lua': unknown error: %s",
+ script_name, lua_tostring(L, -1));
break;
}
if (ret != LUA_OK)
goto fail;
- if (load_cb && (*load_cb)(fs) != 0)
+ /* Push the Lua function we want */
+ lua_getglobal(L, function_name);
+ if (lua_isfunction(L, lua_gettop(L)) == 0) {
+ zlog_err("frrscript: loaded script '%s.lua' but %s not found",
+ script_name, function_name);
goto fail;
+ }
- return fs;
+ if (load_cb && (*load_cb)(fs) != 0) {
+ zlog_err(
+ "frrscript: '%s.lua': %s: loaded but callback returned non-zero exit code",
+ script_name, function_name);
+ goto fail;
+ }
+
+ /* Add the Lua function state to frrscript */
+ struct lua_function_state key = {.name = function_name, .L = L};
+
+ hash_get(fs->lua_function_hash, &key, lua_function_alloc);
+
+ return 0;
fail:
- frrscript_unload(fs);
- return NULL;
+ lua_close(L);
+ return 1;
}
-void frrscript_unload(struct frrscript *fs)
+void frrscript_delete(struct frrscript *fs)
{
- lua_close(fs->L);
+ hash_iterate(fs->lua_function_hash, lua_function_free, NULL);
XFREE(MTYPE_SCRIPT, fs->name);
XFREE(MTYPE_SCRIPT, fs);
}
diff --git a/lib/frrscript.h b/lib/frrscript.h
index 8612c602f3..540676c099 100644
--- a/lib/frrscript.h
+++ b/lib/frrscript.h
@@ -25,7 +25,7 @@
#include <lua.h>
#include "frrlua.h"
-#include "../bgpd/bgp_script.h"
+#include "bgpd/bgp_script.h" // for peer and attr encoders/decoders
#ifdef __cplusplus
extern "C" {
@@ -40,14 +40,30 @@ struct frrscript_codec {
decoder_func decoder;
};
+struct lua_function_state {
+ const char *name;
+ lua_State *L;
+};
+
struct frrscript {
/* Script name */
char *name;
- /* Lua state */
- struct lua_State *L;
+ /* Hash of Lua function name to Lua function state */
+ struct hash *lua_function_hash;
};
+
+/*
+ * Hash related functions for lua_function_hash
+ */
+
+void *lua_function_alloc(void *arg);
+
+unsigned int lua_function_hash_key(const void *data);
+
+bool lua_function_hash_cmp(const void *d1, const void *d2);
+
struct frrscript_env {
/* Value type */
const char *typename;
@@ -60,15 +76,24 @@ struct frrscript_env {
};
/*
- * Create new FRR script.
+ * Create new struct frrscript for a Lua script.
+ * This will hold the states for the Lua functions in this script.
+ *
+ * scriptname
+ * Name of the Lua script file, without the .lua
*/
-struct frrscript *frrscript_load(const char *name,
- int (*load_cb)(struct frrscript *));
+struct frrscript *frrscript_new(const char *scriptname);
/*
- * Destroy FRR script.
+ * Load a function into frrscript, run callback if any
*/
-void frrscript_unload(struct frrscript *fs);
+int frrscript_load(struct frrscript *fs, const char *function_name,
+ int (*load_cb)(struct frrscript *));
+
+/*
+ * Delete Lua function states and frrscript
+ */
+void frrscript_delete(struct frrscript *fs);
/*
* Register a Lua codec for a type.
@@ -97,16 +122,31 @@ void frrscript_register_type_codecs(struct frrscript_codec *codecs);
*/
void frrscript_init(const char *scriptdir);
-#define ENCODE_ARGS(name, value) \
- do { \
- ENCODE_ARGS_WITH_STATE(L, value); \
- lua_setglobal(L, name); \
- } while (0)
+/*
+ * This macro is mapped to every (name, value) in frrscript_call,
+ * so this in turn maps them onto their encoders
+ */
+#define ENCODE_ARGS(name, value) ENCODE_ARGS_WITH_STATE(lfs->L, (value))
+/*
+ * This macro is also mapped to every (name, value) in frrscript_call, but
+ * not every value can be mapped to its decoder - only those that appear
+ * in the returned table will. To find out if they appear in the returned
+ * table, first pop the value and check if its nil. Only call the decoder
+ * if non-nil.
+ *
+ * At the end, the only thing left on the stack should be the
+ * returned table.
+ */
#define DECODE_ARGS(name, value) \
do { \
- lua_getglobal(L, name); \
- DECODE_ARGS_WITH_STATE(L, value); \
+ lua_getfield(lfs->L, 1, (name)); \
+ if (lua_isnil(lfs->L, 2)) { \
+ lua_pop(lfs->L, 1); \
+ } else { \
+ DECODE_ARGS_WITH_STATE(lfs->L, (value)); \
+ } \
+ assert(lua_gettop(lfs->L) == 1); \
} while (0)
/*
@@ -120,6 +160,7 @@ void frrscript_init(const char *scriptdir);
*/
#define ENCODE_ARGS_WITH_STATE(L, value) \
_Generic((value), \
+int : lua_pushinteger, \
long long * : lua_pushintegerp, \
struct prefix * : lua_pushprefix, \
struct interface * : lua_pushinterface, \
@@ -131,10 +172,11 @@ char * : lua_pushstring_wrapper, \
struct attr * : lua_pushattr, \
struct peer * : lua_pushpeer, \
const struct prefix * : lua_pushprefix \
-)(L, value)
+)((L), (value))
#define DECODE_ARGS_WITH_STATE(L, value) \
_Generic((value), \
+int : lua_decode_integer_noop, \
long long * : lua_decode_integerp, \
struct prefix * : lua_decode_prefix, \
struct interface * : lua_decode_interface, \
@@ -146,56 +188,84 @@ char * : lua_decode_stringp, \
struct attr * : lua_decode_attr, \
struct peer * : lua_decode_noop, \
const struct prefix * : lua_decode_noop \
-)(L, -1, value)
+)((L), -1, (value))
/*
- * Call script.
+ * Call Lua function state (abstraction for a single Lua function)
*
- * fs
- * The script to call; this is obtained from frrscript_load().
+ * lfs
+ * The Lua function to call; this should have been loaded in by
+ * frrscript_load(). nargs Number of arguments the function accepts
*
* Returns:
* 0 if the script ran successfully, nonzero otherwise.
*/
-int _frrscript_call(struct frrscript *fs);
+int _frrscript_call_lua(struct lua_function_state *lfs, int nargs);
/*
- * Wrapper for call script. Maps values passed in to their encoder
- * and decoder types.
+ * Wrapper for calling Lua function state. Maps values passed in to their
+ * encoder and decoder types.
*
* fs
- * The script to call; this is obtained from frrscript_load().
+ * The struct frrscript in which the Lua fuction was loaded into
+ * f
+ * Name of the Lua function.
*
* Returns:
* 0 if the script ran successfully, nonzero otherwise.
*/
-#define frrscript_call(fs, ...) \
- ({ \
- lua_State *L = fs->L; \
- MAP_LISTS(ENCODE_ARGS, ##__VA_ARGS__); \
- int ret = _frrscript_call(fs); \
- if (ret == 0) { \
- MAP_LISTS(DECODE_ARGS, ##__VA_ARGS__); \
- } \
- ret; \
+#define frrscript_call(fs, f, ...) \
+ ({ \
+ struct lua_function_state lookup = {.name = (f)}; \
+ struct lua_function_state *lfs; \
+ lfs = hash_lookup((fs)->lua_function_hash, &lookup); \
+ lfs == NULL ? ({ \
+ zlog_err( \
+ "frrscript: '%s.lua': '%s': tried to call this function but it was not loaded", \
+ (fs)->name, (f)); \
+ 1; \
+ }) \
+ : ({ \
+ MAP_LISTS(ENCODE_ARGS, ##__VA_ARGS__); \
+ _frrscript_call_lua( \
+ lfs, PP_NARG(__VA_ARGS__)); \
+ }) != 0 \
+ ? ({ \
+ zlog_err( \
+ "frrscript: '%s.lua': '%s': this function called but returned non-zero exit code. No variables modified.", \
+ (fs)->name, (f)); \
+ 1; \
+ }) \
+ : ({ \
+ MAP_LISTS(DECODE_ARGS, \
+ ##__VA_ARGS__); \
+ 0; \
+ }); \
})
/*
- * Get result from finished script.
+ * Get result from finished function
*
* 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.
+ * function_name
+ * Name of the Lua function.
+ * name
+ * Name of the result.
+ * This will be used as a string key to retrieve from the table that the
+ * Lua function returns.
+ * The name here should *not* appear in frrscript_call.
+ * lua_to
+ * Function pointer to a lua_to decoder function.
+ * This function should allocate and decode a value from the Lua state.
*
* Returns:
- * The script result of the specified name and type, or NULL.
+ * A pointer to the decoded value from the Lua state, or NULL if no such
+ * value.
*/
-void *frrscript_get_result(struct frrscript *fs,
- const struct frrscript_env *result);
+void *frrscript_get_result(struct frrscript *fs, const char *function_name,
+ const char *name,
+ void *(*lua_to)(lua_State *L, int idx));
#ifdef __cplusplus
}
diff --git a/lib/if.c b/lib/if.c
index e37b4f55b0..6c57855ca1 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -1349,10 +1349,20 @@ static const struct cmd_variable_handler if_var_handlers[] = {
{.tokenname = "INTERFACE", .completions = if_autocomplete},
{.completions = NULL}};
-void if_cmd_init(void)
+static struct cmd_node interface_node = {
+ .name = "interface",
+ .node = INTERFACE_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-if)# ",
+};
+
+void if_cmd_init(int (*config_write)(struct vty *))
{
cmd_variable_handler_register(if_var_handlers);
+ interface_node.config_write = config_write;
+ install_node(&interface_node);
+
install_element(CONFIG_NODE, &interface_cmd);
install_element(CONFIG_NODE, &no_interface_cmd);
diff --git a/lib/if.h b/lib/if.h
index 0d689fe14b..43e2d3cffa 100644
--- a/lib/if.h
+++ b/lib/if.h
@@ -597,7 +597,8 @@ struct if_link_params *if_link_params_get(struct interface *);
void if_link_params_free(struct interface *);
/* Northbound. */
-extern void if_cmd_init(void);
+struct vty;
+extern void if_cmd_init(int (*config_write)(struct vty *));
extern void if_zapi_callbacks(int (*create)(struct interface *ifp),
int (*up)(struct interface *ifp),
int (*down)(struct interface *ifp),
diff --git a/lib/pbr.h b/lib/pbr.h
index e365888662..cef1d9d380 100644
--- a/lib/pbr.h
+++ b/lib/pbr.h
@@ -49,7 +49,8 @@ struct pbr_filter {
#define PBR_FILTER_PROTO (1 << 5)
#define PBR_FILTER_SRC_PORT_RANGE (1 << 6)
#define PBR_FILTER_DST_PORT_RANGE (1 << 7)
-#define PBR_FILTER_DSFIELD (1 << 8)
+#define PBR_FILTER_DSFIELD (1 << 8)
+#define PBR_FILTER_IP_PROTOCOL (1 << 9)
#define PBR_DSFIELD_DSCP (0xfc) /* Upper 6 bits of DS field: DSCP */
#define PBR_DSFIELD_ECN (0x03) /* Lower 2 bits of DS field: BCN */
@@ -67,6 +68,9 @@ struct pbr_filter {
/* Filter with fwmark */
uint32_t fwmark;
+
+ /* Filter with the ip protocol */
+ uint8_t ip_proto;
};
/*
diff --git a/lib/plist.c b/lib/plist.c
index 2b42c43764..63ae579796 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -932,102 +932,206 @@ enum display_type {
first_match_display
};
-static void vty_show_prefix_entry(struct vty *vty, afi_t afi,
+static void vty_show_prefix_entry(struct vty *vty, json_object *json, afi_t afi,
struct prefix_list *plist,
struct prefix_master *master,
enum display_type dtype, int seqnum)
{
struct prefix_list_entry *pentry;
+ json_object *json_pl = NULL;
/* Print the name of the protocol */
- vty_out(vty, "%s: ", frr_protoname);
+ if (json) {
+ json_pl = json_object_new_object();
+ json_object_object_add(json, plist->name, json_pl);
+ } else
+ vty_out(vty, "%s: ", frr_protoname);
if (dtype == normal_display) {
- vty_out(vty, "ip%s prefix-list %s: %d entries\n",
- afi == AFI_IP ? "" : "v6", plist->name, plist->count);
- if (plist->desc)
- vty_out(vty, " Description: %s\n", plist->desc);
+ if (json) {
+ json_object_string_add(json_pl, "addressFamily",
+ afi2str(afi));
+ json_object_int_add(json_pl, "entries", plist->count);
+ if (plist->desc)
+ json_object_string_add(json_pl, "description",
+ plist->desc);
+ } else {
+ vty_out(vty, "ip%s prefix-list %s: %d entries\n",
+ afi == AFI_IP ? "" : "v6", plist->name,
+ plist->count);
+ if (plist->desc)
+ vty_out(vty, " Description: %s\n",
+ plist->desc);
+ }
} else if (dtype == summary_display || dtype == detail_display) {
- vty_out(vty, "ip%s prefix-list %s:\n",
- afi == AFI_IP ? "" : "v6", plist->name);
-
- if (plist->desc)
- vty_out(vty, " Description: %s\n", plist->desc);
-
- vty_out(vty,
- " count: %d, range entries: %d, sequences: %" PRId64 " - %" PRId64 "\n",
- plist->count, plist->rangecount,
- plist->head ? plist->head->seq : 0,
- plist->tail ? plist->tail->seq : 0);
+ if (json) {
+ json_object_string_add(json_pl, "addressFamily",
+ afi2str(afi));
+ if (plist->desc)
+ json_object_string_add(json_pl, "description",
+ plist->desc);
+ json_object_int_add(json_pl, "count", plist->count);
+ json_object_int_add(json_pl, "rangeEntries",
+ plist->rangecount);
+ json_object_int_add(json_pl, "sequenceStart",
+ plist->head ? plist->head->seq : 0);
+ json_object_int_add(json_pl, "sequenceEnd",
+ plist->tail ? plist->tail->seq : 0);
+ } else {
+ vty_out(vty, "ip%s prefix-list %s:\n",
+ afi == AFI_IP ? "" : "v6", plist->name);
+
+ if (plist->desc)
+ vty_out(vty, " Description: %s\n",
+ plist->desc);
+
+ vty_out(vty,
+ " count: %d, range entries: %d, sequences: %" PRId64
+ " - %" PRId64 "\n",
+ plist->count, plist->rangecount,
+ plist->head ? plist->head->seq : 0,
+ plist->tail ? plist->tail->seq : 0);
+ }
}
if (dtype != summary_display) {
+ json_object *json_entries = NULL;
+
+ if (json) {
+ json_entries = json_object_new_array();
+ json_object_object_add(json_pl, "entries",
+ json_entries);
+ }
+
for (pentry = plist->head; pentry; pentry = pentry->next) {
if (dtype == sequential_display
&& pentry->seq != seqnum)
continue;
- vty_out(vty, " ");
+ if (json) {
+ json_object *json_entry;
+ char buf[BUFSIZ];
- vty_out(vty, "seq %" PRId64 " ", pentry->seq);
+ json_entry = json_object_new_object();
+ json_object_array_add(json_entries, json_entry);
- vty_out(vty, "%s ", prefix_list_type_str(pentry));
-
- if (pentry->any)
- vty_out(vty, "any");
- else {
- struct prefix *p = &pentry->prefix;
-
- vty_out(vty, "%pFX", p);
+ json_object_int_add(json_entry,
+ "sequenceNumber",
+ pentry->seq);
+ json_object_string_add(
+ json_entry, "type",
+ prefix_list_type_str(pentry));
+ json_object_string_add(
+ json_entry, "prefix",
+ prefix2str(&pentry->prefix, buf,
+ sizeof(buf)));
if (pentry->ge)
- vty_out(vty, " ge %d", pentry->ge);
+ json_object_int_add(
+ json_entry,
+ "minimumPrefixLength",
+ pentry->ge);
if (pentry->le)
- vty_out(vty, " le %d", pentry->le);
+ json_object_int_add(
+ json_entry,
+ "maximumPrefixLength",
+ pentry->le);
+
+ if (dtype == detail_display
+ || dtype == sequential_display) {
+ json_object_int_add(json_entry,
+ "hitCount",
+ pentry->hitcnt);
+ json_object_int_add(json_entry,
+ "referenceCount",
+ pentry->refcnt);
+ }
+ } else {
+ vty_out(vty, " ");
+
+ vty_out(vty, "seq %" PRId64 " ", pentry->seq);
+
+ vty_out(vty, "%s ",
+ prefix_list_type_str(pentry));
+
+ if (pentry->any)
+ vty_out(vty, "any");
+ else {
+ struct prefix *p = &pentry->prefix;
+
+ vty_out(vty, "%pFX", p);
+
+ if (pentry->ge)
+ vty_out(vty, " ge %d",
+ pentry->ge);
+ if (pentry->le)
+ vty_out(vty, " le %d",
+ pentry->le);
+ }
+
+ if (dtype == detail_display
+ || dtype == sequential_display)
+ vty_out(vty,
+ " (hit count: %ld, refcount: %ld)",
+ pentry->hitcnt, pentry->refcnt);
+
+ vty_out(vty, "\n");
}
-
- if (dtype == detail_display
- || dtype == sequential_display)
- vty_out(vty, " (hit count: %ld, refcount: %ld)",
- pentry->hitcnt, pentry->refcnt);
-
- vty_out(vty, "\n");
}
}
}
static int vty_show_prefix_list(struct vty *vty, afi_t afi, const char *name,
- const char *seq, enum display_type dtype)
+ const char *seq, enum display_type dtype,
+ bool uj)
{
struct prefix_list *plist;
struct prefix_master *master;
int64_t seqnum = 0;
+ json_object *json = NULL;
+ json_object *json_proto = NULL;
master = prefix_master_get(afi, 0);
if (master == NULL)
return CMD_WARNING;
+ if (uj) {
+ json = json_object_new_object();
+ json_proto = json_object_new_object();
+ json_object_object_add(json, frr_protoname, json_proto);
+ }
+
if (seq)
seqnum = (int64_t)atol(seq);
if (name) {
plist = prefix_list_lookup(afi, name);
if (!plist) {
- vty_out(vty, "%% Can't find specified prefix-list\n");
+ if (!uj)
+ vty_out(vty,
+ "%% Can't find specified prefix-list\n");
return CMD_WARNING;
}
- vty_show_prefix_entry(vty, afi, plist, master, dtype, seqnum);
+ vty_show_prefix_entry(vty, json_proto, afi, plist, master,
+ dtype, seqnum);
} else {
if (dtype == detail_display || dtype == summary_display) {
- if (master->recent)
+ if (master->recent && !uj)
vty_out(vty,
"Prefix-list with the last deletion/insertion: %s\n",
master->recent->name);
}
for (plist = master->str.head; plist; plist = plist->next)
- vty_show_prefix_entry(vty, afi, plist, master, dtype,
- seqnum);
+ vty_show_prefix_entry(vty, json_proto, afi, plist,
+ master, dtype, seqnum);
+ }
+
+ if (uj) {
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
}
return CMD_SUCCESS;
@@ -1150,19 +1254,21 @@ static int vty_clear_prefix_list(struct vty *vty, afi_t afi, const char *name,
DEFPY (show_ip_prefix_list,
show_ip_prefix_list_cmd,
- "show ip prefix-list [WORD [seq$dseq (1-4294967295)$arg]]",
+ "show ip prefix-list [WORD [seq$dseq (1-4294967295)$arg]] [json$uj]",
SHOW_STR
IP_STR
PREFIX_LIST_STR
"Name of a prefix list\n"
"sequence number of an entry\n"
- "Sequence number\n")
+ "Sequence number\n"
+ JSON_STR)
{
enum display_type dtype = normal_display;
if (dseq)
dtype = sequential_display;
- return vty_show_prefix_list(vty, AFI_IP, prefix_list, arg_str, dtype);
+ return vty_show_prefix_list(vty, AFI_IP, prefix_list, arg_str, dtype,
+ !!uj);
}
DEFPY (show_ip_prefix_list_prefix,
@@ -1188,28 +1294,30 @@ DEFPY (show_ip_prefix_list_prefix,
DEFPY (show_ip_prefix_list_summary,
show_ip_prefix_list_summary_cmd,
- "show ip prefix-list summary [WORD$prefix_list]",
+ "show ip prefix-list summary [WORD$prefix_list] [json$uj]",
SHOW_STR
IP_STR
PREFIX_LIST_STR
"Summary of prefix lists\n"
- "Name of a prefix list\n")
+ "Name of a prefix list\n"
+ JSON_STR)
{
return vty_show_prefix_list(vty, AFI_IP, prefix_list, NULL,
- summary_display);
+ summary_display, !!uj);
}
DEFPY (show_ip_prefix_list_detail,
show_ip_prefix_list_detail_cmd,
- "show ip prefix-list detail [WORD$prefix_list]",
+ "show ip prefix-list detail [WORD$prefix_list] [json$uj]",
SHOW_STR
IP_STR
PREFIX_LIST_STR
"Detail of prefix lists\n"
- "Name of a prefix list\n")
+ "Name of a prefix list\n"
+ JSON_STR)
{
return vty_show_prefix_list(vty, AFI_IP, prefix_list, NULL,
- detail_display);
+ detail_display, !!uj);
}
DEFPY (clear_ip_prefix_list,
@@ -1226,19 +1334,21 @@ DEFPY (clear_ip_prefix_list,
DEFPY (show_ipv6_prefix_list,
show_ipv6_prefix_list_cmd,
- "show ipv6 prefix-list [WORD [seq$dseq (1-4294967295)$arg]]",
+ "show ipv6 prefix-list [WORD [seq$dseq (1-4294967295)$arg]] [json$uj]",
SHOW_STR
IPV6_STR
PREFIX_LIST_STR
"Name of a prefix list\n"
"sequence number of an entry\n"
- "Sequence number\n")
+ "Sequence number\n"
+ JSON_STR)
{
enum display_type dtype = normal_display;
if (dseq)
dtype = sequential_display;
- return vty_show_prefix_list(vty, AFI_IP6, prefix_list, arg_str, dtype);
+ return vty_show_prefix_list(vty, AFI_IP6, prefix_list, arg_str, dtype,
+ !!uj);
}
DEFPY (show_ipv6_prefix_list_prefix,
@@ -1264,28 +1374,30 @@ DEFPY (show_ipv6_prefix_list_prefix,
DEFPY (show_ipv6_prefix_list_summary,
show_ipv6_prefix_list_summary_cmd,
- "show ipv6 prefix-list summary [WORD$prefix-list]",
+ "show ipv6 prefix-list summary [WORD$prefix-list] [json$uj]",
SHOW_STR
IPV6_STR
PREFIX_LIST_STR
"Summary of prefix lists\n"
- "Name of a prefix list\n")
+ "Name of a prefix list\n"
+ JSON_STR)
{
return vty_show_prefix_list(vty, AFI_IP6, prefix_list, NULL,
- summary_display);
+ summary_display, !!uj);
}
DEFPY (show_ipv6_prefix_list_detail,
show_ipv6_prefix_list_detail_cmd,
- "show ipv6 prefix-list detail [WORD$prefix-list]",
+ "show ipv6 prefix-list detail [WORD$prefix-list] [json$uj]",
SHOW_STR
IPV6_STR
PREFIX_LIST_STR
"Detail of prefix lists\n"
- "Name of a prefix list\n")
+ "Name of a prefix list\n"
+ JSON_STR)
{
return vty_show_prefix_list(vty, AFI_IP6, prefix_list, NULL,
- detail_display);
+ detail_display, !!uj);
}
DEFPY (clear_ipv6_prefix_list,
diff --git a/lib/routemap.c b/lib/routemap.c
index 9dc1c7c82d..5d45dc1047 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -22,6 +22,7 @@
#include "linklist.h"
#include "memory.h"
+#include "command.h"
#include "vector.h"
#include "prefix.h"
#include "vty.h"
@@ -32,6 +33,7 @@
#include "libfrr.h"
#include "lib_errors.h"
#include "table.h"
+#include "json.h"
DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP, "Route map");
DEFINE_MTYPE(LIB, ROUTE_MAP_NAME, "Route map name");
@@ -840,50 +842,140 @@ static const char *route_map_result_str(route_map_result_t res)
}
/* show route-map */
-static void vty_show_route_map_entry(struct vty *vty, struct route_map *map)
+static void vty_show_route_map_entry(struct vty *vty, struct route_map *map,
+ json_object *json)
{
struct route_map_index *index;
struct route_map_rule *rule;
-
- vty_out(vty, "route-map: %s Invoked: %" PRIu64 " Optimization: %s Processed Change: %s\n",
- map->name, map->applied - map->applied_clear,
- map->optimization_disabled ? "disabled" : "enabled",
- map->to_be_processed ? "true" : "false");
+ json_object *json_rmap = NULL;
+ json_object *json_rules = NULL;
+
+ if (json) {
+ json_rmap = json_object_new_object();
+ json_object_object_add(json, map->name, json_rmap);
+
+ json_rules = json_object_new_array();
+ json_object_int_add(json_rmap, "invoked",
+ map->applied - map->applied_clear);
+ json_object_boolean_add(json_rmap, "disabledOptimization",
+ map->optimization_disabled);
+ json_object_boolean_add(json_rmap, "processedChange",
+ map->to_be_processed);
+ json_object_object_add(json_rmap, "rules", json_rules);
+ } else {
+ vty_out(vty,
+ "route-map: %s Invoked: %" PRIu64
+ " Optimization: %s Processed Change: %s\n",
+ map->name, map->applied - map->applied_clear,
+ map->optimization_disabled ? "disabled" : "enabled",
+ map->to_be_processed ? "true" : "false");
+ }
for (index = map->head; index; index = index->next) {
- vty_out(vty, " %s, sequence %d Invoked %" PRIu64 "\n",
- route_map_type_str(index->type), index->pref,
- index->applied - index->applied_clear);
-
- /* Description */
- if (index->description)
- vty_out(vty, " Description:\n %s\n",
- index->description);
-
- /* Match clauses */
- vty_out(vty, " Match clauses:\n");
- for (rule = index->match_list.head; rule; rule = rule->next)
- vty_out(vty, " %s %s\n", rule->cmd->str,
- rule->rule_str);
-
- vty_out(vty, " Set clauses:\n");
- for (rule = index->set_list.head; rule; rule = rule->next)
- vty_out(vty, " %s %s\n", rule->cmd->str,
- rule->rule_str);
-
- /* Call clause */
- vty_out(vty, " Call clause:\n");
- if (index->nextrm)
- vty_out(vty, " Call %s\n", index->nextrm);
-
- /* Exit Policy */
- vty_out(vty, " Action:\n");
- if (index->exitpolicy == RMAP_GOTO)
- vty_out(vty, " Goto %d\n", index->nextpref);
- else if (index->exitpolicy == RMAP_NEXT)
- vty_out(vty, " Continue to next entry\n");
- else if (index->exitpolicy == RMAP_EXIT)
- vty_out(vty, " Exit routemap\n");
+ if (json) {
+ json_object *json_rule;
+ json_object *json_matches;
+ json_object *json_sets;
+ char action[BUFSIZ] = {};
+
+ json_rule = json_object_new_object();
+ json_object_array_add(json_rules, json_rule);
+
+ json_object_int_add(json_rule, "sequenceNumber",
+ index->pref);
+ json_object_string_add(json_rule, "type",
+ route_map_type_str(index->type));
+ json_object_int_add(json_rule, "invoked",
+ index->applied
+ - index->applied_clear);
+
+ /* Description */
+ if (index->description)
+ json_object_string_add(json_rule, "description",
+ index->description);
+
+ /* Match clauses */
+ json_matches = json_object_new_array();
+ json_object_object_add(json_rule, "matchClauses",
+ json_matches);
+ for (rule = index->match_list.head; rule;
+ rule = rule->next) {
+ char buf[BUFSIZ];
+
+ snprintf(buf, sizeof(buf), "%s %s",
+ rule->cmd->str, rule->rule_str);
+ json_array_string_add(json_matches, buf);
+ }
+
+ /* Set clauses */
+ json_sets = json_object_new_array();
+ json_object_object_add(json_rule, "setClauses",
+ json_sets);
+ for (rule = index->set_list.head; rule;
+ rule = rule->next) {
+ char buf[BUFSIZ];
+
+ snprintf(buf, sizeof(buf), "%s %s",
+ rule->cmd->str, rule->rule_str);
+ json_array_string_add(json_sets, buf);
+ }
+
+ /* Call clause */
+ if (index->nextrm)
+ json_object_string_add(json_rule, "callClause",
+ index->nextrm);
+
+ /* Exit Policy */
+ if (index->exitpolicy == RMAP_GOTO)
+ snprintf(action, sizeof(action), "Goto %d",
+ index->nextpref);
+ else if (index->exitpolicy == RMAP_NEXT)
+ snprintf(action, sizeof(action),
+ "Continue to next entry");
+ else if (index->exitpolicy == RMAP_EXIT)
+ snprintf(action, sizeof(action),
+ "Exit routemap");
+ if (action[0] != '\0')
+ json_object_string_add(json_rule, "action",
+ action);
+ } else {
+ vty_out(vty, " %s, sequence %d Invoked %" PRIu64 "\n",
+ route_map_type_str(index->type), index->pref,
+ index->applied - index->applied_clear);
+
+ /* Description */
+ if (index->description)
+ vty_out(vty, " Description:\n %s\n",
+ index->description);
+
+ /* Match clauses */
+ vty_out(vty, " Match clauses:\n");
+ for (rule = index->match_list.head; rule;
+ rule = rule->next)
+ vty_out(vty, " %s %s\n", rule->cmd->str,
+ rule->rule_str);
+
+ /* Set clauses */
+ vty_out(vty, " Set clauses:\n");
+ for (rule = index->set_list.head; rule;
+ rule = rule->next)
+ vty_out(vty, " %s %s\n", rule->cmd->str,
+ rule->rule_str);
+
+ /* Call clause */
+ vty_out(vty, " Call clause:\n");
+ if (index->nextrm)
+ vty_out(vty, " Call %s\n", index->nextrm);
+
+ /* Exit Policy */
+ vty_out(vty, " Action:\n");
+ if (index->exitpolicy == RMAP_GOTO)
+ vty_out(vty, " Goto %d\n", index->nextpref);
+ else if (index->exitpolicy == RMAP_NEXT)
+ vty_out(vty, " Continue to next entry\n");
+ else if (index->exitpolicy == RMAP_EXIT)
+ vty_out(vty, " Exit routemap\n");
+ }
}
}
@@ -895,22 +987,28 @@ static int sort_route_map(const void **map1, const void **map2)
return strcmp(m1->name, m2->name);
}
-static int vty_show_route_map(struct vty *vty, const char *name)
+static int vty_show_route_map(struct vty *vty, const char *name, bool use_json)
{
struct route_map *map;
+ json_object *json = NULL;
+ json_object *json_proto = NULL;
- vty_out(vty, "%s:\n", frr_protonameinst);
+ if (use_json) {
+ json = json_object_new_object();
+ json_proto = json_object_new_object();
+ json_object_object_add(json, frr_protonameinst, json_proto);
+ } else
+ vty_out(vty, "%s:\n", frr_protonameinst);
if (name) {
map = route_map_lookup_by_name(name);
if (map) {
- vty_show_route_map_entry(vty, map);
+ vty_show_route_map_entry(vty, map, json_proto);
return CMD_SUCCESS;
- } else {
+ } else if (!use_json) {
vty_out(vty, "%s: 'route-map %s' not found\n",
frr_protonameinst, name);
- return CMD_SUCCESS;
}
} else {
@@ -923,10 +1021,18 @@ static int vty_show_route_map(struct vty *vty, const char *name)
list_sort(maplist, sort_route_map);
for (ALL_LIST_ELEMENTS_RO(maplist, ln, map))
- vty_show_route_map_entry(vty, map);
+ vty_show_route_map_entry(vty, map, json_proto);
list_delete(&maplist);
}
+
+ if (use_json) {
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+
return CMD_SUCCESS;
}
@@ -950,7 +1056,7 @@ static int vty_show_unused_route_map(struct vty *vty)
list_sort(maplist, sort_route_map);
for (ALL_LIST_ELEMENTS_RO(maplist, ln, map))
- vty_show_route_map_entry(vty, map);
+ vty_show_route_map_entry(vty, map, NULL);
} else {
vty_out(vty, "\n%s: None\n", frr_protonameinst);
}
@@ -2957,14 +3063,20 @@ DEFUN (rmap_clear_counters,
DEFUN (rmap_show_name,
rmap_show_name_cmd,
- "show route-map [WORD]",
+ "show route-map [WORD] [json]",
SHOW_STR
"route-map information\n"
- "route-map name\n")
+ "route-map name\n"
+ JSON_STR)
{
- int idx_word = 2;
- const char *name = (argc == 3) ? argv[idx_word]->arg : NULL;
- return vty_show_route_map(vty, name);
+ bool uj = use_json(argc, argv);
+ int idx = 0;
+ const char *name = NULL;
+
+ if (argv_find(argv, argc, "WORD", &idx))
+ name = argv[idx]->arg;
+
+ return vty_show_route_map(vty, name, uj);
}
DEFUN (rmap_show_unused,
diff --git a/lib/vrf.c b/lib/vrf.c
index 03d9a62c0f..815c0fcba2 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -287,6 +287,7 @@ void vrf_delete(struct vrf *vrf)
RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf);
vrf->vrf_id = VRF_UNKNOWN;
}
+ vrf->ns_ctxt = NULL;
return;
}