summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/buffer.c24
-rw-r--r--lib/command.c33
-rw-r--r--lib/command.h2
-rw-r--r--lib/command_match.c4
-rw-r--r--lib/compiler.h23
-rw-r--r--lib/csv.c3
-rw-r--r--lib/filter.c267
-rw-r--r--lib/frr_zmq.c24
-rw-r--r--lib/frrlua.c118
-rw-r--r--lib/frrlua.h25
-rw-r--r--lib/frrscript.c215
-rw-r--r--lib/frrscript.h182
-rw-r--r--lib/if.c12
-rw-r--r--lib/if.h3
-rw-r--r--lib/libfrr.c6
-rw-r--r--lib/link_state.c12
-rw-r--r--lib/network.c3
-rw-r--r--lib/ntop.c14
-rw-r--r--lib/pbr.h6
-rw-r--r--lib/plist.c300
-rw-r--r--lib/plist.h15
-rw-r--r--lib/prefix.c20
-rw-r--r--lib/prefix.h42
-rw-r--r--lib/route_opaque.h8
-rw-r--r--lib/route_types.txt4
-rw-r--r--lib/routemap.c214
-rw-r--r--lib/routemap.h1
-rw-r--r--lib/routemap_cli.c5
-rw-r--r--lib/sockopt.c16
-rw-r--r--lib/sockunion.c3
-rw-r--r--lib/sockunion.h1
-rw-r--r--lib/stream.c8
-rw-r--r--lib/subdir.am17
-rw-r--r--lib/systemd.c173
-rw-r--r--lib/systemd.h10
-rw-r--r--lib/table.c4
-rw-r--r--lib/vrf.c1
-rw-r--r--lib/zclient.c14
38 files changed, 1342 insertions, 490 deletions
diff --git a/lib/buffer.c b/lib/buffer.c
index 7929b3709d..41b1adc9fc 100644
--- a/lib/buffer.c
+++ b/lib/buffer.c
@@ -357,7 +357,8 @@ buffer_status_t buffer_flush_window(struct buffer *b, int fd, int width,
iov_size =
((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
- if ((nbytes = writev(fd, c_iov, iov_size)) < 0) {
+ nbytes = writev(fd, c_iov, iov_size);
+ if (nbytes < 0) {
flog_err(EC_LIB_SOCKET,
"%s: writev to fd %d failed: %s",
__func__, fd, safe_strerror(errno));
@@ -370,7 +371,8 @@ buffer_status_t buffer_flush_window(struct buffer *b, int fd, int width,
}
}
#else /* IOV_MAX */
- if ((nbytes = writev(fd, iov, iov_index)) < 0)
+ nbytes = writev(fd, iov, iov_index);
+ if (nbytes < 0)
flog_err(EC_LIB_SOCKET, "%s: writev to fd %d failed: %s",
__func__, fd, safe_strerror(errno));
#endif /* IOV_MAX */
@@ -472,13 +474,17 @@ buffer_status_t buffer_write(struct buffer *b, int fd, const void *p,
/* Buffer is not empty, so do not attempt to write the new data.
*/
nbytes = 0;
- else if ((nbytes = write(fd, p, size)) < 0) {
- if (ERRNO_IO_RETRY(errno))
- nbytes = 0;
- else {
- flog_err(EC_LIB_SOCKET, "%s: write error on fd %d: %s",
- __func__, fd, safe_strerror(errno));
- return BUFFER_ERROR;
+ else {
+ nbytes = write(fd, p, size);
+ if (nbytes < 0) {
+ if (ERRNO_IO_RETRY(errno))
+ nbytes = 0;
+ else {
+ flog_err(EC_LIB_SOCKET,
+ "%s: write error on fd %d: %s",
+ __func__, fd, safe_strerror(errno));
+ return BUFFER_ERROR;
+ }
}
}
/* Add any remaining data to the buffer. */
diff --git a/lib/command.c b/lib/command.c
index 560d4a09f8..422544b70b 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -160,6 +160,9 @@ static bool vty_check_node_for_xpath_decrement(enum node_type target_node,
|| node == BGP_FLOWSPECV6_NODE))
return false;
+ if (target_node == INTERFACE_NODE && node == LINK_PARAMS_NODE)
+ return false;
+
return true;
}
@@ -2419,26 +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_new(argv[1]->arg);
- 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);
+ 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/command.h b/lib/command.h
index 13bd61e9fd..2b50bc2374 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -438,6 +438,8 @@ struct cmd_node {
#define BFD_PROFILE_STR "BFD profile.\n"
#define BFD_PROFILE_NAME_STR "BFD profile name.\n"
#define SHARP_STR "Sharp Routing Protocol\n"
+#define OSPF_GR_STR \
+ "OSPF non-stop forwarding (NSF) also known as OSPF Graceful Restart\n"
#define CMD_VNI_RANGE "(1-16777215)"
#define CONF_BACKUP_EXT ".sav"
diff --git a/lib/command_match.c b/lib/command_match.c
index e9e8466ffd..5703510148 100644
--- a/lib/command_match.c
+++ b/lib/command_match.c
@@ -813,7 +813,7 @@ static enum match_type match_ipv4_prefix(const char *str)
str++;
}
- if (atoi(sp) > 32)
+ if (atoi(sp) > IPV4_MAX_BITLEN)
return no_match;
return exact_match;
@@ -948,7 +948,7 @@ static enum match_type match_ipv6_prefix(const char *str, bool prefix)
if (*endptr != '\0')
return no_match;
- if (mask < 0 || mask > 128)
+ if (mask < 0 || mask > IPV6_MAX_BITLEN)
return no_match;
return exact_match;
diff --git a/lib/compiler.h b/lib/compiler.h
index 970ed297fc..bf443906eb 100644
--- a/lib/compiler.h
+++ b/lib/compiler.h
@@ -173,6 +173,29 @@ extern "C" {
#define MACRO_REPEAT(NAME, ...) \
MACRO_VARIANT(_MACRO_REPEAT, ##__VA_ARGS__)(NAME, ##__VA_ARGS__)
+/* per-arglist repeat macro, use like this:
+ * #define foo(...) MAP_LISTS(F, ##__VA_ARGS__)
+ * where F is a n-ary function where n is the number of args in each arglist.
+ * e.g.: MAP_LISTS(f, (a, b), (c, d))
+ * expands to: f(a, b); f(c, d)
+ */
+
+#define ESC(...) __VA_ARGS__
+#define MAP_LISTS(M, ...) \
+ _CONCAT(_MAP_LISTS_, PP_NARG(__VA_ARGS__))(M, ##__VA_ARGS__)
+#define _MAP_LISTS_0(M)
+#define _MAP_LISTS_1(M, _1) ESC(M _1)
+#define _MAP_LISTS_2(M, _1, _2) ESC(M _1; M _2)
+#define _MAP_LISTS_3(M, _1, _2, _3) ESC(M _1; M _2; M _3)
+#define _MAP_LISTS_4(M, _1, _2, _3, _4) ESC(M _1; M _2; M _3; M _4)
+#define _MAP_LISTS_5(M, _1, _2, _3, _4, _5) ESC(M _1; M _2; M _3; M _4; M _5)
+#define _MAP_LISTS_6(M, _1, _2, _3, _4, _5, _6) \
+ ESC(M _1; M _2; M _3; M _4; M _5; M _6)
+#define _MAP_LISTS_7(M, _1, _2, _3, _4, _5, _6, _7) \
+ ESC(M _1; M _2; M _3; M _4; M _5; M _6; M _7)
+#define _MAP_LISTS_8(M, _1, _2, _3, _4, _5, _6, _7, _8) \
+ ESC(M _1; M _2; M _3; M _4; M _5; M _6; M _7; M _8)
+
/*
* for warnings on macros, put in the macro content like this:
* #define MACRO BLA CPP_WARN("MACRO has been deprecated")
diff --git a/lib/csv.c b/lib/csv.c
index b48b79792e..05b9dbe39e 100644
--- a/lib/csv.c
+++ b/lib/csv.c
@@ -641,7 +641,8 @@ static int get_memory_usage(pid_t pid)
char *vm;
snprintf(status_child, sizeof(status_child), "/proc/%d/status", pid);
- if ((fd = open(status_child, O_RDONLY)) < 0)
+ fd = open(status_child, O_RDONLY);
+ if (fd < 0)
return -1;
read(fd, buf, 4095);
diff --git a/lib/filter.c b/lib/filter.c
index b7a935d076..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");
@@ -108,10 +109,14 @@ static int filter_match_cisco(struct filter *mfilter, const struct prefix *p)
masklen2ip(p->prefixlen, &mask);
check_mask = mask.s_addr & ~filter->mask_mask.s_addr;
- if (memcmp(&check_addr, &filter->addr.s_addr, 4) == 0
- && memcmp(&check_mask, &filter->mask.s_addr, 4) == 0)
+ if (memcmp(&check_addr, &filter->addr.s_addr, IPV4_MAX_BYTELEN)
+ == 0
+ && memcmp(&check_mask, &filter->mask.s_addr,
+ IPV4_MAX_BYTELEN)
+ == 0)
return 1;
- } else if (memcmp(&check_addr, &filter->addr.s_addr, 4) == 0)
+ } else if (memcmp(&check_addr, &filter->addr.s_addr, IPV4_MAX_BYTELEN)
+ == 0)
return 1;
return 0;
@@ -439,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;
}
@@ -515,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,
@@ -526,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"
@@ -549,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");
@@ -603,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;
@@ -624,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 d8aaa3aa3c..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 */
/*
@@ -52,10 +54,9 @@ int frrlua_table_get_integer(lua_State *L, const char *key)
}
/*
- * Encoders.
- *
* This section has functions that convert internal FRR datatypes into Lua
- * datatypes.
+ * datatypes: one encoder function and two decoder functions for each type.
+ *
*/
void lua_pushprefix(lua_State *L, const struct prefix *prefix)
@@ -71,14 +72,19 @@ void lua_pushprefix(lua_State *L, const struct prefix *prefix)
lua_setfield(L, -2, "family");
}
-void *lua_toprefix(lua_State *L, int idx)
+void lua_decode_prefix(lua_State *L, int idx, struct prefix *prefix)
{
- struct prefix *p = XCALLOC(MTYPE_TMP, sizeof(struct prefix));
-
lua_getfield(L, idx, "network");
- (void)str2prefix(lua_tostring(L, -1), p);
+ (void)str2prefix(lua_tostring(L, -1), prefix);
lua_pop(L, 1);
+ /* pop the table */
+ lua_pop(L, 1);
+}
+void *lua_toprefix(lua_State *L, int idx)
+{
+ struct prefix *p = XCALLOC(MTYPE_SCRIPT_RES, sizeof(struct prefix));
+ lua_decode_prefix(L, idx, p);
return p;
}
@@ -109,10 +115,8 @@ void lua_pushinterface(lua_State *L, const struct interface *ifp)
lua_setfield(L, -2, "linklayer_type");
}
-void *lua_tointerface(lua_State *L, int idx)
+void lua_decode_interface(lua_State *L, int idx, struct interface *ifp)
{
- 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);
@@ -146,13 +150,22 @@ void *lua_tointerface(lua_State *L, int idx)
lua_getfield(L, idx, "linklayer_type");
ifp->ll_type = lua_tointeger(L, -1);
lua_pop(L, 1);
+ /* pop the table */
+ lua_pop(L, 1);
+}
+void *lua_tointerface(lua_State *L, int idx)
+{
+ struct interface *ifp =
+ XCALLOC(MTYPE_SCRIPT_RES, sizeof(struct interface));
+ lua_decode_interface(L, idx, ifp);
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);
@@ -162,14 +175,20 @@ void lua_pushinaddr(lua_State *L, const struct in_addr *addr)
lua_setfield(L, -2, "string");
}
-void *lua_toinaddr(lua_State *L, int idx)
+void lua_decode_inaddr(lua_State *L, int idx, struct in_addr *inaddr)
{
- 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);
+ /* pop the table */
+ lua_pop(L, 1);
+}
+void *lua_toinaddr(lua_State *L, int idx)
+{
+ struct in_addr *inaddr =
+ XCALLOC(MTYPE_SCRIPT_RES, sizeof(struct in_addr));
+ lua_decode_inaddr(L, idx, inaddr);
return inaddr;
}
@@ -177,6 +196,7 @@ void *lua_toinaddr(lua_State *L, int idx)
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);
@@ -186,20 +206,27 @@ void lua_pushin6addr(lua_State *L, const struct in6_addr *addr)
lua_setfield(L, -2, "string");
}
-void *lua_toin6addr(lua_State *L, int idx)
+void lua_decode_in6addr(lua_State *L, int idx, struct in6_addr *in6addr)
{
- 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);
+ /* pop the table */
+ lua_pop(L, 1);
+}
+void *lua_toin6addr(lua_State *L, int idx)
+{
+ struct in6_addr *in6addr =
+ XCALLOC(MTYPE_SCRIPT_RES, sizeof(struct in6_addr));
+ lua_decode_in6addr(L, idx, in6addr);
return in6addr;
}
void lua_pushsockunion(lua_State *L, const union sockunion *su)
{
char buf[SU_ADDRSTRLEN];
+
sockunion2str(su, buf, sizeof(buf));
lua_newtable(L);
@@ -210,13 +237,21 @@ void lua_pushsockunion(lua_State *L, const union sockunion *su)
lua_setfield(L, -2, "string");
}
-void *lua_tosockunion(lua_State *L, int idx)
+void lua_decode_sockunion(lua_State *L, int idx, union sockunion *su)
{
- union sockunion *su = XCALLOC(MTYPE_TMP, sizeof(union sockunion));
-
lua_getfield(L, idx, "string");
str2sockunion(lua_tostring(L, -1), su);
+ lua_pop(L, 1);
+ /* pop the table */
+ lua_pop(L, 1);
+}
+void *lua_tosockunion(lua_State *L, int idx)
+{
+ union sockunion *su =
+ XCALLOC(MTYPE_SCRIPT_RES, sizeof(union sockunion));
+
+ lua_decode_sockunion(L, idx, su);
return su;
}
@@ -225,12 +260,17 @@ void lua_pushtimet(lua_State *L, const time_t *time)
lua_pushinteger(L, *time);
}
-void *lua_totimet(lua_State *L, int idx)
+void lua_decode_timet(lua_State *L, int idx, time_t *t)
{
- time_t *t = XCALLOC(MTYPE_TMP, sizeof(time_t));
-
*t = lua_tointeger(L, idx);
+ lua_pop(L, 1);
+}
+void *lua_totimet(lua_State *L, int idx)
+{
+ time_t *t = XCALLOC(MTYPE_SCRIPT_RES, sizeof(time_t));
+
+ lua_decode_timet(L, idx, t);
return t;
}
@@ -239,25 +279,51 @@ void lua_pushintegerp(lua_State *L, const long long *num)
lua_pushinteger(L, *num);
}
-void *lua_tointegerp(lua_State *L, int idx)
+void lua_decode_integerp(lua_State *L, int idx, long long *num)
{
int isnum;
- long long *num = XCALLOC(MTYPE_TMP, sizeof(long long));
-
*num = lua_tonumberx(L, idx, &isnum);
+ lua_pop(L, 1);
assert(isnum);
+}
+void *lua_tointegerp(lua_State *L, int idx)
+{
+ long long *num = XCALLOC(MTYPE_SCRIPT_RES, sizeof(long long));
+
+ lua_decode_integerp(L, idx, num);
return num;
}
+void lua_decode_stringp(lua_State *L, int idx, char *str)
+{
+ strlcpy(str, lua_tostring(L, idx), strlen(str) + 1);
+ lua_pop(L, 1);
+}
+
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;
}
/*
+ * Decoder for const values, since we cannot modify them.
+ */
+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.
*
* Lua-compatible wrappers for FRR logging functions.
diff --git a/lib/frrlua.h b/lib/frrlua.h
index 6fb30938b0..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
@@ -50,6 +52,8 @@ static inline void lua_pushstring_wrapper(lua_State *L, const char *str)
*/
void lua_pushprefix(lua_State *L, const struct prefix *prefix);
+void lua_decode_prefix(lua_State *L, int idx, struct prefix *prefix);
+
/*
* Converts the Lua value at idx to a prefix.
*
@@ -63,6 +67,8 @@ void *lua_toprefix(lua_State *L, int idx);
*/
void lua_pushinterface(lua_State *L, const struct interface *ifp);
+void lua_decode_interface(lua_State *L, int idx, struct interface *ifp);
+
/*
* Converts the Lua value at idx to an interface.
*
@@ -77,6 +83,8 @@ void *lua_tointerface(lua_State *L, int idx);
*/
void lua_pushinaddr(lua_State *L, const struct in_addr *addr);
+void lua_decode_inaddr(lua_State *L, int idx, struct in_addr *addr);
+
/*
* Converts the Lua value at idx to an in_addr.
*
@@ -90,6 +98,8 @@ void *lua_toinaddr(lua_State *L, int idx);
*/
void lua_pushin6addr(lua_State *L, const struct in6_addr *addr);
+void lua_decode_in6addr(lua_State *L, int idx, struct in6_addr *addr);
+
/*
* Converts the Lua value at idx to an in6_addr.
*
@@ -103,6 +113,8 @@ void *lua_toin6addr(lua_State *L, int idx);
*/
void lua_pushtimet(lua_State *L, const time_t *time);
+void lua_decode_timet(lua_State *L, int idx, time_t *time);
+
/*
* Converts the Lua value at idx to a time_t.
*
@@ -116,6 +128,8 @@ void *lua_totimet(lua_State *L, int idx);
*/
void lua_pushsockunion(lua_State *L, const union sockunion *su);
+void lua_decode_sockunion(lua_State *L, int idx, union sockunion *su);
+
/*
* Converts the Lua value at idx to a sockunion.
*
@@ -129,6 +143,8 @@ void *lua_tosockunion(lua_State *L, int idx);
*/
void lua_pushintegerp(lua_State *L, const long long *num);
+void lua_decode_integerp(lua_State *L, int idx, long long *num);
+
/*
* Converts the Lua value at idx to an int.
*
@@ -137,6 +153,8 @@ void lua_pushintegerp(lua_State *L, const long long *num);
*/
void *lua_tointegerp(lua_State *L, int idx);
+void lua_decode_stringp(lua_State *L, int idx, char *str);
+
/*
* Pop string.
*
@@ -146,6 +164,13 @@ void *lua_tointegerp(lua_State *L, int idx);
void *lua_tostringp(lua_State *L, int idx);
/*
+ * 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.
*
* key
diff --git a/lib/frrscript.c b/lib/frrscript.c
index 10d400886d..d00b84ccbb 100644
--- a/lib/frrscript.c
+++ b/lib/frrscript.c
@@ -102,83 +102,136 @@ static void codec_free(struct codec *c)
}
#endif
-/* Generic script APIs */
+/* Lua function hash utils */
-int frrscript_call(struct frrscript *fs, struct frrscript_env *env)
+unsigned int lua_function_hash_key(const void *data)
{
- struct frrscript_codec c = {};
- const void *arg;
- const char *bindname;
+ const struct lua_function_state *lfs = data;
- /* 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;
+ return string_hash_make(lfs->name);
+}
- struct frrscript_codec *codec = hash_lookup(codec_hash, &c);
- assert(codec && "No encoder for type");
- codec->encoder(fs->L, arg);
+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;
- lua_setglobal(fs->L, bindname);
- }
+ 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);
+}
- int ret = lua_pcall(fs->L, 0, 0, 0);
+/* 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)
@@ -199,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 *))
+{
+
+ /* Set up the Lua script */
+ lua_State *L = luaL_newstate();
- char fname[MAXPATHLEN * 2];
- snprintf(fname, sizeof(fname), "%s/%s.lua", scriptdir, fs->name);
+ frrlua_export_logging(L);
- int ret = luaL_loadfile(fs->L, fname);
+ 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 f4057f531b..540676c099 100644
--- a/lib/frrscript.h
+++ b/lib/frrscript.h
@@ -25,6 +25,7 @@
#include <lua.h>
#include "frrlua.h"
+#include "bgpd/bgp_script.h" // for peer and attr encoders/decoders
#ifdef __cplusplus
extern "C" {
@@ -39,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;
@@ -59,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_new(const char *scriptname);
+
+/*
+ * Load a function into frrscript, run callback if any
*/
-struct frrscript *frrscript_load(const char *name,
- int (*load_cb)(struct frrscript *));
+int frrscript_load(struct frrscript *fs, const char *function_name,
+ int (*load_cb)(struct frrscript *));
/*
- * Destroy FRR script.
+ * Delete Lua function states and frrscript
*/
-void frrscript_unload(struct frrscript *fs);
+void frrscript_delete(struct frrscript *fs);
/*
* Register a Lua codec for a type.
@@ -96,38 +122,150 @@ void frrscript_register_type_codecs(struct frrscript_codec *codecs);
*/
void frrscript_init(const char *scriptdir);
+/*
+ * 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))
/*
- * Call script.
+ * 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.
*
- * fs
- * The script to call; this is obtained from frrscript_load().
+ * At the end, the only thing left on the stack should be the
+ * returned table.
+ */
+#define DECODE_ARGS(name, value) \
+ do { \
+ 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)
+
+/*
+ * Maps the type of value to its encoder/decoder.
+ * Add new mappings here.
+ *
+ * L
+ * Lua state
+ * scriptdir
+ * Directory in which to look for scripts
+ */
+#define ENCODE_ARGS_WITH_STATE(L, value) \
+ _Generic((value), \
+int : lua_pushinteger, \
+long long * : lua_pushintegerp, \
+struct prefix * : lua_pushprefix, \
+struct interface * : lua_pushinterface, \
+struct in_addr * : lua_pushinaddr, \
+struct in6_addr * : lua_pushin6addr, \
+union sockunion * : lua_pushsockunion, \
+time_t * : lua_pushtimet, \
+char * : lua_pushstring_wrapper, \
+struct attr * : lua_pushattr, \
+struct peer * : lua_pushpeer, \
+const struct prefix * : lua_pushprefix \
+)((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, \
+struct in_addr * : lua_decode_inaddr, \
+struct in6_addr * : lua_decode_in6addr, \
+union sockunion * : lua_decode_sockunion, \
+time_t * : lua_decode_timet, \
+char * : lua_decode_stringp, \
+struct attr * : lua_decode_attr, \
+struct peer * : lua_decode_noop, \
+const struct prefix * : lua_decode_noop \
+)((L), -1, (value))
+
+/*
+ * Call Lua function state (abstraction for a single Lua function)
*
- * env
- * The script's environment. Specify this as an array of frrscript_env.
+ * 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, struct frrscript_env *env);
+int _frrscript_call_lua(struct lua_function_state *lfs, int nargs);
+/*
+ * Wrapper for calling Lua function state. Maps values passed in to their
+ * encoder and decoder types.
+ *
+ * fs
+ * 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, 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/libfrr.c b/lib/libfrr.c
index 0817182f7a..97dab74d9b 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -44,6 +44,7 @@
#include "frr_pthread.h"
#include "defaults.h"
#include "frrscript.h"
+#include "systemd.h"
DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm));
DEFINE_HOOK(frr_config_pre, (struct thread_master * tm), (tm));
@@ -363,6 +364,11 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv)
startup_fds |= UINT64_C(0x1) << (uint64_t)i;
}
+
+ /* note this doesn't do anything, it just grabs state, so doing it
+ * early in _preinit is perfect.
+ */
+ systemd_init_env();
}
bool frr_is_startup_fd(int fd)
diff --git a/lib/link_state.c b/lib/link_state.c
index e8a6b89f89..062384aac7 100644
--- a/lib/link_state.c
+++ b/lib/link_state.c
@@ -538,8 +538,6 @@ struct ls_edge *ls_edge_add(struct ls_ted *ted,
/* Create Edge and add it to the TED */
new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_edge));
- if (!new)
- return NULL;
new->attributes = attributes;
new->key = key;
@@ -804,8 +802,6 @@ struct ls_ted *ls_ted_new(const uint32_t key, const char *name,
struct ls_ted *new;
new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_ted));
- if (new == NULL)
- return new;
/* Set basic information for this ted */
new->key = key;
@@ -1005,8 +1001,6 @@ static struct ls_node *ls_parse_node(struct stream *s)
size_t len;
node = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_node));
- if (node == NULL)
- return NULL;
STREAM_GET(&node->adv, s, sizeof(struct ls_node_id));
STREAM_GETW(s, node->flags);
@@ -1051,8 +1045,6 @@ static struct ls_attributes *ls_parse_attributes(struct stream *s)
size_t len;
attr = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_attributes));
- if (attr == NULL)
- return NULL;
attr->srlgs = NULL;
STREAM_GET(&attr->adv, s, sizeof(struct ls_node_id));
@@ -1157,8 +1149,6 @@ static struct ls_prefix *ls_parse_prefix(struct stream *s)
size_t len;
ls_pref = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_prefix));
- if (ls_pref == NULL)
- return NULL;
STREAM_GET(&ls_pref->adv, s, sizeof(struct ls_node_id));
STREAM_GETW(s, ls_pref->flags);
@@ -1193,8 +1183,6 @@ struct ls_message *ls_parse_msg(struct stream *s)
struct ls_message *msg;
msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message));
- if (msg == NULL)
- return NULL;
/* Read LS Message header */
STREAM_GETC(s, msg->event);
diff --git a/lib/network.c b/lib/network.c
index 411661a5e1..b60ad9a57c 100644
--- a/lib/network.c
+++ b/lib/network.c
@@ -78,7 +78,8 @@ int set_nonblocking(int fd)
/* According to the Single UNIX Spec, the return value for F_GETFL
should
never be negative. */
- if ((flags = fcntl(fd, F_GETFL)) < 0) {
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0) {
flog_err(EC_LIB_SYSTEM_CALL,
"fcntl(F_GETFL) failed for fd %d: %s", fd,
safe_strerror(errno));
diff --git a/lib/ntop.c b/lib/ntop.c
index ccbf8793d3..1b2dd7a6d1 100644
--- a/lib/ntop.c
+++ b/lib/ntop.c
@@ -40,14 +40,18 @@ static inline void putbyte(uint8_t bytex, char **posx)
bool zero = false;
int byte = bytex, tmp, a, b;
- if ((tmp = byte - 200) >= 0) {
+ tmp = byte - 200;
+ if (tmp >= 0) {
*pos++ = '2';
zero = true;
byte = tmp;
- } else if ((tmp = byte - 100) >= 0) {
- *pos++ = '1';
- zero = true;
- byte = tmp;
+ } else {
+ tmp = byte - 100;
+ if (tmp >= 0) {
+ *pos++ = '1';
+ zero = true;
+ byte = tmp;
+ }
}
/* make sure the compiler knows the value range of "byte" */
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 0ee02f8a0b..2f9f06f43b 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -750,7 +750,7 @@ static const char *prefix_list_type_str(struct prefix_list_entry *pentry)
}
static int prefix_list_entry_match(struct prefix_list_entry *pentry,
- const struct prefix *p)
+ const struct prefix *p, bool address_mode)
{
int ret;
@@ -761,6 +761,9 @@ static int prefix_list_entry_match(struct prefix_list_entry *pentry,
if (!ret)
return 0;
+ if (address_mode)
+ return 1;
+
/* In case of le nor ge is specified, exact match is performed. */
if (!pentry->le && !pentry->ge) {
if (pentry->prefix.prefixlen != p->prefixlen)
@@ -777,14 +780,15 @@ static int prefix_list_entry_match(struct prefix_list_entry *pentry,
return 1;
}
-enum prefix_list_type prefix_list_apply_which_prefix(
+enum prefix_list_type prefix_list_apply_ext(
struct prefix_list *plist,
- const struct prefix **which,
- const void *object)
+ const struct prefix_list_entry **which,
+ union prefixconstptr object,
+ bool address_mode)
{
struct prefix_list_entry *pentry, *pbest = NULL;
- const struct prefix *p = (const struct prefix *)object;
+ const struct prefix *p = object.p;
const uint8_t *byte = p->u.val;
size_t depth;
size_t validbits = p->prefixlen;
@@ -809,7 +813,7 @@ enum prefix_list_type prefix_list_apply_which_prefix(
pentry = pentry->next_best) {
if (pbest && pbest->seq < pentry->seq)
continue;
- if (prefix_list_entry_match(pentry, p))
+ if (prefix_list_entry_match(pentry, p, address_mode))
pbest = pentry;
}
@@ -830,7 +834,7 @@ enum prefix_list_type prefix_list_apply_which_prefix(
pentry = pentry->next_best) {
if (pbest && pbest->seq < pentry->seq)
continue;
- if (prefix_list_entry_match(pentry, p))
+ if (prefix_list_entry_match(pentry, p, address_mode))
pbest = pentry;
}
break;
@@ -838,7 +842,7 @@ enum prefix_list_type prefix_list_apply_which_prefix(
if (which) {
if (pbest)
- *which = &pbest->prefix;
+ *which = pbest;
else
*which = NULL;
}
@@ -878,7 +882,7 @@ static void __attribute__((unused)) prefix_list_print(struct prefix_list *plist)
}
}
-/* Retrun 1 when plist already include pentry policy. */
+/* Return 1 when plist already include pentry policy. */
static struct prefix_list_entry *
prefix_entry_dup_check(struct prefix_list *plist, struct prefix_list_entry *new)
{
@@ -928,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;
@@ -1146,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,
@@ -1184,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,
@@ -1222,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,
@@ -1260,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,
@@ -1296,6 +1412,51 @@ DEFPY (clear_ipv6_prefix_list,
return vty_clear_prefix_list(vty, AFI_IP6, prefix_list, prefix_str);
}
+DEFPY (debug_prefix_list_match,
+ debug_prefix_list_match_cmd,
+ "debug prefix-list WORD$prefix-list match <A.B.C.D/M|X:X::X:X/M>"
+ " [address-mode$addr_mode]",
+ DEBUG_STR
+ "Prefix-list test access\n"
+ "Name of a prefix list\n"
+ "Test prefix for prefix list result\n"
+ "Prefix to test in ip prefix-list\n"
+ "Prefix to test in ipv6 prefix-list\n"
+ "Use address matching mode (PIM RP)\n")
+{
+ struct prefix_list *plist;
+ const struct prefix_list_entry *entry = NULL;
+ enum prefix_list_type ret;
+
+ plist = prefix_list_lookup(family2afi(match->family), prefix_list);
+ if (!plist) {
+ vty_out(vty, "%% no prefix list named %s for AFI %s\n",
+ prefix_list, afi2str(family2afi(match->family)));
+ return CMD_WARNING;
+ }
+
+ ret = prefix_list_apply_ext(plist, &entry, match, !!addr_mode);
+
+ vty_out(vty, "%s prefix list %s yields %s for %pFX, ",
+ afi2str(family2afi(match->family)), prefix_list,
+ ret == PREFIX_DENY ? "DENY" : "PERMIT", match);
+
+ if (!entry)
+ vty_out(vty, "no match found\n");
+ else {
+ vty_out(vty, "matching entry #%"PRId64": %pFX", entry->seq,
+ &entry->prefix);
+ if (entry->ge)
+ vty_out(vty, " ge %d", entry->ge);
+ if (entry->le)
+ vty_out(vty, " le %d", entry->le);
+ vty_out(vty, "\n");
+ }
+
+ /* allow using this in scripts for quick prefix-list member tests */
+ return (ret == PREFIX_PERMIT) ? CMD_SUCCESS : CMD_WARNING;
+}
+
struct stream *prefix_bgp_orf_entry(struct stream *s, struct prefix_list *plist,
uint8_t init_flag, uint8_t permit_flag,
uint8_t deny_flag)
@@ -1537,6 +1698,7 @@ static void prefix_list_init_ipv6(void)
install_element(VIEW_NODE, &show_ipv6_prefix_list_prefix_cmd);
install_element(VIEW_NODE, &show_ipv6_prefix_list_summary_cmd);
install_element(VIEW_NODE, &show_ipv6_prefix_list_detail_cmd);
+ install_element(VIEW_NODE, &debug_prefix_list_match_cmd);
install_element(ENABLE_NODE, &clear_ipv6_prefix_list_cmd);
}
diff --git a/lib/plist.h b/lib/plist.h
index 57eb763a68..c9507df57c 100644
--- a/lib/plist.h
+++ b/lib/plist.h
@@ -37,6 +37,7 @@ enum prefix_list_type {
};
struct prefix_list;
+struct prefix_list_entry;
struct orf_prefix {
uint32_t seq;
@@ -63,12 +64,18 @@ extern struct prefix_list *prefix_list_lookup(afi_t, const char *);
*
* If no pointer is sent in, do not return anything.
* If it is a empty plist return a NULL pointer.
+ *
+ * address_mode = the "prefix" being passed in is really an address, match
+ * regardless of prefix length (i.e. ge/le are ignored.) prefix->prefixlen
+ * must be /32.
*/
extern enum prefix_list_type
-prefix_list_apply_which_prefix(struct prefix_list *plist,
- const struct prefix **which,
- const void *object);
-#define prefix_list_apply(A, B) prefix_list_apply_which_prefix((A), NULL, (B))
+prefix_list_apply_ext(struct prefix_list *plist,
+ const struct prefix_list_entry **matches,
+ union prefixconstptr prefix,
+ bool address_mode);
+#define prefix_list_apply(A, B) \
+ prefix_list_apply_ext((A), NULL, (B), false)
extern struct prefix_list *prefix_bgp_orf_lookup(afi_t, const char *);
extern struct stream *prefix_bgp_orf_entry(struct stream *,
diff --git a/lib/prefix.c b/lib/prefix.c
index 7dbb5f07f0..ef7d2e59da 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -577,7 +577,7 @@ int str2prefix_ipv4(const char *str, struct prefix_ipv4 *p)
/* Get prefix length. */
plen = (uint8_t)atoi(++pnt);
- if (plen > IPV4_MAX_PREFIXLEN)
+ if (plen > IPV4_MAX_BITLEN)
return 0;
p->family = AF_INET;
@@ -1034,7 +1034,8 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size)
l = strlen(buf);
buf[l++] = '/';
byte = p->prefixlen;
- if ((tmp = p->prefixlen - 100) >= 0) {
+ tmp = p->prefixlen - 100;
+ if (tmp >= 0) {
buf[l++] = '1';
z = true;
byte = tmp;
@@ -1128,7 +1129,7 @@ void apply_classful_mask_ipv4(struct prefix_ipv4 *p)
destination = ntohl(p->prefix.s_addr);
- if (p->prefixlen == IPV4_MAX_PREFIXLEN)
+ if (p->prefixlen == IPV4_MAX_BITLEN)
;
/* do nothing for host routes */
else if (IN_CLASSC(destination)) {
@@ -1148,12 +1149,13 @@ in_addr_t ipv4_broadcast_addr(in_addr_t hostaddr, int masklen)
struct in_addr mask;
masklen2ip(masklen, &mask);
- return (masklen != IPV4_MAX_PREFIXLEN - 1) ?
- /* normal case */
- (hostaddr | ~mask.s_addr)
- :
- /* For prefix 31 return 255.255.255.255 (RFC3021) */
- htonl(0xFFFFFFFF);
+ return (masklen != IPV4_MAX_BITLEN - 1)
+ ?
+ /* normal case */
+ (hostaddr | ~mask.s_addr)
+ :
+ /* For prefix 31 return 255.255.255.255 (RFC3021) */
+ htonl(0xFFFFFFFF);
}
/* Utility function to convert ipv4 netmask to prefixes
diff --git a/lib/prefix.h b/lib/prefix.h
index 217a23d561..944c94f57f 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -372,7 +372,6 @@ union prefixconstptr {
/* Max bit/byte length of IPv4 address. */
#define IPV4_MAX_BYTELEN 4
#define IPV4_MAX_BITLEN 32
-#define IPV4_MAX_PREFIXLEN 32
#define IPV4_ADDR_CMP(D,S) memcmp ((D), (S), IPV4_MAX_BYTELEN)
static inline bool ipv4_addr_same(const struct in_addr *a,
@@ -398,7 +397,6 @@ static inline void ipv4_addr_copy(struct in_addr *dst,
/* Max bit/byte length of IPv6 address. */
#define IPV6_MAX_BYTELEN 16
#define IPV6_MAX_BITLEN 128
-#define IPV6_MAX_PREFIXLEN 128
#define IPV6_ADDR_CMP(D,S) memcmp ((D), (S), IPV6_MAX_BYTELEN)
#define IPV6_ADDR_SAME(D,S) (memcmp ((D), (S), IPV6_MAX_BYTELEN) == 0)
#define IPV6_ADDR_COPY(D,S) memcpy ((D), (S), IPV6_MAX_BYTELEN)
@@ -481,11 +479,6 @@ extern void prefix_ipv4_free(struct prefix_ipv4 **p);
extern int str2prefix_ipv4(const char *, struct prefix_ipv4 *);
extern void apply_mask_ipv4(struct prefix_ipv4 *);
-#define PREFIX_COPY(DST, SRC) \
- *((struct prefix *)(DST)) = *((const struct prefix *)(SRC))
-#define PREFIX_COPY_IPV4(DST, SRC) \
- *((struct prefix_ipv4 *)(DST)) = *((const struct prefix_ipv4 *)(SRC))
-
extern int prefix_ipv4_any(const struct prefix_ipv4 *);
extern void apply_classful_mask_ipv4(struct prefix_ipv4 *);
@@ -503,9 +496,6 @@ extern void prefix_ipv6_free(struct prefix_ipv6 **p);
extern int str2prefix_ipv6(const char *, struct prefix_ipv6 *);
extern void apply_mask_ipv6(struct prefix_ipv6 *);
-#define PREFIX_COPY_IPV6(DST, SRC) \
- *((struct prefix_ipv6 *)(DST)) = *((const struct prefix_ipv6 *)(SRC))
-
extern int ip6_masklen(struct in6_addr);
extern void masklen2ip6(const int, struct in6_addr *);
@@ -547,20 +537,32 @@ static inline int ipv4_martian(struct in_addr *addr)
return 0;
}
-static inline int is_default_prefix(const struct prefix *p)
+static inline bool is_default_prefix4(const struct prefix_ipv4 *p)
{
- if (!p)
- return 0;
+ return p && p->family == AF_INET && p->prefixlen == 0
+ && p->prefix.s_addr == INADDR_ANY;
+}
- if ((p->family == AF_INET) && (p->u.prefix4.s_addr == INADDR_ANY)
- && (p->prefixlen == 0))
- return 1;
+static inline bool is_default_prefix6(const struct prefix_ipv6 *p)
+{
+ return p && p->family == AF_INET6 && p->prefixlen == 0
+ && memcmp(&p->prefix, &in6addr_any, sizeof(struct in6_addr))
+ == 0;
+}
- if ((p->family == AF_INET6) && (p->prefixlen == 0)
- && (!memcmp(&p->u.prefix6, &in6addr_any, sizeof(struct in6_addr))))
- return 1;
+static inline bool is_default_prefix(const struct prefix *p)
+{
+ if (p == NULL)
+ return false;
+
+ switch (p->family) {
+ case AF_INET:
+ return is_default_prefix4((const struct prefix_ipv4 *)p);
+ case AF_INET6:
+ return is_default_prefix6((const struct prefix_ipv6 *)p);
+ }
- return 0;
+ return false;
}
static inline int is_host_route(const struct prefix *p)
diff --git a/lib/route_opaque.h b/lib/route_opaque.h
index 599a0363eb..7c4e9a16e1 100644
--- a/lib/route_opaque.h
+++ b/lib/route_opaque.h
@@ -21,12 +21,15 @@
#ifndef FRR_ROUTE_OPAQUE_H
#define FRR_ROUTE_OPAQUE_H
+#include "assert.h"
+#include "zclient.h"
+
#include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_community.h"
#include "bgpd/bgp_lcommunity.h"
struct bgp_zebra_opaque {
- char aspath[ASPATH_STR_DEFAULT_LEN];
+ char aspath[256];
/* Show at least 10 communities AA:BB */
char community[COMMUNITY_SIZE * 20];
@@ -35,4 +38,7 @@ struct bgp_zebra_opaque {
char lcommunity[LCOMMUNITY_SIZE * 30];
};
+static_assert(sizeof(struct bgp_zebra_opaque) <= ZAPI_MESSAGE_OPAQUE_LENGTH,
+ "BGP opaque data shouldn't be larger than zebra's buffer");
+
#endif /* FRR_ROUTE_OPAQUE_H */
diff --git a/lib/route_types.txt b/lib/route_types.txt
index c48391545d..77639070c9 100644
--- a/lib/route_types.txt
+++ b/lib/route_types.txt
@@ -1,4 +1,4 @@
-# Canonical Zserv route types information registry for Quagga.
+# Canonical Zserv route types information registry for FRR.
#
# Used to construct route_types.c and route_types.h
#
@@ -60,7 +60,7 @@ ZEBRA_ROUTE_PIM, pim, pimd, 'P', 0, 0, 0, "PIM", pimd
ZEBRA_ROUTE_EIGRP, eigrp, eigrpd, 'E', 1, 0, 1, "EIGRP", eigrpd
ZEBRA_ROUTE_NHRP, nhrp, nhrpd, 'N', 1, 1, 1, "NHRP", nhrpd
# HSLS and OLSR both are AFI independent (so: 1, 1), however
-# we want to disable for them for general Quagga distribution.
+# we want to disable for them for general FRR distribution.
# This at least makes it trivial for users of these protocols
# to 'switch on' redist support (direct numeric entry remaining
# possible).
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/routemap.h b/lib/routemap.h
index 4a40ec08b9..8af3b2c3c0 100644
--- a/lib/routemap.h
+++ b/lib/routemap.h
@@ -270,6 +270,7 @@ DECLARE_QOBJ_TYPE(route_map);
/* BGP route-map match conditions */
#define IS_MATCH_LOCAL_PREF(C) \
(strmatch(C, "frr-bgp-route-map:match-local-preference"))
+#define IS_MATCH_ALIAS(C) (strmatch(C, "frr-bgp-route-map:match-alias"))
#define IS_MATCH_ORIGIN(C) \
(strmatch(C, "frr-bgp-route-map:match-origin"))
#define IS_MATCH_RPKI(C) (strmatch(C, "frr-bgp-route-map:rpki"))
diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c
index bf982cfa2b..ec9033b3aa 100644
--- a/lib/routemap_cli.c
+++ b/lib/routemap_cli.c
@@ -634,6 +634,11 @@ void route_map_condition_show(struct vty *vty, struct lyd_node *dnode,
yang_dnode_get_string(
dnode,
"./rmap-match-condition/frr-bgp-route-map:local-preference"));
+ } else if (IS_MATCH_ALIAS(condition)) {
+ vty_out(vty, " match alias %s\n",
+ yang_dnode_get_string(
+ dnode,
+ "./rmap-match-condition/frr-bgp-route-map:alias"));
} else if (IS_MATCH_ORIGIN(condition)) {
vty_out(vty, " match origin %s\n",
yang_dnode_get_string(
diff --git a/lib/sockopt.c b/lib/sockopt.c
index 98bfda5079..150736e00c 100644
--- a/lib/sockopt.c
+++ b/lib/sockopt.c
@@ -379,14 +379,14 @@ static int setsockopt_ipv4_ifindex(int sock, ifindex_t val)
int ret;
#if defined(IP_PKTINFO)
- if ((ret = setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof(val)))
- < 0)
+ ret = setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof(val));
+ if (ret < 0)
flog_err(EC_LIB_SOCKET,
"Can't set IP_PKTINFO option for fd %d to %d: %s",
sock, val, safe_strerror(errno));
#elif defined(IP_RECVIF)
- if ((ret = setsockopt(sock, IPPROTO_IP, IP_RECVIF, &val, sizeof(val)))
- < 0)
+ ret = setsockopt(sock, IPPROTO_IP, IP_RECVIF, &val, sizeof(val));
+ if (ret < 0)
flog_err(EC_LIB_SOCKET,
"Can't set IP_RECVIF option for fd %d to %d: %s", sock,
val, safe_strerror(errno));
@@ -639,12 +639,8 @@ int sockopt_tcp_signature_ext(int sock, union sockunion *su, uint16_t prefixlen,
#endif /* GNU_LINUX */
- if ((ret = setsockopt(sock, IPPROTO_TCP, optname, &md5sig,
- sizeof(md5sig)))
- < 0) {
- /* ENOENT is harmless. It is returned when we clear a password
- for which
- one was not previously set. */
+ ret = setsockopt(sock, IPPROTO_TCP, optname, &md5sig, sizeof(md5sig));
+ if (ret < 0) {
if (ENOENT == errno)
ret = 0;
else
diff --git a/lib/sockunion.c b/lib/sockunion.c
index e6340a1743..c7af458e9e 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -606,8 +606,7 @@ static void __attribute__((unused)) sockunion_print(const union sockunion *su)
}
}
-static int in6addr_cmp(const struct in6_addr *addr1,
- const struct in6_addr *addr2)
+int in6addr_cmp(const struct in6_addr *addr1, const struct in6_addr *addr2)
{
unsigned int i;
const uint8_t *p1, *p2;
diff --git a/lib/sockunion.h b/lib/sockunion.h
index 2cc80bb70f..9e6719ccf9 100644
--- a/lib/sockunion.h
+++ b/lib/sockunion.h
@@ -73,6 +73,7 @@ enum connect_result { connect_error, connect_success, connect_in_progress };
/* Prototypes. */
extern int str2sockunion(const char *, union sockunion *);
extern const char *sockunion2str(const union sockunion *, char *, size_t);
+int in6addr_cmp(const struct in6_addr *addr1, const struct in6_addr *addr2);
extern int sockunion_cmp(const union sockunion *, const union sockunion *);
extern int sockunion_same(const union sockunion *, const union sockunion *);
extern unsigned int sockunion_hash(const union sockunion *);
diff --git a/lib/stream.c b/lib/stream.c
index 904ee73b10..1557500c60 100644
--- a/lib/stream.c
+++ b/lib/stream.c
@@ -1097,7 +1097,8 @@ ssize_t stream_read_try(struct stream *s, int fd, size_t size)
return -1;
}
- if ((nbytes = read(fd, s->data + s->endp, size)) >= 0) {
+ nbytes = read(fd, s->data + s->endp, size);
+ if (nbytes >= 0) {
s->endp += nbytes;
return nbytes;
}
@@ -1126,9 +1127,8 @@ ssize_t stream_recvfrom(struct stream *s, int fd, size_t size, int flags,
return -1;
}
- if ((nbytes = recvfrom(fd, s->data + s->endp, size, flags, from,
- fromlen))
- >= 0) {
+ nbytes = recvfrom(fd, s->data + s->endp, size, flags, from, fromlen);
+ if (nbytes >= 0) {
s->endp += nbytes;
return nbytes;
}
diff --git a/lib/subdir.am b/lib/subdir.am
index 90301d800a..714af43238 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -2,7 +2,7 @@
# libfrr
#
lib_LTLIBRARIES += lib/libfrr.la
-lib_libfrr_la_LDFLAGS = -version-info 0:0:0 -Xlinker -e_libfrr_version
+lib_libfrr_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0 -Xlinker -e_libfrr_version
lib_libfrr_la_LIBADD = $(LIBCAP) $(UNWIND_LIBS) $(LIBYANG_LIBS) $(LUA_LIB) $(UST_LIBS) $(LIBM)
lib_libfrr_la_SOURCES = \
@@ -322,7 +322,7 @@ lib_LTLIBRARIES += lib/libfrrsnmp.la
endif
lib_libfrrsnmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
-lib_libfrrsnmp_la_LDFLAGS = -version-info 0:0:0
+lib_libfrrsnmp_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
lib_libfrrsnmp_la_LIBADD = $(SNMP_LIBS)
lib_libfrrsnmp_la_SOURCES = \
lib/agentx.c \
@@ -338,7 +338,7 @@ pkginclude_HEADERS += lib/resolver.h
endif
lib_libfrrcares_la_CFLAGS = $(AM_CFLAGS) $(CARES_CFLAGS)
-lib_libfrrcares_la_LDFLAGS = -version-info 0:0:0
+lib_libfrrcares_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
lib_libfrrcares_la_LIBADD = $(CARES_LIBS)
lib_libfrrcares_la_SOURCES = \
lib/resolver.c \
@@ -353,7 +353,7 @@ pkginclude_HEADERS += lib/frr_zmq.h
endif
lib_libfrrzmq_la_CFLAGS = $(AM_CFLAGS) $(ZEROMQ_CFLAGS)
-lib_libfrrzmq_la_LDFLAGS = -version-info 0:0:0
+lib_libfrrzmq_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
lib_libfrrzmq_la_LIBADD = $(ZEROMQ_LIBS)
lib_libfrrzmq_la_SOURCES = \
lib/frr_zmq.c \
@@ -367,7 +367,7 @@ module_LTLIBRARIES += lib/confd.la
endif
lib_confd_la_CFLAGS = $(AM_CFLAGS) $(CONFD_CFLAGS)
-lib_confd_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+lib_confd_la_LDFLAGS = $(MODULE_LDFLAGS)
lib_confd_la_LIBADD = lib/libfrr.la $(CONFD_LIBS)
lib_confd_la_SOURCES = lib/northbound_confd.c
@@ -379,7 +379,7 @@ module_LTLIBRARIES += lib/sysrepo.la
endif
lib_sysrepo_la_CFLAGS = $(AM_CFLAGS) $(SYSREPO_CFLAGS)
-lib_sysrepo_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+lib_sysrepo_la_LDFLAGS = $(MODULE_LDFLAGS)
lib_sysrepo_la_LIBADD = lib/libfrr.la $(SYSREPO_LIBS)
lib_sysrepo_la_SOURCES = lib/northbound_sysrepo.c
@@ -391,7 +391,7 @@ module_LTLIBRARIES += lib/grpc.la
endif
lib_grpc_la_CXXFLAGS = $(WERROR) $(GRPC_CFLAGS)
-lib_grpc_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+lib_grpc_la_LDFLAGS = $(MODULE_LDFLAGS)
lib_grpc_la_LIBADD = lib/libfrr.la grpc/libfrrgrpc_pb.la $(GRPC_LIBS)
lib_grpc_la_SOURCES = lib/northbound_grpc.cpp
@@ -419,7 +419,8 @@ lib_grammar_sandbox_LDADD = \
lib_clippy_CPPFLAGS = $(CPPFLAGS_BASE) -D_GNU_SOURCE -DBUILDING_CLIPPY
lib_clippy_CFLAGS = $(AC_CFLAGS) $(PYTHON_CFLAGS)
lib_clippy_LDADD = $(PYTHON_LIBS) $(UST_LIBS) -lelf
-lib_clippy_LDFLAGS = -export-dynamic
+# no $(SAN_FLAGS) here
+lib_clippy_LDFLAGS = -export-dynamic $(AC_LDFLAGS) $(AC_LDFLAGS_EXEC)
lib_clippy_SOURCES = \
lib/jhash.c \
lib/clippy.c \
diff --git a/lib/systemd.c b/lib/systemd.c
index c5cc3aa447..2238dc9f3d 100644
--- a/lib/systemd.c
+++ b/lib/systemd.c
@@ -20,68 +20,56 @@
*/
#include <zebra.h>
+#include <sys/un.h>
#include "thread.h"
#include "systemd.h"
+#include "lib_errors.h"
-#if defined HAVE_SYSTEMD
-#include <systemd/sd-daemon.h>
-#endif
+/* these are cleared from env so they don't "leak" into things we fork(),
+ * particularly for watchfrr starting individual daemons
+ *
+ * watchdog_pid is currently not used since watchfrr starts forking.
+ * (TODO: handle that better, somehow?)
+ */
+static pid_t watchdog_pid = -1;
+static intmax_t watchdog_msec;
+
+/* not used yet, but can trigger auto-switch to journald logging */
+bool sd_stdout_is_journal;
+bool sd_stderr_is_journal;
+
+static char *notify_socket;
-/*
- * Wrapper this silliness if we
- * don't have systemd
+/* talk to whatever entity claims to be systemd ;)
+ *
+ * refer to sd_notify docs for messages systemd accepts over this socket.
+ * This function should be functionally equivalent to sd_notify().
*/
static void systemd_send_information(const char *info)
{
-#if defined HAVE_SYSTEMD
- sd_notify(0, info);
-#else
- return;
-#endif
-}
+ int sock;
+ struct sockaddr_un sun;
-/*
- * A return of 0 means that we are not watchdoged
- */
-static int systemd_get_watchdog_time(int the_process)
-{
-#if defined HAVE_SYSTEMD
- uint64_t usec;
- char *watchdog = NULL;
- int ret;
-
- ret = sd_watchdog_enabled(0, &usec);
-
- /*
- * If return is 0 -> we don't want watchdog
- * if return is < 0, some sort of failure occurred
- */
- if (ret < 0)
- return 0;
-
- /*
- * systemd can return that this process
- * is not the expected sender of the watchdog timer
- * If we set the_process = 0 then we expect to
- * be able to send the watchdog to systemd
- * irrelevant of the pid of this process.
- */
- if (ret == 0 && the_process)
- return 0;
-
- if (ret == 0 && !the_process) {
- watchdog = getenv("WATCHDOG_USEC");
- if (!watchdog)
- return 0;
-
- usec = atol(watchdog);
- }
+ if (!notify_socket)
+ return;
+
+ sock = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (sock < 0)
+ return;
+
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, notify_socket, sizeof(sun.sun_path));
- return (usec / 1000000) / 3;
-#else
- return 0;
-#endif
+ /* linux abstract unix socket namespace */
+ if (sun.sun_path[0] == '@')
+ sun.sun_path[0] = '\0';
+
+ /* nothing we can do if this errors out... */
+ (void)sendto(sock, info, strlen(info), 0, (struct sockaddr *)&sun,
+ sizeof(sun));
+
+ close(sock);
}
void systemd_send_stopping(void)
@@ -90,34 +78,27 @@ void systemd_send_stopping(void)
systemd_send_information("STOPPING=1");
}
-/*
- * How many seconds should we wait between watchdog sends
- */
-static int wsecs = 0;
static struct thread_master *systemd_master = NULL;
static int systemd_send_watchdog(struct thread *t)
{
systemd_send_information("WATCHDOG=1");
- thread_add_timer(systemd_master, systemd_send_watchdog, NULL, wsecs,
- NULL);
-
+ assert(watchdog_msec > 0);
+ thread_add_timer_msec(systemd_master, systemd_send_watchdog, NULL,
+ watchdog_msec, NULL);
return 1;
}
-void systemd_send_started(struct thread_master *m, int the_process)
+void systemd_send_started(struct thread_master *m)
{
assert(m != NULL);
- wsecs = systemd_get_watchdog_time(the_process);
systemd_master = m;
systemd_send_information("READY=1");
- if (wsecs != 0) {
- systemd_send_information("WATCHDOG=1");
- thread_add_timer(m, systemd_send_watchdog, m, wsecs, NULL);
- }
+ if (watchdog_msec > 0)
+ systemd_send_watchdog(NULL);
}
void systemd_send_status(const char *status)
@@ -127,3 +108,65 @@ void systemd_send_status(const char *status)
snprintf(buffer, sizeof(buffer), "STATUS=%s", status);
systemd_send_information(buffer);
}
+
+static intmax_t getenv_int(const char *varname, intmax_t dflt)
+{
+ char *val, *err;
+ intmax_t intval;
+
+ val = getenv(varname);
+ if (!val)
+ return dflt;
+
+ intval = strtoimax(val, &err, 0);
+ if (*err || !*val)
+ return dflt;
+ return intval;
+}
+
+void systemd_init_env(void)
+{
+ char *tmp;
+ uintmax_t dev, ino;
+ int len;
+ struct stat st;
+
+ notify_socket = getenv("NOTIFY_SOCKET");
+
+ /* no point in setting up watchdog w/o notify socket */
+ if (notify_socket) {
+ intmax_t watchdog_usec;
+
+ watchdog_pid = getenv_int("WATCHDOG_PID", -1);
+ if (watchdog_pid <= 0)
+ watchdog_pid = -1;
+
+ /* note this is the deadline, hence the divide by 3 */
+ watchdog_usec = getenv_int("WATCHDOG_USEC", 0);
+ if (watchdog_usec >= 3000)
+ watchdog_msec = watchdog_usec / 3000;
+ else {
+ if (watchdog_usec != 0)
+ flog_err(
+ EC_LIB_UNAVAILABLE,
+ "systemd expects a %jd microsecond watchdog timer, but FRR only supports millisecond resolution!",
+ watchdog_usec);
+ watchdog_msec = 0;
+ }
+ }
+
+ tmp = getenv("JOURNAL_STREAM");
+ if (tmp && sscanf(tmp, "%ju:%ju%n", &dev, &ino, &len) == 2
+ && (size_t)len == strlen(tmp)) {
+ if (fstat(1, &st) == 0 && st.st_dev == (dev_t)dev
+ && st.st_ino == (ino_t)ino)
+ sd_stdout_is_journal = true;
+ if (fstat(2, &st) == 0 && st.st_dev == (dev_t)dev
+ && st.st_ino == (ino_t)ino)
+ sd_stderr_is_journal = true;
+ }
+
+ /* these should *not* be passed to any other process we start */
+ unsetenv("WATCHDOG_PID");
+ unsetenv("WATCHDOG_USEC");
+}
diff --git a/lib/systemd.h b/lib/systemd.h
index d9885c5d9c..1933f4f688 100644
--- a/lib/systemd.h
+++ b/lib/systemd.h
@@ -28,9 +28,6 @@ extern "C" {
*
* Design point is that if systemd is not being used on this system
* then these functions becomes a no-op.
- *
- * To turn on systemd compilation, use --enable-systemd on
- * configure run.
*/
void systemd_send_stopping(void);
@@ -39,13 +36,18 @@ void systemd_send_stopping(void);
* the_process - Should we send watchdog if we are not the requested
* process?
*/
-void systemd_send_started(struct thread_master *master, int the_process);
+void systemd_send_started(struct thread_master *master);
/*
* status - A status string to send to systemd
*/
void systemd_send_status(const char *status);
+/*
+ * grab startup state from env vars
+ */
+void systemd_init_env(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/table.c b/lib/table.c
index dfd92c6189..e6030ca4ca 100644
--- a/lib/table.c
+++ b/lib/table.c
@@ -230,7 +230,7 @@ struct route_node *route_node_match_ipv4(struct route_table *table,
memset(&p, 0, sizeof(struct prefix_ipv4));
p.family = AF_INET;
- p.prefixlen = IPV4_MAX_PREFIXLEN;
+ p.prefixlen = IPV4_MAX_BITLEN;
p.prefix = *addr;
return route_node_match(table, (struct prefix *)&p);
@@ -243,7 +243,7 @@ struct route_node *route_node_match_ipv6(struct route_table *table,
memset(&p, 0, sizeof(struct prefix_ipv6));
p.family = AF_INET6;
- p.prefixlen = IPV6_MAX_PREFIXLEN;
+ p.prefixlen = IPV6_MAX_BITLEN;
p.prefix = *addr;
return route_node_match(table, &p);
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;
}
diff --git a/lib/zclient.c b/lib/zclient.c
index ffae1332af..0815e77d4e 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -446,7 +446,7 @@ enum zclient_send_status zclient_send_localsid(struct zclient *zclient,
struct nexthop nh = {};
p.family = AF_INET6;
- p.prefixlen = 128;
+ p.prefixlen = IPV6_MAX_BITLEN;
p.prefix = *sid;
api.vrf_id = VRF_DEFAULT;
@@ -1432,7 +1432,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
STREAM_GETC(s, api->prefix.prefixlen);
switch (api->prefix.family) {
case AF_INET:
- if (api->prefix.prefixlen > IPV4_MAX_PREFIXLEN) {
+ if (api->prefix.prefixlen > IPV4_MAX_BITLEN) {
flog_err(
EC_LIB_ZAPI_ENCODE,
"%s: V4 prefixlen is %d which should not be more than 32",
@@ -1441,7 +1441,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
}
break;
case AF_INET6:
- if (api->prefix.prefixlen > IPV6_MAX_PREFIXLEN) {
+ if (api->prefix.prefixlen > IPV6_MAX_BITLEN) {
flog_err(
EC_LIB_ZAPI_ENCODE,
"%s: v6 prefixlen is %d which should not be more than 128",
@@ -1460,7 +1460,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_SRCPFX)) {
api->src_prefix.family = AF_INET6;
STREAM_GETC(s, api->src_prefix.prefixlen);
- if (api->src_prefix.prefixlen > IPV6_MAX_PREFIXLEN) {
+ if (api->src_prefix.prefixlen > IPV6_MAX_BITLEN) {
flog_err(
EC_LIB_ZAPI_ENCODE,
"%s: SRC Prefix prefixlen received: %d is too large",
@@ -1537,7 +1537,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_OPAQUE)) {
STREAM_GETW(s, api->opaque.length);
- assert(api->opaque.length < ZAPI_MESSAGE_OPAQUE_LENGTH);
+ assert(api->opaque.length <= ZAPI_MESSAGE_OPAQUE_LENGTH);
STREAM_GET(api->opaque.data, s, api->opaque.length);
}
@@ -3812,7 +3812,8 @@ static int zclient_read(struct thread *thread)
zclient->t_read = NULL;
/* Read zebra header (if we don't have it already). */
- if ((already = stream_get_endp(zclient->ibuf)) < ZEBRA_HEADER_SIZE) {
+ already = stream_get_endp(zclient->ibuf);
+ if (already < ZEBRA_HEADER_SIZE) {
ssize_t nbyte;
if (((nbyte = stream_read_try(zclient->ibuf, zclient->sock,
ZEBRA_HEADER_SIZE - already))
@@ -3825,7 +3826,6 @@ static int zclient_read(struct thread *thread)
return zclient_failed(zclient);
}
if (nbyte != (ssize_t)(ZEBRA_HEADER_SIZE - already)) {
- /* Try again later. */
zclient_event(ZCLIENT_READ, zclient);
return 0;
}