summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/agentx.c44
-rw-r--r--lib/bfd.c22
-rw-r--r--lib/buffer.c10
-rw-r--r--lib/clippy.c3
-rw-r--r--lib/command.c79
-rw-r--r--lib/command.h42
-rw-r--r--lib/command_graph.h2
-rw-r--r--lib/command_parse.y2
-rw-r--r--lib/compiler.h23
-rw-r--r--lib/ferr.h2
-rw-r--r--lib/filter_cli.c24
-rw-r--r--lib/frr_zmq.c58
-rw-r--r--lib/frr_zmq.h42
-rw-r--r--lib/frrlua.c387
-rw-r--r--lib/frrlua.h216
-rw-r--r--lib/frrscript.c272
-rw-r--r--lib/frrscript.h138
-rw-r--r--lib/hash.c8
-rw-r--r--lib/hash.h2
-rw-r--r--lib/if.c176
-rw-r--r--lib/if.h2
-rw-r--r--lib/lib_vty.c6
-rw-r--r--lib/libfrr.c39
-rw-r--r--lib/libfrr.h5
-rw-r--r--lib/link_state.c1282
-rw-r--r--lib/link_state.h780
-rw-r--r--lib/log.c13
-rw-r--r--lib/log.h10
-rw-r--r--lib/module.c2
-rw-r--r--lib/module.h8
-rw-r--r--lib/network.c18
-rw-r--r--lib/network.h18
-rw-r--r--lib/northbound.h5
-rw-r--r--lib/northbound_cli.c26
-rw-r--r--lib/printf/printf-pos.c4
-rw-r--r--lib/printf/vfprintf.c6
-rw-r--r--lib/privs.c6
-rw-r--r--lib/privs.h2
-rw-r--r--lib/resolver.c3
-rwxr-xr-xlib/route_types.pl2
-rw-r--r--lib/routemap.c58
-rw-r--r--lib/sigevent.c2
-rw-r--r--lib/smux.h53
-rw-r--r--lib/snmp.c54
-rw-r--r--lib/sockunion.c17
-rw-r--r--lib/sockunion.h1
-rw-r--r--lib/stream.c4
-rw-r--r--lib/subdir.am7
-rw-r--r--lib/thread.c137
-rw-r--r--lib/thread.h116
-rw-r--r--lib/vrf.c54
-rw-r--r--lib/vrf.h15
-rw-r--r--lib/vty.c125
-rw-r--r--lib/wheel.c3
-rw-r--r--lib/workqueue.c5
-rw-r--r--lib/xref.c130
-rw-r--r--lib/xref.h272
-rw-r--r--lib/yang.c1
-rw-r--r--lib/zclient.c71
-rw-r--r--lib/zclient.h64
-rw-r--r--lib/zlog.c22
-rw-r--r--lib/zlog.h79
62 files changed, 4417 insertions, 662 deletions
diff --git a/lib/agentx.c b/lib/agentx.c
index 603d8d6172..5351f8bda2 100644
--- a/lib/agentx.c
+++ b/lib/agentx.c
@@ -32,6 +32,9 @@
#include "linklist.h"
#include "version.h"
#include "lib_errors.h"
+#include "xref.h"
+
+XREF_SETUP()
static int agentx_enabled = 0;
@@ -262,11 +265,28 @@ void smux_register_mib(const char *descr, struct variable *var, size_t width,
register_mib(descr, var, width, num, name, namelen);
}
-int smux_trap(struct variable *vp, size_t vp_len, const oid *ename,
- size_t enamelen, const oid *name, size_t namelen,
- const oid *iname, size_t inamelen,
- const struct trap_object *trapobj, size_t trapobjlen,
- uint8_t sptrap)
+void smux_trap(struct variable *vp, size_t vp_len, const oid *ename,
+ size_t enamelen, const oid *name, size_t namelen,
+ const oid *iname, size_t inamelen,
+ const struct trap_object *trapobj, size_t trapobjlen,
+ uint8_t sptrap)
+{
+ struct index_oid trap_index[1];
+
+ /* copy the single index into the multi-index format */
+ oid_copy(trap_index[0].indexname, iname, inamelen);
+ trap_index[0].indexlen = inamelen;
+
+ smux_trap_multi_index(vp, vp_len, ename, enamelen, name, namelen,
+ trap_index, array_size(trap_index), trapobj,
+ trapobjlen, sptrap);
+}
+
+int smux_trap_multi_index(struct variable *vp, size_t vp_len, const oid *ename,
+ size_t enamelen, const oid *name, size_t namelen,
+ struct index_oid *iname, size_t index_len,
+ const struct trap_object *trapobj, size_t trapobjlen,
+ uint8_t sptrap)
{
oid objid_snmptrap[] = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0};
size_t objid_snmptrap_len = sizeof(objid_snmptrap) / sizeof(oid);
@@ -296,6 +316,13 @@ int smux_trap(struct variable *vp, size_t vp_len, const oid *ename,
size_t val_len;
WriteMethod *wm = NULL;
struct variable cvp;
+ unsigned int iindex;
+ /*
+ * this allows the behaviour of smux_trap with a singe index
+ * for all objects to be maintained whilst allowing traps which
+ * have different indices per object to be supported
+ */
+ iindex = (index_len == 1) ? 0 : i;
/* Make OID. */
if (trapobj[i].namelen > 0) {
@@ -303,8 +330,10 @@ int smux_trap(struct variable *vp, size_t vp_len, const oid *ename,
onamelen = trapobj[i].namelen;
oid_copy(oid, name, namelen);
oid_copy(oid + namelen, trapobj[i].name, onamelen);
- oid_copy(oid + namelen + onamelen, iname, inamelen);
- oid_len = namelen + onamelen + inamelen;
+ oid_copy(oid + namelen + onamelen,
+ iname[iindex].indexname,
+ iname[iindex].indexlen);
+ oid_len = namelen + onamelen + iname[iindex].indexlen;
} else {
/* Scalar object */
onamelen = trapobj[i].namelen * (-1);
@@ -330,6 +359,7 @@ int smux_trap(struct variable *vp, size_t vp_len, const oid *ename,
cvp.magic = vp[j].magic;
cvp.acl = vp[j].acl;
cvp.findVar = vp[j].findVar;
+
/* Grab the result. */
val = cvp.findVar(&cvp, oid, &oid_len, 1, &val_len,
&wm);
diff --git a/lib/bfd.c b/lib/bfd.c
index cdf7008601..3ab0e21af5 100644
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -224,6 +224,17 @@ struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp,
int plen;
int local_remote_cbit;
+ /*
+ * If the ifindex lookup fails the
+ * rest of the data in the stream is
+ * not read. All examples of this function
+ * call immediately use the dp->family which
+ * is not good. Ensure we are not using
+ * random data
+ */
+ memset(dp, 0, sizeof(*dp));
+ memset(sp, 0, sizeof(*sp));
+
/* Get interface index. */
ifindex = stream_getl(s);
@@ -249,13 +260,12 @@ struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp,
/* Get BFD status. */
*status = stream_getl(s);
- if (sp) {
- sp->family = stream_getc(s);
+ sp->family = stream_getc(s);
+
+ plen = prefix_blen(sp);
+ stream_get(&sp->u.prefix, s, plen);
+ sp->prefixlen = stream_getc(s);
- plen = prefix_blen(sp);
- stream_get(&sp->u.prefix, s, plen);
- sp->prefixlen = stream_getc(s);
- }
local_remote_cbit = stream_getc(s);
if (remote_cbit)
*remote_cbit = local_remote_cbit;
diff --git a/lib/buffer.c b/lib/buffer.c
index 459d98e75d..42796faae8 100644
--- a/lib/buffer.c
+++ b/lib/buffer.c
@@ -468,16 +468,6 @@ buffer_status_t buffer_write(struct buffer *b, int fd, const void *p,
{
ssize_t nbytes;
-#if 0
- /*
- * Should we attempt to drain any previously buffered data?
- * This could help reduce latency in pushing out the data if
- * we are stuck in a long-running thread that is preventing
- * the main select loop from calling the flush thread...
- */
- if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR))
- return BUFFER_ERROR;
-#endif
if (b->head)
/* Buffer is not empty, so do not attempt to write the new data.
*/
diff --git a/lib/clippy.c b/lib/clippy.c
index 2e09c24c66..15cd9d7a4b 100644
--- a/lib/clippy.c
+++ b/lib/clippy.c
@@ -107,7 +107,8 @@ int main(int argc, char **argv)
#include "log.h"
#include "zassert.h"
-void vzlog(int prio, const char *format, va_list args)
+void vzlogx(const struct xref_logmsg *xref, int prio,
+ const char *format, va_list args)
{
vfprintf(stderr, format, args);
fputs("\n", stderr);
diff --git a/lib/command.c b/lib/command.c
index 87110157f6..6a4d504b2f 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -49,6 +49,8 @@
#include "northbound_cli.h"
#include "network.h"
+#include "frrscript.h"
+
DEFINE_MTYPE_STATIC(LIB, HOST, "Host config")
DEFINE_MTYPE(LIB, COMPLETION, "Completion item")
@@ -275,7 +277,7 @@ const char *cmd_prompt(enum node_type node)
}
/* Install a command into a node. */
-void install_element(enum node_type ntype, const struct cmd_element *cmd)
+void _install_element(enum node_type ntype, const struct cmd_element *cmd)
{
struct cmd_node *cnode;
@@ -321,7 +323,7 @@ void install_element(enum node_type ntype, const struct cmd_element *cmd)
vector_set(cnode->cmd_vector, (void *)cmd);
if (ntype == VIEW_NODE)
- install_element(ENABLE_NODE, cmd);
+ _install_element(ENABLE_NODE, cmd);
}
void uninstall_element(enum node_type ntype, const struct cmd_element *cmd)
@@ -863,6 +865,30 @@ enum node_type node_parent(enum node_type node)
case BFD_PROFILE_NODE:
ret = BFD_NODE;
break;
+ case SR_TRAFFIC_ENG_NODE:
+ ret = SEGMENT_ROUTING_NODE;
+ break;
+ case SR_SEGMENT_LIST_NODE:
+ ret = SR_TRAFFIC_ENG_NODE;
+ break;
+ case SR_POLICY_NODE:
+ ret = SR_TRAFFIC_ENG_NODE;
+ break;
+ case SR_CANDIDATE_DYN_NODE:
+ ret = SR_POLICY_NODE;
+ break;
+ case PCEP_NODE:
+ ret = SR_TRAFFIC_ENG_NODE;
+ break;
+ case PCEP_PCE_CONFIG_NODE:
+ ret = PCEP_NODE;
+ break;
+ case PCEP_PCE_NODE:
+ ret = PCEP_NODE;
+ break;
+ case PCEP_PCC_NODE:
+ ret = PCEP_NODE;
+ break;
default:
ret = CONFIG_NODE;
break;
@@ -2279,6 +2305,31 @@ done:
return CMD_SUCCESS;
}
+#if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
+DEFUN(script,
+ script_cmd,
+ "script SCRIPT",
+ "Test command - execute a script\n"
+ "Script name (same as filename in /etc/frr/scripts/\n")
+{
+ struct prefix p;
+
+ (void)str2prefix("1.2.3.4/24", &p);
+
+ struct frrscript *fs = frrscript_load(argv[1]->arg, NULL);
+
+ if (fs == NULL) {
+ vty_out(vty, "Script '/etc/frr/scripts/%s.lua' not found\n",
+ argv[1]->arg);
+ } else {
+ int ret = frrscript_call(fs, NULL);
+ vty_out(vty, "Script result: %d\n", ret);
+ }
+
+ return CMD_SUCCESS;
+}
+#endif
+
/* Set config filename. Called from vty.c */
void host_config_set(const char *filename)
{
@@ -2293,18 +2344,18 @@ const char *host_config_get(void)
void install_default(enum node_type node)
{
- install_element(node, &config_exit_cmd);
- install_element(node, &config_quit_cmd);
- install_element(node, &config_end_cmd);
- install_element(node, &config_help_cmd);
- install_element(node, &config_list_cmd);
- install_element(node, &show_cli_graph_cmd);
- install_element(node, &find_cmd);
+ _install_element(node, &config_exit_cmd);
+ _install_element(node, &config_quit_cmd);
+ _install_element(node, &config_end_cmd);
+ _install_element(node, &config_help_cmd);
+ _install_element(node, &config_list_cmd);
+ _install_element(node, &show_cli_graph_cmd);
+ _install_element(node, &find_cmd);
- install_element(node, &config_write_cmd);
- install_element(node, &show_running_config_cmd);
+ _install_element(node, &config_write_cmd);
+ _install_element(node, &show_running_config_cmd);
- install_element(node, &autocomplete_cmd);
+ _install_element(node, &autocomplete_cmd);
nb_cli_install_default(node);
}
@@ -2373,6 +2424,10 @@ void cmd_init(int terminal)
install_element(VIEW_NODE, &echo_cmd);
install_element(VIEW_NODE, &autocomplete_cmd);
install_element(VIEW_NODE, &find_cmd);
+#if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
+ install_element(VIEW_NODE, &script_cmd);
+#endif
+
install_element(ENABLE_NODE, &config_end_cmd);
install_element(ENABLE_NODE, &config_disable_cmd);
diff --git a/lib/command.h b/lib/command.h
index 1b0504101c..71abb20b05 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -145,6 +145,15 @@ enum node_type {
PROTOCOL_NODE, /* protocol filtering node */
MPLS_NODE, /* MPLS config node */
PW_NODE, /* Pseudowire config node */
+ SEGMENT_ROUTING_NODE, /* Segment routing root node */
+ SR_TRAFFIC_ENG_NODE, /* SR Traffic Engineering node */
+ SR_SEGMENT_LIST_NODE, /* SR segment list config node */
+ SR_POLICY_NODE, /* SR policy config node */
+ SR_CANDIDATE_DYN_NODE, /* SR dynamic candidate path config node */
+ PCEP_NODE, /* PCEP node */
+ PCEP_PCE_CONFIG_NODE, /* PCE shared configuration node */
+ PCEP_PCE_NODE, /* PCE configuration node */
+ PCEP_PCC_NODE, /* PCC configuration node */
VTY_NODE, /* Vty node. */
FPM_NODE, /* Dataplane FPM node. */
LINK_PARAMS_NODE, /* Link-parameters node */
@@ -230,7 +239,11 @@ struct cmd_node {
.attr = attrs, \
.daemon = dnum, \
.name = #cmdname, \
- };
+ .xref = XREF_INIT(XREFT_DEFUN, NULL, #funcname), \
+ }; \
+ XREF_LINK(cmdname.xref); \
+ /* end */
+
#define DEFUN_CMD_FUNC_DECL(funcname) \
static int funcname(const struct cmd_element *, struct vty *, int, \
@@ -405,7 +418,8 @@ struct cmd_node {
"<neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr>"
#define AREA_TAG_STR "[area tag]\n"
#define COMMUNITY_AANN_STR "Community number where AA and NN are (0-65535)\n"
-#define COMMUNITY_VAL_STR "Community number in AA:NN format (where AA and NN are (0-65535)) or local-AS|no-advertise|no-export|internet or additive\n"
+#define COMMUNITY_VAL_STR \
+ "Community number in AA:NN format (where AA and NN are (0-65535)) or local-AS|no-advertise|no-export|internet|graceful-shutdown|accept-own-nexthop|accept-own|route-filter-translated-v4|route-filter-v4|route-filter-translated-v6|route-filter-v6|llgr-stale|no-llgr|blackhole|no-peer or additive\n"
#define MPLS_TE_STR "MPLS-TE specific commands\n"
#define LINK_PARAMS_STR "Configure interface link parameters\n"
#define OSPF_RI_STR "OSPF Router Information specific commands\n"
@@ -474,7 +488,29 @@ struct cmd_node {
/* Prototypes. */
extern void install_node(struct cmd_node *node);
extern void install_default(enum node_type);
-extern void install_element(enum node_type, const struct cmd_element *);
+
+struct xref_install_element {
+ struct xref xref;
+
+ const struct cmd_element *cmd_element;
+ enum node_type node_type;
+};
+
+#ifndef VTYSH_EXTRACT_PL
+#define install_element(node_type_, cmd_element_) do { \
+ static const struct xref_install_element _xref \
+ __attribute__((used)) = { \
+ .xref = XREF_INIT(XREFT_INSTALL_ELEMENT, NULL, \
+ __func__), \
+ .cmd_element = cmd_element_, \
+ .node_type = node_type_, \
+ }; \
+ XREF_LINK(_xref.xref); \
+ _install_element(node_type_, cmd_element_); \
+ } while (0)
+#endif
+
+extern void _install_element(enum node_type, const struct cmd_element *);
/* known issue with uninstall_element: changes to cmd_token->attr (i.e.
* deprecated/hidden) are not reversed. */
diff --git a/lib/command_graph.h b/lib/command_graph.h
index 179e104a57..09824460e6 100644
--- a/lib/command_graph.h
+++ b/lib/command_graph.h
@@ -31,6 +31,7 @@
#include "memory.h"
#include "vector.h"
#include "graph.h"
+#include "xref.h"
#ifdef __cplusplus
extern "C" {
@@ -105,6 +106,7 @@ struct cmd_element {
struct cmd_token *[]);
const char *name; /* symbol name for debugging */
+ struct xref xref;
};
/* text for <cr> command */
diff --git a/lib/command_parse.y b/lib/command_parse.y
index ba5225b702..8135d02b4b 100644
--- a/lib/command_parse.y
+++ b/lib/command_parse.y
@@ -496,7 +496,7 @@ terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx,
zlog_err ("----------");
while (ctx->docstr && ctx->docstr[1] != '\0')
zlog_err ("%s", strsep(&ctx->docstr, "\n"));
- zlog_err ("----------\n");
+ zlog_err ("----------");
}
graph_add_edge (finalnode, end_token_node);
diff --git a/lib/compiler.h b/lib/compiler.h
index 217a60d888..70ef8e9bc8 100644
--- a/lib/compiler.h
+++ b/lib/compiler.h
@@ -279,6 +279,29 @@ extern "C" {
#define array_size(ar) (sizeof(ar) / sizeof(ar[0]))
+/* Some insane macros to count number of varargs to a functionlike macro */
+#define PP_ARG_N( \
+ _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
+ _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \
+ _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \
+ _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \
+ _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \
+ _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \
+ _61, _62, _63, N, ...) N
+
+#define PP_RSEQ_N() \
+ 62, 61, 60, \
+ 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \
+ 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \
+ 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, \
+ 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \
+ 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \
+ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
+
+#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
+#define PP_NARG(...) PP_NARG_(_, ##__VA_ARGS__, PP_RSEQ_N())
+
+
/* sigh. this is so ugly, it overflows and wraps to being nice again.
*
* printfrr() supports "%Ld" for <int64_t>, whatever that is typedef'd to.
diff --git a/lib/ferr.h b/lib/ferr.h
index a89b595e87..4e95431cea 100644
--- a/lib/ferr.h
+++ b/lib/ferr.h
@@ -132,6 +132,8 @@ struct ferr {
#define VTYSH_FRR_END 0x0FFFFFFF
#define WATCHFRR_FERR_START 0x10000001
#define WATCHFRR_FERR_END 0x10FFFFFF
+#define PATH_FERR_START 0x11000001
+#define PATH_FERR_END 0x11FFFFFF
#define ZEBRA_FERR_START 0xF1000001
#define ZEBRA_FERR_END 0xF1FFFFFF
#define END_FERR 0xFFFFFFFF
diff --git a/lib/filter_cli.c b/lib/filter_cli.c
index 54b6cda9a5..5d66a9fc73 100644
--- a/lib/filter_cli.c
+++ b/lib/filter_cli.c
@@ -259,7 +259,7 @@ DEFPY_YANG(
/* Access-list must exist before entries. */
if (yang_dnode_exists(running_config->dnode, xpath) == false)
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
/* Use access-list data structure to fetch sequence. */
dnode = yang_dnode_get(running_config->dnode, xpath);
@@ -268,7 +268,7 @@ DEFPY_YANG(
mask_str ? mask_str : CISCO_HOST_WILDCARD_MASK,
NULL, NULL);
if (sseq == -1)
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
snprintfrr(xpath_entry, sizeof(xpath_entry),
"%s/entry[sequence='%" PRId64 "']", xpath, sseq);
@@ -436,7 +436,7 @@ DEFPY_YANG(
/* Access-list must exist before entries. */
if (yang_dnode_exists(running_config->dnode, xpath) == false)
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
/* Use access-list data structure to fetch sequence. */
dnode = yang_dnode_get(running_config->dnode, xpath);
@@ -469,7 +469,7 @@ DEFPY_YANG(
"0.0.0.0", CISCO_ANY_WILDCARD_MASK);
}
if (sseq == -1)
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
snprintfrr(xpath_entry, sizeof(xpath_entry),
"%s/entry[sequence='%" PRId64 "']", xpath, sseq);
@@ -588,7 +588,7 @@ DEFPY_YANG(
/* Access-list must exist before entries. */
if (yang_dnode_exists(running_config->dnode, xpath) == false)
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
/* Use access-list data structure to fetch sequence. */
dnode = yang_dnode_get(running_config->dnode, xpath);
@@ -601,7 +601,7 @@ DEFPY_YANG(
sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix,
exact);
if (sseq == -1)
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
snprintfrr(xpath_entry, sizeof(xpath_entry),
"%s/entry[sequence='%" PRId64 "']", xpath, sseq);
@@ -786,7 +786,7 @@ DEFPY_YANG(
/* Access-list must exist before entries. */
if (yang_dnode_exists(running_config->dnode, xpath) == false)
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
/* Use access-list data structure to fetch sequence. */
dnode = yang_dnode_get(running_config->dnode, xpath);
@@ -799,7 +799,7 @@ DEFPY_YANG(
sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix,
exact);
if (sseq == -1)
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
snprintfrr(xpath_entry, sizeof(xpath_entry),
"%s/entry[sequence='%" PRId64 "']", xpath, sseq);
@@ -979,7 +979,7 @@ DEFPY_YANG(
/* Access-list must exist before entries. */
if (yang_dnode_exists(running_config->dnode, xpath) == false)
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
/* Use access-list data structure to fetch sequence. */
dnode = yang_dnode_get(running_config->dnode, xpath);
@@ -992,7 +992,7 @@ DEFPY_YANG(
sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix,
false);
if (sseq == -1)
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
snprintfrr(xpath_entry, sizeof(xpath_entry),
"%s/entry[sequence='%" PRId64 "']", xpath, sseq);
@@ -1277,7 +1277,7 @@ static int plist_remove(struct vty *vty, const char *iptype, const char *name,
/* Access-list must exist before entries. */
if (yang_dnode_exists(running_config->dnode, xpath) == false)
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
/* Use access-list data structure to fetch sequence. */
assert(action != NULL);
@@ -1290,7 +1290,7 @@ static int plist_remove(struct vty *vty, const char *iptype, const char *name,
pl = nb_running_get_entry(dnode, NULL, true);
pentry = prefix_list_entry_lookup(pl, p, plt, -1, le, ge);
if (pentry == NULL)
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
snprintfrr(xpath_entry, sizeof(xpath_entry),
"%s/entry[sequence='%" PRId64 "']", xpath, pentry->seq);
diff --git a/lib/frr_zmq.c b/lib/frr_zmq.c
index cc11d76700..33adcd7b80 100644
--- a/lib/frr_zmq.c
+++ b/lib/frr_zmq.c
@@ -135,9 +135,8 @@ static int frrzmq_read_msg(struct thread *t)
if (read)
frrzmq_check_events(cbp, &cb->write, ZMQ_POLLOUT);
- funcname_thread_add_read_write(
- THREAD_READ, t->master, frrzmq_read_msg, cbp, cb->fd,
- &cb->read.thread, t->funcname, t->schedfrom, t->schedfrom_line);
+ _thread_add_read_write(t->xref, t->master, frrzmq_read_msg, cbp,
+ cb->fd, &cb->read.thread);
return 0;
out_err:
@@ -148,14 +147,14 @@ out_err:
return 1;
}
-int funcname_frrzmq_thread_add_read(struct thread_master *master,
- void (*msgfunc)(void *arg, void *zmqsock),
- void (*partfunc)(void *arg, void *zmqsock,
- zmq_msg_t *msg,
- unsigned partnum),
- void (*errfunc)(void *arg, void *zmqsock),
- void *arg, void *zmqsock,
- struct frrzmq_cb **cbp, debugargdef)
+int _frrzmq_thread_add_read(const struct xref_threadsched *xref,
+ struct thread_master *master,
+ void (*msgfunc)(void *arg, void *zmqsock),
+ void (*partfunc)(void *arg, void *zmqsock,
+ zmq_msg_t *msg, unsigned partnum),
+ void (*errfunc)(void *arg, void *zmqsock),
+ void *arg, void *zmqsock,
+ struct frrzmq_cb **cbp)
{
int fd, events;
size_t len;
@@ -192,13 +191,11 @@ int funcname_frrzmq_thread_add_read(struct thread_master *master,
if (events & ZMQ_POLLIN) {
thread_cancel(&cb->read.thread);
- funcname_thread_add_event(master, frrzmq_read_msg, cbp, fd,
- &cb->read.thread, funcname, schedfrom,
- fromln);
+ _thread_add_event(xref, master, frrzmq_read_msg, cbp, fd,
+ &cb->read.thread);
} else
- funcname_thread_add_read_write(
- THREAD_READ, master, frrzmq_read_msg, cbp, fd,
- &cb->read.thread, funcname, schedfrom, fromln);
+ _thread_add_read_write(xref, master, frrzmq_read_msg, cbp, fd,
+ &cb->read.thread);
return 0;
}
@@ -244,10 +241,8 @@ static int frrzmq_write_msg(struct thread *t)
if (written)
frrzmq_check_events(cbp, &cb->read, ZMQ_POLLIN);
- funcname_thread_add_read_write(THREAD_WRITE, t->master,
- frrzmq_write_msg, cbp, cb->fd,
- &cb->write.thread, t->funcname,
- t->schedfrom, t->schedfrom_line);
+ _thread_add_read_write(t->xref, t->master, frrzmq_write_msg, cbp,
+ cb->fd, &cb->write.thread);
return 0;
out_err:
@@ -257,11 +252,12 @@ out_err:
cb->write.cb_error(cb->write.arg, cb->zmqsock);
return 1;
}
-int funcname_frrzmq_thread_add_write(struct thread_master *master,
- void (*msgfunc)(void *arg, void *zmqsock),
- void (*errfunc)(void *arg, void *zmqsock),
- void *arg, void *zmqsock,
- struct frrzmq_cb **cbp, debugargdef)
+
+int _frrzmq_thread_add_write(const struct xref_threadsched *xref,
+ struct thread_master *master,
+ void (*msgfunc)(void *arg, void *zmqsock),
+ void (*errfunc)(void *arg, void *zmqsock),
+ void *arg, void *zmqsock, struct frrzmq_cb **cbp)
{
int fd, events;
size_t len;
@@ -298,13 +294,11 @@ int funcname_frrzmq_thread_add_write(struct thread_master *master,
if (events & ZMQ_POLLOUT) {
thread_cancel(&cb->write.thread);
- funcname_thread_add_event(master, frrzmq_write_msg, cbp, fd,
- &cb->write.thread, funcname,
- schedfrom, fromln);
+ _thread_add_event(xref, master, frrzmq_write_msg, cbp, fd,
+ &cb->write.thread);
} else
- funcname_thread_add_read_write(
- THREAD_WRITE, master, frrzmq_write_msg, cbp, fd,
- &cb->write.thread, funcname, schedfrom, fromln);
+ _thread_add_read_write(xref, master, frrzmq_write_msg, cbp, fd,
+ &cb->write.thread);
return 0;
}
diff --git a/lib/frr_zmq.h b/lib/frr_zmq.h
index 4303df9ccd..d30cf8a841 100644
--- a/lib/frr_zmq.h
+++ b/lib/frr_zmq.h
@@ -67,18 +67,32 @@ extern void *frrzmq_context;
extern void frrzmq_init(void);
extern void frrzmq_finish(void);
-#define debugargdef const char *funcname, const char *schedfrom, int fromln
+#define _xref_zmq_a(type, f, d, call) \
+ ({ \
+ static const struct xref_threadsched _xref \
+ __attribute__((used)) = { \
+ .xref = XREF_INIT(XREFT_THREADSCHED, NULL, __func__), \
+ .funcname = #f, \
+ .dest = #d, \
+ .thread_type = THREAD_ ## type, \
+ }; \
+ XREF_LINK(_xref.xref); \
+ call; \
+ }) \
+ /* end */
/* core event registration, one of these 2 macros should be used */
#define frrzmq_thread_add_read_msg(m, f, e, a, z, d) \
- funcname_frrzmq_thread_add_read(m, f, NULL, e, a, z, d, #f, __FILE__, \
- __LINE__)
+ _xref_zmq_a(READ, f, d, \
+ _frrzmq_thread_add_read(&_xref, m, f, NULL, e, a, z, d))
+
#define frrzmq_thread_add_read_part(m, f, e, a, z, d) \
- funcname_frrzmq_thread_add_read(m, NULL, f, e, a, z, d, #f, __FILE__, \
- __LINE__)
+ _xref_zmq_a(READ, f, d, \
+ _frrzmq_thread_add_read(&_xref, m, NULL, f, e, a, z, d))
+
#define frrzmq_thread_add_write_msg(m, f, e, a, z, d) \
- funcname_frrzmq_thread_add_write(m, f, e, a, z, d, #f, __FILE__, \
- __LINE__)
+ _xref_zmq_a(WRITE, f, d, \
+ _frrzmq_thread_add_write(&_xref, m, f, e, a, z, d))
struct cb_core;
struct frrzmq_cb;
@@ -104,16 +118,18 @@ struct frrzmq_cb;
* may schedule the event to run as soon as libfrr is back in its main
* loop.
*/
-extern int funcname_frrzmq_thread_add_read(
- struct thread_master *master, void (*msgfunc)(void *arg, void *zmqsock),
+extern int _frrzmq_thread_add_read(
+ const struct xref_threadsched *xref, struct thread_master *master,
+ void (*msgfunc)(void *arg, void *zmqsock),
void (*partfunc)(void *arg, void *zmqsock, zmq_msg_t *msg,
unsigned partnum),
void (*errfunc)(void *arg, void *zmqsock), void *arg, void *zmqsock,
- struct frrzmq_cb **cb, debugargdef);
-extern int funcname_frrzmq_thread_add_write(
- struct thread_master *master, void (*msgfunc)(void *arg, void *zmqsock),
+ struct frrzmq_cb **cb);
+extern int _frrzmq_thread_add_write(
+ const struct xref_threadsched *xref, struct thread_master *master,
+ void (*msgfunc)(void *arg, void *zmqsock),
void (*errfunc)(void *arg, void *zmqsock), void *arg, void *zmqsock,
- struct frrzmq_cb **cb, debugargdef);
+ struct frrzmq_cb **cb);
extern void frrzmq_thread_cancel(struct frrzmq_cb **cb, struct cb_core *core);
diff --git a/lib/frrlua.c b/lib/frrlua.c
index 9f9cf8c1f6..d8aaa3aa3c 100644
--- a/lib/frrlua.c
+++ b/lib/frrlua.c
@@ -2,128 +2,365 @@
* This file defines the lua interface into
* FRRouting.
*
- * Copyright (C) 2016 Cumulus Networks, Inc.
- * Donald Sharp
+ * Copyright (C) 2016-2019 Cumulus Networks, Inc.
+ * Donald Sharp, Quentin Young
*
- * This file is part of FRRouting (FRR).
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
*
- * FRR is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free Software
- * Foundation; either version 2, or (at your option) any later version.
- *
- * FRR is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
*
* You should have received a copy of the GNU General Public License along
- * with FRR; see the file COPYING. If not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <zebra.h>
-#if defined(HAVE_LUA)
+#ifdef HAVE_SCRIPTING
+
#include "prefix.h"
#include "frrlua.h"
#include "log.h"
+#include "buffer.h"
+
+/* Lua stuff */
-static int lua_zlog_debug(lua_State *L)
+/*
+ * FRR convenience functions.
+ *
+ * This section has convenience functions used to make interacting with the Lua
+ * stack easier.
+ */
+
+int frrlua_table_get_integer(lua_State *L, const char *key)
{
- int debug_lua = 1;
- const char *string = lua_tostring(L, 1);
+ int result;
- if (debug_lua)
- zlog_debug("%s", string);
+ lua_pushstring(L, key);
+ lua_gettable(L, -2);
- return 0;
+ result = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ return result;
}
-const char *get_string(lua_State *L, const char *key)
+/*
+ * Encoders.
+ *
+ * This section has functions that convert internal FRR datatypes into Lua
+ * datatypes.
+ */
+
+void lua_pushprefix(lua_State *L, const struct prefix *prefix)
{
- const char *str;
+ char buffer[PREFIX_STRLEN];
- lua_pushstring(L, key);
- lua_gettable(L, -2);
+ lua_newtable(L);
+ lua_pushstring(L, prefix2str(prefix, buffer, PREFIX_STRLEN));
+ lua_setfield(L, -2, "network");
+ lua_pushinteger(L, prefix->prefixlen);
+ lua_setfield(L, -2, "length");
+ lua_pushinteger(L, prefix->family);
+ lua_setfield(L, -2, "family");
+}
- str = (const char *)lua_tostring(L, -1);
+void *lua_toprefix(lua_State *L, int idx)
+{
+ struct prefix *p = XCALLOC(MTYPE_TMP, sizeof(struct prefix));
+
+ lua_getfield(L, idx, "network");
+ (void)str2prefix(lua_tostring(L, -1), p);
lua_pop(L, 1);
- return str;
+ return p;
}
-int get_integer(lua_State *L, const char *key)
+void lua_pushinterface(lua_State *L, const struct interface *ifp)
{
- int result;
+ lua_newtable(L);
+ lua_pushstring(L, ifp->name);
+ lua_setfield(L, -2, "name");
+ lua_pushinteger(L, ifp->ifindex);
+ lua_setfield(L, -2, "ifindex");
+ lua_pushinteger(L, ifp->status);
+ lua_setfield(L, -2, "status");
+ lua_pushinteger(L, ifp->flags);
+ lua_setfield(L, -2, "flags");
+ lua_pushinteger(L, ifp->metric);
+ lua_setfield(L, -2, "metric");
+ lua_pushinteger(L, ifp->speed);
+ lua_setfield(L, -2, "speed");
+ lua_pushinteger(L, ifp->mtu);
+ lua_setfield(L, -2, "mtu");
+ lua_pushinteger(L, ifp->mtu6);
+ lua_setfield(L, -2, "mtu6");
+ lua_pushinteger(L, ifp->bandwidth);
+ lua_setfield(L, -2, "bandwidth");
+ lua_pushinteger(L, ifp->link_ifindex);
+ lua_setfield(L, -2, "link_ifindex");
+ lua_pushinteger(L, ifp->ll_type);
+ lua_setfield(L, -2, "linklayer_type");
+}
- lua_pushstring(L, key);
- lua_gettable(L, -2);
+void *lua_tointerface(lua_State *L, int idx)
+{
+ struct interface *ifp = XCALLOC(MTYPE_TMP, sizeof(struct interface));
- result = lua_tointeger(L, -1);
+ lua_getfield(L, idx, "name");
+ strlcpy(ifp->name, lua_tostring(L, -1), sizeof(ifp->name));
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "ifindex");
+ ifp->ifindex = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "status");
+ ifp->status = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "flags");
+ ifp->flags = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "metric");
+ ifp->metric = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "speed");
+ ifp->speed = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "mtu");
+ ifp->mtu = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "mtu6");
+ ifp->mtu6 = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "bandwidth");
+ ifp->bandwidth = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "link_ifindex");
+ ifp->link_ifindex = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "linklayer_type");
+ ifp->ll_type = lua_tointeger(L, -1);
lua_pop(L, 1);
- return result;
+ return ifp;
}
-static void *lua_alloc(void *ud, void *ptr, size_t osize,
- size_t nsize)
+void lua_pushinaddr(lua_State *L, const struct in_addr *addr)
{
- (void)ud; (void)osize; /* not used */
- if (nsize == 0) {
- free(ptr);
- return NULL;
- } else
- return realloc(ptr, nsize);
+ char buf[INET_ADDRSTRLEN];
+ inet_ntop(AF_INET, addr, buf, sizeof(buf));
+
+ lua_newtable(L);
+ lua_pushinteger(L, addr->s_addr);
+ lua_setfield(L, -2, "value");
+ lua_pushstring(L, buf);
+ lua_setfield(L, -2, "string");
}
-lua_State *lua_initialize(const char *file)
+void *lua_toinaddr(lua_State *L, int idx)
{
- int status;
- lua_State *L = lua_newstate(lua_alloc, NULL);
+ struct in_addr *inaddr = XCALLOC(MTYPE_TMP, sizeof(struct in_addr));
- zlog_debug("Newstate: %p", L);
- luaL_openlibs(L);
- zlog_debug("Opened lib");
- status = luaL_loadfile(L, file);
- if (status) {
- zlog_debug("Failure to open %s %d", file, status);
- lua_close(L);
- return NULL;
- }
+ lua_getfield(L, idx, "value");
+ inaddr->s_addr = lua_tointeger(L, -1);
+ lua_pop(L, 1);
- lua_pcall(L, 0, LUA_MULTRET, 0);
- zlog_debug("Setting global function");
- lua_pushcfunction(L, lua_zlog_debug);
- lua_setglobal(L, "zlog_debug");
+ return inaddr;
+}
+
+
+void lua_pushin6addr(lua_State *L, const struct in6_addr *addr)
+{
+ char buf[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, addr, buf, sizeof(buf));
+
+ lua_newtable(L);
+ lua_pushlstring(L, (const char *)addr->s6_addr, 16);
+ lua_setfield(L, -2, "value");
+ lua_pushstring(L, buf);
+ lua_setfield(L, -2, "string");
+}
+
+void *lua_toin6addr(lua_State *L, int idx)
+{
+ struct in6_addr *in6addr = XCALLOC(MTYPE_TMP, sizeof(struct in6_addr));
+
+ lua_getfield(L, idx, "string");
+ inet_pton(AF_INET6, lua_tostring(L, -1), in6addr);
+ lua_pop(L, 1);
- return L;
+ return in6addr;
}
-void lua_setup_prefix_table(lua_State *L, const struct prefix *prefix)
+void lua_pushsockunion(lua_State *L, const union sockunion *su)
{
- char buffer[100];
+ char buf[SU_ADDRSTRLEN];
+ sockunion2str(su, buf, sizeof(buf));
lua_newtable(L);
- lua_pushstring(L, prefix2str(prefix, buffer, 100));
- lua_setfield(L, -2, "route");
- lua_pushinteger(L, prefix->family);
- lua_setfield(L, -2, "family");
- lua_setglobal(L, "prefix");
+ lua_pushlstring(L, (const char *)sockunion_get_addr(su),
+ sockunion_get_addrlen(su));
+ lua_setfield(L, -2, "value");
+ lua_pushstring(L, buf);
+ lua_setfield(L, -2, "string");
}
-enum lua_rm_status lua_run_rm_rule(lua_State *L, const char *rule)
+void *lua_tosockunion(lua_State *L, int idx)
{
- int status;
+ union sockunion *su = XCALLOC(MTYPE_TMP, sizeof(union sockunion));
+
+ lua_getfield(L, idx, "string");
+ str2sockunion(lua_tostring(L, -1), su);
+
+ return su;
+}
- lua_getglobal(L, rule);
- status = lua_pcall(L, 0, 1, 0);
- if (status) {
- zlog_debug("Executing Failure with function: %s: %d",
- rule, status);
- return LUA_RM_FAILURE;
+void lua_pushtimet(lua_State *L, const time_t *time)
+{
+ lua_pushinteger(L, *time);
+}
+
+void *lua_totimet(lua_State *L, int idx)
+{
+ time_t *t = XCALLOC(MTYPE_TMP, sizeof(time_t));
+
+ *t = lua_tointeger(L, idx);
+
+ return t;
+}
+
+void lua_pushintegerp(lua_State *L, const long long *num)
+{
+ lua_pushinteger(L, *num);
+}
+
+void *lua_tointegerp(lua_State *L, int idx)
+{
+ int isnum;
+ long long *num = XCALLOC(MTYPE_TMP, sizeof(long long));
+
+ *num = lua_tonumberx(L, idx, &isnum);
+ assert(isnum);
+
+ return num;
+}
+
+void *lua_tostringp(lua_State *L, int idx)
+{
+ char *string = XSTRDUP(MTYPE_TMP, lua_tostring(L, idx));
+
+ return string;
+}
+
+/*
+ * Logging.
+ *
+ * Lua-compatible wrappers for FRR logging functions.
+ */
+static const char *frrlua_log_thunk(lua_State *L)
+{
+ int nargs;
+
+ nargs = lua_gettop(L);
+ assert(nargs == 1);
+
+ return lua_tostring(L, 1);
+}
+
+static int frrlua_log_debug(lua_State *L)
+{
+ zlog_debug("%s", frrlua_log_thunk(L));
+ return 0;
+}
+
+static int frrlua_log_info(lua_State *L)
+{
+ zlog_info("%s", frrlua_log_thunk(L));
+ return 0;
+}
+
+static int frrlua_log_notice(lua_State *L)
+{
+ zlog_notice("%s", frrlua_log_thunk(L));
+ return 0;
+}
+
+static int frrlua_log_warn(lua_State *L)
+{
+ zlog_warn("%s", frrlua_log_thunk(L));
+ return 0;
+}
+
+static int frrlua_log_error(lua_State *L)
+{
+ zlog_err("%s", frrlua_log_thunk(L));
+ return 0;
+}
+
+static const luaL_Reg log_funcs[] = {
+ {"debug", frrlua_log_debug},
+ {"info", frrlua_log_info},
+ {"notice", frrlua_log_notice},
+ {"warn", frrlua_log_warn},
+ {"error", frrlua_log_error},
+ {},
+};
+
+void frrlua_export_logging(lua_State *L)
+{
+ lua_newtable(L);
+ luaL_setfuncs(L, log_funcs, 0);
+ lua_setglobal(L, "log");
+}
+
+/*
+ * Debugging.
+ */
+
+char *frrlua_stackdump(lua_State *L)
+{
+ int top = lua_gettop(L);
+
+ char tmpbuf[64];
+ struct buffer *buf = buffer_new(4098);
+
+ for (int i = 1; i <= top; i++) {
+ int t = lua_type(L, i);
+
+ switch (t) {
+ case LUA_TSTRING: /* strings */
+ snprintf(tmpbuf, sizeof(tmpbuf), "\"%s\"\n",
+ lua_tostring(L, i));
+ buffer_putstr(buf, tmpbuf);
+ break;
+ case LUA_TBOOLEAN: /* booleans */
+ snprintf(tmpbuf, sizeof(tmpbuf), "%s\n",
+ lua_toboolean(L, i) ? "true" : "false");
+ buffer_putstr(buf, tmpbuf);
+ break;
+ case LUA_TNUMBER: /* numbers */
+ snprintf(tmpbuf, sizeof(tmpbuf), "%g\n",
+ lua_tonumber(L, i));
+ buffer_putstr(buf, tmpbuf);
+ break;
+ default: /* other values */
+ snprintf(tmpbuf, sizeof(tmpbuf), "%s\n",
+ lua_typename(L, t));
+ buffer_putstr(buf, tmpbuf);
+ break;
+ }
}
- status = lua_tonumber(L, -1);
- return status;
+ char *result = XSTRDUP(MTYPE_TMP, buffer_getstr(buf));
+
+ buffer_free(buf);
+
+ return result;
}
-#endif
+
+#endif /* HAVE_SCRIPTING */
diff --git a/lib/frrlua.h b/lib/frrlua.h
index 40c7a67b89..6fb30938b0 100644
--- a/lib/frrlua.h
+++ b/lib/frrlua.h
@@ -1,88 +1,184 @@
/*
- * This file defines the lua interface into
- * FRRouting.
+ * Copyright (C) 2016-2019 Cumulus Networks, Inc.
+ * Donald Sharp, Quentin Young
*
- * Copyright (C) 2016 Cumulus Networks, Inc.
- * Donald Sharp
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
*
- * This file is part of FRRouting (FRR).
- *
- * FRR is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free Software
- * Foundation; either version 2, or (at your option) any later version.
- *
- * FRR is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
*
* You should have received a copy of the GNU General Public License along
- * with FRR; see the file COPYING. If not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef __LUA_H__
-#define __LUA_H__
+#ifndef __FRRLUA_H__
+#define __FRRLUA_H__
+
+#include <zebra.h>
-#if defined(HAVE_LUA)
+#ifdef HAVE_SCRIPTING
-#include "lua.h"
-#include "lualib.h"
-#include "lauxlib.h"
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include "prefix.h"
+#include "frrscript.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
- * These functions are helper functions that
- * try to glom some of the lua_XXX functionality
- * into what we actually need, instead of having
- * to make multiple calls to set up what
- * we want
+ * gcc-10 is complaining about the wrapper function
+ * not being compatible with lua_pushstring returning
+ * a char *. Let's wrapper it here to make our life
+ * easier
+ */
+static inline void lua_pushstring_wrapper(lua_State *L, const char *str)
+{
+ (void)lua_pushstring(L, str);
+}
+
+/*
+ * Converts a prefix to a Lua value and pushes it on the stack.
+ */
+void lua_pushprefix(lua_State *L, const struct prefix *prefix);
+
+/*
+ * Converts the Lua value at idx to a prefix.
+ *
+ * Returns:
+ * struct prefix allocated with MTYPE_TMP
+ */
+void *lua_toprefix(lua_State *L, int idx);
+
+/*
+ * Converts an interface to a Lua value and pushes it on the stack.
+ */
+void lua_pushinterface(lua_State *L, const struct interface *ifp);
+
+/*
+ * Converts the Lua value at idx to an interface.
+ *
+ * Returns:
+ * struct interface allocated with MTYPE_TMP. This interface is not hooked
+ * to anything, nor is it inserted in the global interface tree.
*/
-enum lua_rm_status {
- /*
- * Script function run failure. This will translate into a
- * deny
- */
- LUA_RM_FAILURE = 0,
- /*
- * No Match was found for the route map function
- */
- LUA_RM_NOMATCH,
- /*
- * Match was found but no changes were made to the
- * incoming data.
- */
- LUA_RM_MATCH,
- /*
- * Match was found and data was modified, so
- * figure out what changed
- */
- LUA_RM_MATCH_AND_CHANGE,
-};
+void *lua_tointerface(lua_State *L, int idx);
/*
- * Open up the lua.scr file and parse
- * initial global values, if any.
+ * Converts an in_addr to a Lua value and pushes it on the stack.
*/
-lua_State *lua_initialize(const char *file);
+void lua_pushinaddr(lua_State *L, const struct in_addr *addr);
-void lua_setup_prefix_table(lua_State *L, const struct prefix *prefix);
+/*
+ * Converts the Lua value at idx to an in_addr.
+ *
+ * Returns:
+ * struct in_addr allocated with MTYPE_TMP.
+ */
+void *lua_toinaddr(lua_State *L, int idx);
-enum lua_rm_status lua_run_rm_rule(lua_State *L, const char *rule);
+/*
+ * Converts an in6_addr to a Lua value and pushes it on the stack.
+ */
+void lua_pushin6addr(lua_State *L, const struct in6_addr *addr);
/*
- * Get particular string/integer information
- * from a table. It is *assumed* that
- * the table has already been selected
+ * Converts the Lua value at idx to an in6_addr.
+ *
+ * Returns:
+ * struct in6_addr allocated with MTYPE_TMP.
*/
-const char *get_string(lua_State *L, const char *key);
-int get_integer(lua_State *L, const char *key);
+void *lua_toin6addr(lua_State *L, int idx);
+
+/*
+ * Converts a time_t to a Lua value and pushes it on the stack.
+ */
+void lua_pushtimet(lua_State *L, const time_t *time);
+
+/*
+ * Converts the Lua value at idx to a time_t.
+ *
+ * Returns:
+ * time_t allocated with MTYPE_TMP.
+ */
+void *lua_totimet(lua_State *L, int idx);
+
+/*
+ * Converts a sockunion to a Lua value and pushes it on the stack.
+ */
+void lua_pushsockunion(lua_State *L, const union sockunion *su);
+
+/*
+ * Converts the Lua value at idx to a sockunion.
+ *
+ * Returns:
+ * sockunion allocated with MTYPE_TMP.
+ */
+void *lua_tosockunion(lua_State *L, int idx);
+
+/*
+ * Converts an int to a Lua value and pushes it on the stack.
+ */
+void lua_pushintegerp(lua_State *L, const long long *num);
+
+/*
+ * Converts the Lua value at idx to an int.
+ *
+ * Returns:
+ * int allocated with MTYPE_TMP.
+ */
+void *lua_tointegerp(lua_State *L, int idx);
+
+/*
+ * Pop string.
+ *
+ * Sets *string to a copy of the string at the top of the stack. The copy is
+ * allocated with MTYPE_TMP and the caller is responsible for freeing it.
+ */
+void *lua_tostringp(lua_State *L, int idx);
+
+/*
+ * Retrieve an integer from table on the top of the stack.
+ *
+ * key
+ * Key of string value in table
+ */
+int frrlua_table_get_integer(lua_State *L, const char *key);
+
+/*
+ * Exports a new table containing bindings to FRR zlog functions into the
+ * global namespace.
+ *
+ * From Lua, these functions may be accessed as:
+ *
+ * - log.debug()
+ * - log.info()
+ * - log.warn()
+ * - log.error()
+ *
+ * They take a single string argument.
+ */
+void frrlua_export_logging(lua_State *L);
+
+/*
+ * Dump Lua stack to a string.
+ *
+ * Return value must be freed with XFREE(MTYPE_TMP, ...);
+ */
+char *frrlua_stackdump(lua_State *L);
#ifdef __cplusplus
}
#endif
-#endif
-#endif
+#endif /* HAVE_SCRIPTING */
+
+#endif /* __FRRLUA_H__ */
diff --git a/lib/frrscript.c b/lib/frrscript.c
new file mode 100644
index 0000000000..10d400886d
--- /dev/null
+++ b/lib/frrscript.c
@@ -0,0 +1,272 @@
+/* Scripting foo
+ * Copyright (C) 2020 NVIDIA Corporation
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#ifdef HAVE_SCRIPTING
+
+#include <stdarg.h>
+#include <lua.h>
+
+#include "frrscript.h"
+#include "frrlua.h"
+#include "memory.h"
+#include "hash.h"
+#include "log.h"
+
+
+DEFINE_MTYPE_STATIC(LIB, SCRIPT, "Scripting");
+
+/* Codecs */
+
+struct frrscript_codec frrscript_codecs_lib[] = {
+ {.typename = "integer",
+ .encoder = (encoder_func)lua_pushintegerp,
+ .decoder = lua_tointegerp},
+ {.typename = "string",
+ .encoder = (encoder_func)lua_pushstring_wrapper,
+ .decoder = lua_tostringp},
+ {.typename = "prefix",
+ .encoder = (encoder_func)lua_pushprefix,
+ .decoder = lua_toprefix},
+ {.typename = "interface",
+ .encoder = (encoder_func)lua_pushinterface,
+ .decoder = lua_tointerface},
+ {.typename = "in_addr",
+ .encoder = (encoder_func)lua_pushinaddr,
+ .decoder = lua_toinaddr},
+ {.typename = "in6_addr",
+ .encoder = (encoder_func)lua_pushin6addr,
+ .decoder = lua_toin6addr},
+ {.typename = "sockunion",
+ .encoder = (encoder_func)lua_pushsockunion,
+ .decoder = lua_tosockunion},
+ {.typename = "time_t",
+ .encoder = (encoder_func)lua_pushtimet,
+ .decoder = lua_totimet},
+ {}};
+
+/* Type codecs */
+
+struct hash *codec_hash;
+char scriptdir[MAXPATHLEN];
+
+static unsigned int codec_hash_key(const void *data)
+{
+ const struct frrscript_codec *c = data;
+
+ return string_hash_make(c->typename);
+}
+
+static bool codec_hash_cmp(const void *d1, const void *d2)
+{
+ const struct frrscript_codec *e1 = d1;
+ const struct frrscript_codec *e2 = d2;
+
+ return strmatch(e1->typename, e2->typename);
+}
+
+static void *codec_alloc(void *arg)
+{
+ struct frrscript_codec *tmp = arg;
+
+ struct frrscript_codec *e =
+ XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript_codec));
+ e->typename = XSTRDUP(MTYPE_SCRIPT, tmp->typename);
+ e->encoder = tmp->encoder;
+ e->decoder = tmp->decoder;
+
+ return e;
+}
+
+#if 0
+static void codec_free(struct codec *c)
+{
+ XFREE(MTYPE_TMP, c->typename);
+ XFREE(MTYPE_TMP, c);
+}
+#endif
+
+/* Generic script APIs */
+
+int frrscript_call(struct frrscript *fs, struct frrscript_env *env)
+{
+ struct frrscript_codec c = {};
+ const void *arg;
+ const char *bindname;
+
+ /* Encode script arguments */
+ for (int i = 0; env && env[i].val != NULL; i++) {
+ bindname = env[i].name;
+ c.typename = env[i].typename;
+ arg = env[i].val;
+
+ struct frrscript_codec *codec = hash_lookup(codec_hash, &c);
+ assert(codec && "No encoder for type");
+ codec->encoder(fs->L, arg);
+
+ lua_setglobal(fs->L, bindname);
+ }
+
+ int ret = lua_pcall(fs->L, 0, 0, 0);
+
+ switch (ret) {
+ case LUA_OK:
+ break;
+ case LUA_ERRRUN:
+ zlog_err("Script '%s' runtime error: %s", fs->name,
+ lua_tostring(fs->L, -1));
+ break;
+ case LUA_ERRMEM:
+ zlog_err("Script '%s' memory error: %s", fs->name,
+ lua_tostring(fs->L, -1));
+ break;
+ case LUA_ERRERR:
+ zlog_err("Script '%s' error handler error: %s", fs->name,
+ lua_tostring(fs->L, -1));
+ break;
+ case LUA_ERRGCMM:
+ zlog_err("Script '%s' garbage collector error: %s", fs->name,
+ lua_tostring(fs->L, -1));
+ break;
+ default:
+ zlog_err("Script '%s' unknown error: %s", fs->name,
+ lua_tostring(fs->L, -1));
+ break;
+ }
+
+ if (ret != LUA_OK) {
+ lua_pop(fs->L, 1);
+ goto done;
+ }
+
+done:
+ /* LUA_OK is 0, so we can just return lua_pcall's result directly */
+ return ret;
+}
+
+void *frrscript_get_result(struct frrscript *fs,
+ const struct frrscript_env *result)
+{
+ void *r;
+ struct frrscript_codec c = {.typename = result->typename};
+
+ struct frrscript_codec *codec = hash_lookup(codec_hash, &c);
+ assert(codec && "No encoder for type");
+
+ if (!codec->decoder) {
+ zlog_err("No script decoder for type '%s'", result->typename);
+ return NULL;
+ }
+
+ lua_getglobal(fs->L, result->name);
+ r = codec->decoder(fs->L, -1);
+ lua_pop(fs->L, 1);
+
+ return r;
+}
+
+void frrscript_register_type_codec(struct frrscript_codec *codec)
+{
+ struct frrscript_codec c = *codec;
+
+ if (hash_lookup(codec_hash, &c)) {
+ zlog_backtrace(LOG_ERR);
+ assert(!"Type codec double-registered.");
+ }
+
+ assert(hash_get(codec_hash, &c, codec_alloc));
+}
+
+void frrscript_register_type_codecs(struct frrscript_codec *codecs)
+{
+ for (int i = 0; codecs[i].typename != NULL; i++)
+ frrscript_register_type_codec(&codecs[i]);
+}
+
+struct frrscript *frrscript_load(const char *name,
+ int (*load_cb)(struct frrscript *))
+{
+ struct frrscript *fs = XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript));
+
+ fs->name = XSTRDUP(MTYPE_SCRIPT, name);
+ fs->L = luaL_newstate();
+ frrlua_export_logging(fs->L);
+
+ char fname[MAXPATHLEN * 2];
+ snprintf(fname, sizeof(fname), "%s/%s.lua", scriptdir, fs->name);
+
+ int ret = luaL_loadfile(fs->L, fname);
+
+ switch (ret) {
+ case LUA_OK:
+ break;
+ case LUA_ERRSYNTAX:
+ zlog_err("Failed loading script '%s': syntax error: %s", fname,
+ lua_tostring(fs->L, -1));
+ break;
+ case LUA_ERRMEM:
+ zlog_err("Failed loading script '%s': out-of-memory error: %s",
+ fname, lua_tostring(fs->L, -1));
+ break;
+ case LUA_ERRGCMM:
+ zlog_err(
+ "Failed loading script '%s': garbage collector error: %s",
+ fname, lua_tostring(fs->L, -1));
+ break;
+ case LUA_ERRFILE:
+ zlog_err("Failed loading script '%s': file read error: %s",
+ fname, lua_tostring(fs->L, -1));
+ break;
+ default:
+ zlog_err("Failed loading script '%s': unknown error: %s", fname,
+ lua_tostring(fs->L, -1));
+ break;
+ }
+
+ if (ret != LUA_OK)
+ goto fail;
+
+ if (load_cb && (*load_cb)(fs) != 0)
+ goto fail;
+
+ return fs;
+fail:
+ frrscript_unload(fs);
+ return NULL;
+}
+
+void frrscript_unload(struct frrscript *fs)
+{
+ lua_close(fs->L);
+ XFREE(MTYPE_SCRIPT, fs->name);
+ XFREE(MTYPE_SCRIPT, fs);
+}
+
+void frrscript_init(const char *sd)
+{
+ codec_hash = hash_create(codec_hash_key, codec_hash_cmp,
+ "Lua type encoders");
+
+ strlcpy(scriptdir, sd, sizeof(scriptdir));
+
+ /* Register core library types */
+ frrscript_register_type_codecs(frrscript_codecs_lib);
+}
+
+#endif /* HAVE_SCRIPTING */
diff --git a/lib/frrscript.h b/lib/frrscript.h
new file mode 100644
index 0000000000..f4057f531b
--- /dev/null
+++ b/lib/frrscript.h
@@ -0,0 +1,138 @@
+/* Scripting foo
+ * Copyright (C) 2020 NVIDIA Corporation
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __FRRSCRIPT_H__
+#define __FRRSCRIPT_H__
+
+#include <zebra.h>
+
+#ifdef HAVE_SCRIPTING
+
+#include <lua.h>
+#include "frrlua.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void (*encoder_func)(lua_State *, const void *);
+typedef void *(*decoder_func)(lua_State *, int);
+
+struct frrscript_codec {
+ const char *typename;
+ encoder_func encoder;
+ decoder_func decoder;
+};
+
+struct frrscript {
+ /* Script name */
+ char *name;
+
+ /* Lua state */
+ struct lua_State *L;
+};
+
+struct frrscript_env {
+ /* Value type */
+ const char *typename;
+
+ /* Binding name */
+ const char *name;
+
+ /* Value */
+ const void *val;
+};
+
+/*
+ * Create new FRR script.
+ */
+struct frrscript *frrscript_load(const char *name,
+ int (*load_cb)(struct frrscript *));
+
+/*
+ * Destroy FRR script.
+ */
+void frrscript_unload(struct frrscript *fs);
+
+/*
+ * Register a Lua codec for a type.
+ *
+ * tname
+ * Name of type; e.g., "peer", "ospf_interface", etc. Chosen at will.
+ *
+ * codec(s)
+ * Function pointer to codec struct. Encoder function should push a Lua
+ * table representing the passed argument - which will have the C type
+ * associated with the chosen 'tname' to the provided stack. The decoder
+ * function should pop a value from the top of the stack and return a heap
+ * chunk containing that value. Allocations should be made with MTYPE_TMP.
+ *
+ * If using the plural function variant, pass a NULL-terminated array.
+ *
+ */
+void frrscript_register_type_codec(struct frrscript_codec *codec);
+void frrscript_register_type_codecs(struct frrscript_codec *codecs);
+
+/*
+ * Initialize scripting subsystem. Call this before anything else.
+ *
+ * scriptdir
+ * Directory in which to look for scripts
+ */
+void frrscript_init(const char *scriptdir);
+
+
+/*
+ * Call script.
+ *
+ * fs
+ * The script to call; this is obtained from frrscript_load().
+ *
+ * env
+ * The script's environment. Specify this as an array of frrscript_env.
+ *
+ * Returns:
+ * 0 if the script ran successfully, nonzero otherwise.
+ */
+int frrscript_call(struct frrscript *fs, struct frrscript_env *env);
+
+
+/*
+ * Get result from finished script.
+ *
+ * fs
+ * The script. This script must have been run already.
+ *
+ * result
+ * The result to extract from the script.
+ * This reuses the frrscript_env type, but only the typename and name fields
+ * need to be set. The value is returned directly.
+ *
+ * Returns:
+ * The script result of the specified name and type, or NULL.
+ */
+void *frrscript_get_result(struct frrscript *fs,
+ const struct frrscript_env *result);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* HAVE_SCRIPTING */
+
+#endif /* __FRRSCRIPT_H__ */
diff --git a/lib/hash.c b/lib/hash.c
index ed429b77d0..ec616ee724 100644
--- a/lib/hash.c
+++ b/lib/hash.c
@@ -32,7 +32,7 @@
#include "libfrr_trace.h"
DEFINE_MTYPE_STATIC(LIB, HASH, "Hash")
-DEFINE_MTYPE_STATIC(LIB, HASH_BACKET, "Hash Bucket")
+DEFINE_MTYPE_STATIC(LIB, HASH_BUCKET, "Hash Bucket")
DEFINE_MTYPE_STATIC(LIB, HASH_INDEX, "Hash Index")
static pthread_mutex_t _hashes_mtx = PTHREAD_MUTEX_INITIALIZER;
@@ -168,7 +168,7 @@ void *hash_get(struct hash *hash, void *data, void *(*alloc_func)(void *))
index = key & (hash->size - 1);
}
- bucket = XCALLOC(MTYPE_HASH_BACKET, sizeof(struct hash_bucket));
+ bucket = XCALLOC(MTYPE_HASH_BUCKET, sizeof(struct hash_bucket));
bucket->data = newdata;
bucket->key = key;
bucket->next = hash->index[index];
@@ -239,7 +239,7 @@ void *hash_release(struct hash *hash, void *data)
hash_update_ssq(hash, oldlen, newlen);
ret = bucket->data;
- XFREE(MTYPE_HASH_BACKET, bucket);
+ XFREE(MTYPE_HASH_BUCKET, bucket);
hash->count--;
break;
}
@@ -302,7 +302,7 @@ void hash_clean(struct hash *hash, void (*free_func)(void *))
if (free_func)
(*free_func)(hb->data);
- XFREE(MTYPE_HASH_BACKET, hb);
+ XFREE(MTYPE_HASH_BUCKET, hb);
hash->count--;
}
hash->index[i] = NULL;
diff --git a/lib/hash.h b/lib/hash.h
index 23e93b6d7d..47d951a34b 100644
--- a/lib/hash.h
+++ b/lib/hash.h
@@ -76,7 +76,7 @@ struct hash {
/* Data compare function. */
bool (*hash_cmp)(const void *, const void *);
- /* Backet alloc. */
+ /* Bucket alloc. */
unsigned long count;
struct hashstats stats;
diff --git a/lib/if.c b/lib/if.c
index c707c4c6d9..7ec53d356d 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -351,6 +351,40 @@ struct interface *if_lookup_by_index(ifindex_t ifindex, vrf_id_t vrf_id)
return NULL;
}
+/* Interface existance check by index. */
+struct interface *if_vrf_lookup_by_index_next(ifindex_t ifindex,
+ vrf_id_t vrf_id)
+{
+ struct vrf *vrf = vrf_lookup_by_id(vrf_id);
+ struct interface *tmp_ifp;
+ bool found = false;
+
+ if (!vrf)
+ return NULL;
+
+ if (ifindex == 0) {
+ tmp_ifp = RB_MIN(if_index_head, &vrf->ifaces_by_index);
+ /* skip the vrf interface */
+ if (tmp_ifp && if_is_vrf(tmp_ifp))
+ ifindex = tmp_ifp->ifindex;
+ else
+ return tmp_ifp;
+ }
+
+ RB_FOREACH (tmp_ifp, if_index_head, &vrf->ifaces_by_index) {
+ if (found) {
+ /* skip the vrf interface */
+ if (tmp_ifp && if_is_vrf(tmp_ifp))
+ continue;
+ else
+ return tmp_ifp;
+ }
+ if (tmp_ifp->ifindex == ifindex)
+ found = true;
+ }
+ return NULL;
+}
+
const char *ifindex2ifname(ifindex_t ifindex, vrf_id_t vrf_id)
{
struct interface *ifp;
@@ -802,70 +836,6 @@ void if_dump_all(void)
if_dump(ifp);
}
-#if 0
-/* For debug purpose. */
-DEFUN (show_address,
- show_address_cmd,
- "show address [vrf NAME]",
- SHOW_STR
- "address\n"
- VRF_CMD_HELP_STR)
-{
- int idx_vrf = 3;
- struct listnode *node;
- struct interface *ifp;
- struct connected *ifc;
- struct prefix *p;
- vrf_id_t vrf_id = VRF_DEFAULT;
-
- if (argc > 2)
- VRF_GET_ID (vrf_id, argv[idx_vrf]->arg);
-
- FOR_ALL_INTERFACES (vrf, ifp) {
- for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, ifc)) {
- p = ifc->address;
-
- if (p->family == AF_INET)
- vty_out (vty, "%pFX\n", p);
- }
- }
- return CMD_SUCCESS;
-}
-
-DEFUN (show_address_vrf_all,
- show_address_vrf_all_cmd,
- "show address vrf all",
- SHOW_STR
- "address\n"
- VRF_ALL_CMD_HELP_STR)
-{
- struct vrf *vrf;
- struct listnode *node;
- struct interface *ifp;
- struct connected *ifc;
- struct prefix *p;
-
- RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
- {
- if (RB_EMPTY (if_name_head, &vrf->ifaces_by_name))
- continue;
-
- vty_out (vty, "\nVRF %s(%u)\n\n",
- VRF_LOGNAME(vrf), vrf->vrf_id);
-
- FOR_ALL_INTERFACES (vrf, ifp) {
- for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, ifc)) {
- p = ifc->address;
-
- if (p->family == AF_INET)
- vty_out (vty, "%pFX\n", p);
- }
- }
- }
- return CMD_SUCCESS;
-}
-#endif
-
/* Allocate connected structure. */
struct connected *connected_new(void)
{
@@ -1083,84 +1053,6 @@ struct connected *connected_get_linklocal(struct interface *ifp)
return c;
}
-#if 0 /* this route_table of struct connected's is unused \
- * however, it would be good to use a route_table rather than \
- * a list.. \
- */
-/* Interface looking up by interface's address. */
-/* Interface's IPv4 address reverse lookup table. */
-struct route_table *ifaddr_ipv4_table;
-/* struct route_table *ifaddr_ipv6_table; */
-
-static void
-ifaddr_ipv4_add (struct in_addr *ifaddr, struct interface *ifp)
-{
- struct route_node *rn;
- struct prefix_ipv4 p;
-
- p.family = AF_INET;
- p.prefixlen = IPV4_MAX_PREFIXLEN;
- p.prefix = *ifaddr;
-
- rn = route_node_get (ifaddr_ipv4_table, (struct prefix *) &p);
- if (rn)
- {
- route_unlock_node (rn);
- zlog_info("ifaddr_ipv4_add(): address %pI4 is already added",
- ifaddr);
- return;
- }
- rn->info = ifp;
-}
-
-static void
-ifaddr_ipv4_delete (struct in_addr *ifaddr, struct interface *ifp)
-{
- struct route_node *rn;
- struct prefix_ipv4 p;
-
- p.family = AF_INET;
- p.prefixlen = IPV4_MAX_PREFIXLEN;
- p.prefix = *ifaddr;
-
- rn = route_node_lookup (ifaddr_ipv4_table, (struct prefix *) &p);
- if (! rn)
- {
- zlog_info("%s: can't find address %pI4", __func__, ifaddr);
- return;
- }
- rn->info = NULL;
- route_unlock_node (rn);
- route_unlock_node (rn);
-}
-
-/* Lookup interface by interface's IP address or interface index. */
-static struct interface *
-ifaddr_ipv4_lookup (struct in_addr *addr, ifindex_t ifindex)
-{
- struct prefix_ipv4 p;
- struct route_node *rn;
- struct interface *ifp;
-
- if (addr)
- {
- p.family = AF_INET;
- p.prefixlen = IPV4_MAX_PREFIXLEN;
- p.prefix = *addr;
-
- rn = route_node_lookup (ifaddr_ipv4_table, (struct prefix *) &p);
- if (! rn)
- return NULL;
-
- ifp = rn->info;
- route_unlock_node (rn);
- return ifp;
- }
- else
- return if_lookup_by_index(ifindex, VRF_DEFAULT);
-}
-#endif /* ifaddr_ipv4_table */
-
void if_terminate(struct vrf *vrf)
{
struct interface *ifp;
diff --git a/lib/if.h b/lib/if.h
index a2a40d0957..5bf52936ae 100644
--- a/lib/if.h
+++ b/lib/if.h
@@ -513,6 +513,8 @@ extern struct interface *if_create_name(const char *name, vrf_id_t vrf_id);
/* Create new interface, adds to index list only */
extern struct interface *if_create_ifindex(ifindex_t ifindex, vrf_id_t vrf_id);
extern struct interface *if_lookup_by_index(ifindex_t, vrf_id_t vrf_id);
+extern struct interface *if_vrf_lookup_by_index_next(ifindex_t ifindex,
+ vrf_id_t vrf_id);
extern struct interface *if_lookup_by_index_all_vrf(ifindex_t);
extern struct interface *if_lookup_exact_address(const void *matchaddr,
int family, vrf_id_t vrf_id);
diff --git a/lib/lib_vty.c b/lib/lib_vty.c
index cd8b5c9809..128261a39c 100644
--- a/lib/lib_vty.c
+++ b/lib/lib_vty.c
@@ -43,10 +43,14 @@
#include "vty.h"
#include "command.h"
-#ifdef HAVE_MALLINFO
+#if defined(HAVE_MALLINFO2) || defined(HAVE_MALLINFO)
static int show_memory_mallinfo(struct vty *vty)
{
+#if defined(HAVE_MALLINFO2)
+ struct mallinfo2 minfo = mallinfo2();
+#elif defined(HAVE_MALLINFO)
struct mallinfo minfo = mallinfo();
+#endif
char buf[MTYPE_MEMSTR_LEN];
vty_out(vty, "System allocator statistics:\n");
diff --git a/lib/libfrr.c b/lib/libfrr.c
index 8e7777a1a9..51b97369c9 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -43,6 +43,7 @@
#include "frrcu.h"
#include "frr_pthread.h"
#include "defaults.h"
+#include "frrscript.h"
DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm))
DEFINE_HOOK(frr_very_late_init, (struct thread_master * tm), (tm))
@@ -55,6 +56,7 @@ char frr_vtydir[256];
const char frr_dbdir[] = DAEMON_DB_DIR;
#endif
const char frr_moduledir[] = MODULE_PATH;
+const char frr_scriptdir[] = SCRIPT_PATH;
char frr_protoname[256] = "NONE";
char frr_protonameinst[256] = "NONE";
@@ -69,6 +71,7 @@ static char vtypath_default[512];
bool debug_memstats_at_exit = false;
static bool nodetach_term, nodetach_daemon;
+static uint64_t startup_fds;
static char comb_optstr[256];
static struct option comb_lo[64];
@@ -100,6 +103,7 @@ static void opt_extend(const struct optspec *os)
#define OPTION_DB_FILE 1006
#define OPTION_LOGGING 1007
#define OPTION_LIMIT_FDS 1008
+#define OPTION_SCRIPTDIR 1009
static const struct option lo_always[] = {
{"help", no_argument, NULL, 'h'},
@@ -110,6 +114,7 @@ static const struct option lo_always[] = {
{"pathspace", required_argument, NULL, 'N'},
{"vty_socket", required_argument, NULL, OPTION_VTYSOCK},
{"moduledir", required_argument, NULL, OPTION_MODULEDIR},
+ {"scriptdir", required_argument, NULL, OPTION_SCRIPTDIR},
{"log", required_argument, NULL, OPTION_LOG},
{"log-level", required_argument, NULL, OPTION_LOGLEVEL},
{"tcli", no_argument, NULL, OPTION_TCLI},
@@ -126,6 +131,7 @@ static const struct optspec os_always = {
" -N, --pathspace Insert prefix into config & socket paths\n"
" --vty_socket Override vty socket path\n"
" --moduledir Override modules directory\n"
+ " --scriptdir Override scripts directory\n"
" --log Set Logging to stdout, syslog, or file:<name>\n"
" --log-level Set Logging Level to use, debug, info, warn, etc\n"
" --tcli Use transaction-based CLI\n"
@@ -336,6 +342,28 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv)
strlcpy(frr_protonameinst, di->logname, sizeof(frr_protonameinst));
di->cli_mode = FRR_CLI_CLASSIC;
+
+ /* we may be starting with extra FDs open for whatever purpose,
+ * e.g. logging, some module, etc. Recording them here allows later
+ * checking whether an fd is valid for such extension purposes,
+ * without this we could end up e.g. logging to a BGP session fd.
+ */
+ startup_fds = 0;
+ for (int i = 0; i < 64; i++) {
+ struct stat st;
+
+ if (fstat(i, &st))
+ continue;
+ if (S_ISDIR(st.st_mode) || S_ISBLK(st.st_mode))
+ continue;
+
+ startup_fds |= UINT64_C(0x1) << (uint64_t)i;
+ }
+}
+
+bool frr_is_startup_fd(int fd)
+{
+ return !!(startup_fds & (UINT64_C(0x1) << (uint64_t)fd));
}
void frr_opt_add(const char *optstr, const struct option *longopts,
@@ -533,6 +561,14 @@ static int frr_opt(int opt)
}
di->module_path = optarg;
break;
+ case OPTION_SCRIPTDIR:
+ if (di->script_path) {
+ fprintf(stderr, "--scriptdir option specified more than once!\n");
+ errors++;
+ break;
+ }
+ di->script_path = optarg;
+ break;
case OPTION_TCLI:
di->cli_mode = FRR_CLI_TRANSACTIONAL;
break;
@@ -717,6 +753,9 @@ struct thread_master *frr_init(void)
lib_cmd_init();
frr_pthread_init();
+#ifdef HAVE_SCRIPTING
+ frrscript_init(di->script_path ? di->script_path : frr_scriptdir);
+#endif
log_ref_init();
log_ref_vty_init();
diff --git a/lib/libfrr.h b/lib/libfrr.h
index 2e4dcbe093..825f502bdf 100644
--- a/lib/libfrr.h
+++ b/lib/libfrr.h
@@ -81,6 +81,7 @@ struct frr_daemon_info {
#endif
const char *vty_path;
const char *module_path;
+ const char *script_path;
const char *pathspace;
bool zpathspace;
@@ -137,7 +138,8 @@ extern __attribute__((__noreturn__)) void frr_help_exit(int status);
extern struct thread_master *frr_init(void);
extern const char *frr_get_progname(void);
extern enum frr_cli_mode frr_get_cli_mode(void);
-uint32_t frr_get_fd_limit(void);
+extern uint32_t frr_get_fd_limit(void);
+extern bool frr_is_startup_fd(int fd);
DECLARE_HOOK(frr_late_init, (struct thread_master * tm), (tm))
DECLARE_HOOK(frr_very_late_init, (struct thread_master * tm), (tm))
@@ -162,6 +164,7 @@ extern char frr_zclientpath[256];
extern const char frr_sysconfdir[];
extern char frr_vtydir[256];
extern const char frr_moduledir[];
+extern const char frr_scriptdir[];
extern char frr_protoname[];
extern char frr_protonameinst[];
diff --git a/lib/link_state.c b/lib/link_state.c
new file mode 100644
index 0000000000..6bd7ef703c
--- /dev/null
+++ b/lib/link_state.c
@@ -0,0 +1,1282 @@
+/*
+ * Link State Database - link_state.c
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ *
+ * Copyright (C) 2020 Orange http://www.orange.com
+ *
+ * This file is part of Free Range Routing (FRR).
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "if.h"
+#include "linklist.h"
+#include "log.h"
+#include "command.h"
+#include "termtable.h"
+#include "memory.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "zclient.h"
+#include "stream.h"
+#include "link_state.h"
+
+/* Link State Memory allocation */
+DEFINE_MTYPE_STATIC(LIB, LS_DB, "Link State Database")
+
+/**
+ * Link State Node management functions
+ */
+struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr rid,
+ struct in6_addr rid6)
+{
+ struct ls_node *new;
+
+ if (adv.origin == NONE)
+ return NULL;
+
+ new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_node));
+ new->adv = adv;
+ if (!IPV4_NET0(rid.s_addr)) {
+ new->router_id = rid;
+ SET_FLAG(new->flags, LS_NODE_ROUTER_ID);
+ } else {
+ if (adv.origin == OSPFv2 || adv.origin == STATIC
+ || adv.origin == DIRECT) {
+ new->router_id = adv.id.ip.addr;
+ SET_FLAG(new->flags, LS_NODE_ROUTER_ID);
+ }
+ }
+ if (!IN6_IS_ADDR_UNSPECIFIED(&rid6)) {
+ new->router6_id = rid6;
+ SET_FLAG(new->flags, LS_NODE_ROUTER_ID6);
+ }
+ return new;
+}
+
+void ls_node_del(struct ls_node *node)
+{
+ XFREE(MTYPE_LS_DB, node);
+ node = NULL;
+}
+
+int ls_node_same(struct ls_node *n1, struct ls_node *n2)
+{
+ if ((n1 && !n2) || (!n1 && n2))
+ return 0;
+
+ if (n1 == n2)
+ return 1;
+
+ if (n1->flags != n2->flags)
+ return 0;
+
+ if (n1->adv.origin != n2->adv.origin)
+ return 0;
+
+ if (!memcmp(&n1->adv.id, &n2->adv.id, sizeof(struct ls_node_id)))
+ return 0;
+
+ /* Do we need to test individually each field, instead performing a
+ * global memcmp? There is a risk that an old value that is bit masked
+ * i.e. corresponding flag = 0, will result into a false negative
+ */
+ if (!memcmp(n1, n2, sizeof(struct ls_node)))
+ return 0;
+ else
+ return 1;
+}
+
+/**
+ * Link State Attributes management functions
+ */
+struct ls_attributes *ls_attributes_new(struct ls_node_id adv,
+ struct in_addr local,
+ struct in6_addr local6,
+ uint32_t local_id)
+{
+ struct ls_attributes *new;
+
+ if (adv.origin == NONE)
+ return NULL;
+
+ new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_attributes));
+ new->adv = adv;
+ if (!IPV4_NET0(local.s_addr)) {
+ new->standard.local = local;
+ SET_FLAG(new->flags, LS_ATTR_LOCAL_ADDR);
+ }
+ if (!IN6_IS_ADDR_UNSPECIFIED(&local6)) {
+ new->standard.local6 = local6;
+ SET_FLAG(new->flags, LS_ATTR_LOCAL_ADDR6);
+ }
+ if (local_id != 0) {
+ new->standard.local_id = local_id;
+ SET_FLAG(new->flags, LS_ATTR_LOCAL_ID);
+ }
+
+ /* Check that almost one identifier is set */
+ if (!CHECK_FLAG(new->flags, LS_ATTR_LOCAL_ADDR | LS_ATTR_LOCAL_ADDR6
+ | LS_ATTR_LOCAL_ID)) {
+ XFREE(MTYPE_LS_DB, new);
+ return NULL;
+ }
+
+ return new;
+}
+
+void ls_attributes_del(struct ls_attributes *attr)
+{
+ if (!attr)
+ return;
+
+ if (attr->srlgs)
+ XFREE(MTYPE_LS_DB, attr->srlgs);
+
+ XFREE(MTYPE_LS_DB, attr);
+ attr = NULL;
+}
+
+int ls_attributes_same(struct ls_attributes *l1, struct ls_attributes *l2)
+{
+ if ((l1 && !l2) || (!l1 && l2))
+ return 0;
+
+ if (l1 == l2)
+ return 1;
+
+ if (l1->flags != l2->flags)
+ return 0;
+
+ if (l1->adv.origin != l2->adv.origin)
+ return 0;
+
+ if (!memcmp(&l1->adv.id, &l2->adv.id, sizeof(struct ls_node_id)))
+ return 0;
+
+ /* Do we need to test individually each field, instead performing a
+ * global memcmp? There is a risk that an old value that is bit masked
+ * i.e. corresponding flag = 0, will result into a false negative
+ */
+ if (!memcmp(l1, l2, sizeof(struct ls_attributes)))
+ return 0;
+ else
+ return 1;
+}
+
+/**
+ * Link State Vertices management functions
+ */
+struct ls_vertex *ls_vertex_new(struct ls_node *node)
+{
+ struct ls_vertex *new;
+
+ if (node == NULL)
+ return NULL;
+
+ new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_vertex));
+ new->node = node;
+ new->incoming_edges = list_new();
+ new->outgoing_edges = list_new();
+ new->prefixes = list_new();
+
+ return new;
+}
+
+void ls_vertex_del(struct ls_vertex *vertex)
+{
+ if (vertex == NULL)
+ return;
+
+ list_delete_all_node(vertex->incoming_edges);
+ list_delete_all_node(vertex->outgoing_edges);
+ list_delete_all_node(vertex->prefixes);
+ XFREE(MTYPE_LS_DB, vertex);
+ vertex = NULL;
+}
+
+struct ls_vertex *ls_vertex_add(struct ls_ted *ted, struct ls_node *node)
+{
+ struct ls_vertex *new;
+
+ if ((ted == NULL) || (node == NULL))
+ return NULL;
+
+ new = ls_vertex_new(node);
+ if (!new)
+ return NULL;
+
+ /* set Key as the IPv4/Ipv6 Router ID or ISO System ID */
+ switch (node->adv.origin) {
+ case OSPFv2:
+ case STATIC:
+ case DIRECT:
+ memcpy(&new->key, &node->adv.id.ip.addr, IPV4_MAX_BYTELEN);
+ break;
+ case ISIS_L1:
+ case ISIS_L2:
+ memcpy(&new->key, &node->adv.id.iso.sys_id, ISO_SYS_ID_LEN);
+ break;
+ default:
+ new->key = 0;
+ break;
+ }
+
+ /* Remove Vertex if key is not set */
+ if (new->key == 0) {
+ ls_vertex_del(new);
+ return NULL;
+ }
+
+ /* Add Vertex to TED */
+ vertices_add(&ted->vertices, new);
+
+ return new;
+}
+
+struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node)
+{
+ struct ls_vertex *old;
+
+ if (node == NULL)
+ return NULL;
+
+ old = ls_find_vertex_by_id(ted, node->adv);
+ if (old) {
+ if (!ls_node_same(old->node, node)) {
+ ls_node_del(old->node);
+ old->node = node;
+ }
+ return old;
+ }
+
+ return ls_vertex_add(ted, node);
+}
+
+void ls_vertex_remove(struct ls_ted *ted, struct ls_vertex *vertex)
+{
+ vertices_del(&ted->vertices, vertex);
+ ls_vertex_del(vertex);
+}
+
+struct ls_vertex *ls_find_vertex_by_key(struct ls_ted *ted, const uint64_t key)
+{
+ struct ls_vertex node = {};
+
+ if (key == 0)
+ return NULL;
+
+ node.key = key;
+ return vertices_find(&ted->vertices, &node);
+}
+
+struct ls_vertex *ls_find_vertex_by_id(struct ls_ted *ted,
+ struct ls_node_id nid)
+{
+ struct ls_vertex node = {};
+
+ switch (nid.origin) {
+ case OSPFv2:
+ case STATIC:
+ case DIRECT:
+ memcpy(&node.key, &nid.id.ip.addr, IPV4_MAX_BYTELEN);
+ break;
+ case ISIS_L1:
+ case ISIS_L2:
+ memcpy(&node.key, &nid.id.iso.sys_id, ISO_SYS_ID_LEN);
+ break;
+ default:
+ return NULL;
+ }
+
+ return vertices_find(&ted->vertices, &node);
+}
+
+int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2)
+{
+ if ((v1 && !v2) || (!v1 && v2))
+ return 0;
+
+ if (!v1 && !v2)
+ return 1;
+
+ if (v1->key != v2->key)
+ return 0;
+
+ if (v1->node == v2->node)
+ return 1;
+
+ return ls_node_same(v1->node, v2->node);
+}
+
+/**
+ * Link State Edges management functions
+ */
+
+/**
+ * This function allows to connect the Edge to the vertices present in the TED.
+ * A temporary vertex that corresponds to the source of this Edge i.e. the
+ * advertised router, is created if not found in the Data Base. If a Edge that
+ * corresponds to the reverse path is found, the Edge is attached to the
+ * destination vertex as destination and reverse Edge is attached to the source
+ * vertex as source.
+ *
+ * @param ted Link State Data Base
+ * @param edge Link State Edge to be attached
+ */
+static void ls_edge_connect_to(struct ls_ted *ted, struct ls_edge *edge)
+{
+ struct ls_vertex *vertex = NULL;
+ struct ls_node *node;
+ struct ls_edge *dst;
+ const struct in_addr inaddr_any = {.s_addr = INADDR_ANY};
+
+ /* First, search if there is a Vertex that correspond to the Node ID */
+ vertex = ls_find_vertex_by_id(ted, edge->attributes->adv);
+ if (vertex == NULL) {
+ /* Create a new temporary Node & Vertex if not found */
+ node = ls_node_new(edge->attributes->adv, inaddr_any,
+ in6addr_any);
+ vertex = ls_vertex_add(ted, node);
+ }
+ /* and attach the edge as source to the vertex */
+ listnode_add(vertex->outgoing_edges, edge);
+ edge->source = vertex;
+
+ /* Then search if there is a reverse Edge */
+ dst = ls_find_edge_by_destination(ted, edge->attributes);
+ /* attach the destination edge to the vertex */
+ if (dst) {
+ listnode_add(vertex->incoming_edges, dst);
+ dst->destination = vertex;
+ /* and destination vertex to this edge */
+ vertex = dst->source;
+ listnode_add(vertex->incoming_edges, edge);
+ edge->destination = vertex;
+ }
+}
+
+struct ls_edge *ls_edge_add(struct ls_ted *ted,
+ struct ls_attributes *attributes)
+{
+ struct ls_edge *new;
+
+ if (attributes == NULL)
+ return NULL;
+
+ new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_edge));
+ new->attributes = attributes;
+ /* Key is the IPv4 local address */
+ if (!IPV4_NET0(attributes->standard.local.s_addr))
+ new->key = ((uint64_t)attributes->standard.local.s_addr)
+ & 0xffffffff;
+ /* or the IPv6 local address if IPv4 is not defined */
+ else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.local6))
+ new->key = (uint64_t)(attributes->standard.local6.s6_addr32[0]
+ & 0xffffffff)
+ | ((uint64_t)attributes->standard.local6.s6_addr32[1]
+ << 32);
+ /* of local identifier if no IP addresses are defined */
+ else if (attributes->standard.local_id != 0)
+ new->key = (uint64_t)(
+ (attributes->standard.local_id & 0xffffffff)
+ | ((uint64_t)attributes->standard.remote_id << 32));
+
+ /* Remove Edge if key is not known */
+ if (new->key == 0) {
+ XFREE(MTYPE_LS_DB, new);
+ return NULL;
+ }
+
+ edges_add(&ted->edges, new);
+
+ /* Finally, connect edge to vertices */
+ ls_edge_connect_to(ted, new);
+
+ return new;
+}
+
+struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted, const uint64_t key)
+{
+ struct ls_edge edge = {};
+
+ if (key == 0)
+ return NULL;
+
+ edge.key = key;
+ return edges_find(&ted->edges, &edge);
+}
+
+struct ls_edge *ls_find_edge_by_source(struct ls_ted *ted,
+ struct ls_attributes *attributes)
+{
+ struct ls_edge edge = {};
+
+ if (attributes == NULL)
+ return NULL;
+
+ /* Key is the IPv4 local address */
+ if (!IPV4_NET0(attributes->standard.local.s_addr))
+ edge.key = ((uint64_t)attributes->standard.local.s_addr)
+ & 0xffffffff;
+ /* or the IPv6 local address if IPv4 is not defined */
+ else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.local6))
+ edge.key = (uint64_t)(attributes->standard.local6.s6_addr32[0]
+ & 0xffffffff)
+ | ((uint64_t)attributes->standard.local6.s6_addr32[1]
+ << 32);
+ /* of local identifier if no IP addresses are defined */
+ else if (attributes->standard.local_id != 0)
+ edge.key = (uint64_t)(
+ (attributes->standard.local_id & 0xffffffff)
+ | ((uint64_t)attributes->standard.remote_id << 32));
+
+ if (edge.key == 0)
+ return NULL;
+
+ return edges_find(&ted->edges, &edge);
+}
+
+struct ls_edge *ls_find_edge_by_destination(struct ls_ted *ted,
+ struct ls_attributes *attributes)
+{
+ struct ls_edge edge = {};
+
+ if (attributes == NULL)
+ return NULL;
+
+ /* Key is the IPv4 local address */
+ if (!IPV4_NET0(attributes->standard.remote.s_addr))
+ edge.key = ((uint64_t)attributes->standard.remote.s_addr)
+ & 0xffffffff;
+ /* or the IPv6 local address if IPv4 is not defined */
+ else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.remote6))
+ edge.key =
+ (uint64_t)(attributes->standard.remote6.s6_addr32[0]
+ & 0xffffffff)
+ | ((uint64_t)attributes->standard.remote6.s6_addr32[1]
+ << 32);
+ /* of local identifier if no IP addresses are defined */
+ else if (attributes->standard.remote_id != 0)
+ edge.key = (uint64_t)(
+ (attributes->standard.remote_id & 0xffffffff)
+ | ((uint64_t)attributes->standard.local_id << 32));
+
+ if (edge.key == 0)
+ return NULL;
+
+ return edges_find(&ted->edges, &edge);
+}
+
+struct ls_edge *ls_edge_update(struct ls_ted *ted,
+ struct ls_attributes *attributes)
+{
+ struct ls_edge *old;
+
+ if (attributes == NULL)
+ return NULL;
+
+ /* First, search for an existing Edge */
+ old = ls_find_edge_by_source(ted, attributes);
+ if (old) {
+ /* Check if attributes are similar */
+ if (!ls_attributes_same(old->attributes, attributes)) {
+ ls_attributes_del(old->attributes);
+ old->attributes = attributes;
+ }
+ return old;
+ }
+
+ /* If not found, add new Edge from the attributes */
+ return ls_edge_add(ted, attributes);
+}
+
+void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge)
+{
+ /* Fist disconnect Edge */
+ ls_disconnect_edge(edge);
+ /* Then remove it from the Data Base */
+ edges_del(&ted->edges, edge);
+ XFREE(MTYPE_LS_DB, edge);
+}
+
+/**
+ * Link State Subnet Management functions.
+ */
+struct ls_subnet *ls_subnet_add(struct ls_ted *ted,
+ struct ls_prefix *ls_pref)
+{
+ struct ls_subnet *new;
+ struct ls_vertex *vertex;
+ struct ls_node *node;
+ const struct in_addr inaddr_any = {.s_addr = INADDR_ANY};
+
+ if (ls_pref == NULL)
+ return NULL;
+
+ new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_subnet));
+ new->ls_pref = ls_pref;
+ new->key = ls_pref->pref;
+
+ /* Find Vertex */
+ vertex = ls_find_vertex_by_id(ted, ls_pref->adv);
+ if (vertex == NULL) {
+ /* Create a new temporary Node & Vertex if not found */
+ node = ls_node_new(ls_pref->adv, inaddr_any, in6addr_any);
+ vertex = ls_vertex_add(ted, node);
+ }
+ /* And attach the subnet to the corresponding Vertex */
+ new->vertex = vertex;
+ listnode_add(vertex->prefixes, new);
+
+ subnets_add(&ted->subnets, new);
+
+ return new;
+}
+
+void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet)
+{
+ subnets_del(&ted->subnets, subnet);
+ XFREE(MTYPE_LS_DB, subnet);
+}
+
+struct ls_subnet *ls_find_subnet(struct ls_ted *ted, const struct prefix prefix)
+{
+ struct ls_subnet subnet = {};
+
+ subnet.key = prefix;
+ return subnets_find(&ted->subnets, &subnet);
+}
+
+/**
+ * Link State TED management functions
+ */
+struct ls_ted *ls_ted_new(const uint32_t key, const char *name,
+ uint32_t as_number)
+{
+ 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;
+ new->as_number = as_number;
+ strlcpy(new->name, name, MAX_NAME_LENGTH);
+
+ /* Initialize the various RB tree */
+ vertices_init(&new->vertices);
+ edges_init(&new->edges);
+ subnets_init(&new->subnets);
+
+ return new;
+}
+
+void ls_ted_del(struct ls_ted *ted)
+{
+ if (ted == NULL)
+ return;
+
+ /* Release RB Tree */
+ vertices_fini(&ted->vertices);
+ edges_fini(&ted->edges);
+ subnets_fini(&ted->subnets);
+
+ XFREE(MTYPE_LS_DB, ted);
+ ted = NULL;
+}
+
+void ls_connect(struct ls_vertex *vertex, struct ls_edge *edge, bool source)
+{
+ if (vertex == NULL || edge == NULL)
+ return;
+
+ if (source) {
+ listnode_add(vertex->outgoing_edges, edge);
+ edge->source = vertex;
+ } else {
+ listnode_add(vertex->incoming_edges, edge);
+ edge->destination = vertex;
+ }
+}
+
+void ls_disconnect(struct ls_vertex *vertex, struct ls_edge *edge, bool source)
+{
+
+ if (vertex == NULL || edge == NULL)
+ return;
+
+ if (source) {
+ listnode_delete(vertex->outgoing_edges, edge);
+ edge->source = NULL;
+ } else {
+ listnode_delete(vertex->incoming_edges, edge);
+ edge->destination = NULL;
+ }
+}
+
+void ls_connect_vertices(struct ls_vertex *src, struct ls_vertex *dst,
+ struct ls_edge *edge)
+{
+ if (edge == NULL)
+ return;
+
+ edge->source = src;
+ edge->destination = dst;
+
+ if (src != NULL)
+ listnode_add(src->outgoing_edges, edge);
+
+ if (dst != NULL)
+ listnode_add(dst->incoming_edges, edge);
+
+}
+
+void ls_disconnect_edge(struct ls_edge *edge)
+{
+ if (edge == NULL)
+ return;
+
+ ls_disconnect(edge->source, edge, true);
+ ls_disconnect(edge->destination, edge, false);
+}
+
+/**
+ * Link State Message management functions
+ */
+
+static struct ls_node *ls_parse_node(struct stream *s)
+{
+ struct ls_node *node;
+ 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);
+ if (CHECK_FLAG(node->flags, LS_NODE_NAME)) {
+ STREAM_GETC(s, len);
+ STREAM_GET(node->name, s, len);
+ }
+ if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID))
+ node->router_id.s_addr = stream_get_ipv4(s);
+ if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID6))
+ STREAM_GET(&node->router6_id, s, IPV6_MAX_BYTELEN);
+ if (CHECK_FLAG(node->flags, LS_NODE_FLAG))
+ STREAM_GETC(s, node->node_flag);
+ if (CHECK_FLAG(node->flags, LS_NODE_TYPE))
+ STREAM_GETC(s, node->type);
+ if (CHECK_FLAG(node->flags, LS_NODE_AS_NUMBER))
+ STREAM_GETL(s, node->as_number);
+ if (CHECK_FLAG(node->flags, LS_NODE_SR)) {
+ STREAM_GETL(s, node->srgb.lower_bound);
+ STREAM_GETL(s, node->srgb.range_size);
+ STREAM_GETC(s, node->srgb.flag);
+ STREAM_GET(node->algo, s, 2);
+ }
+ if (CHECK_FLAG(node->flags, LS_NODE_SRLB)) {
+ STREAM_GETL(s, node->srlb.lower_bound);
+ STREAM_GETL(s, node->srlb.range_size);
+ }
+ if (CHECK_FLAG(node->flags, LS_NODE_MSD))
+ STREAM_GETC(s, node->msd);
+
+ return node;
+
+stream_failure:
+ zlog_err("LS(%s): Could not parse Link State Node. Abort!", __func__);
+ XFREE(MTYPE_LS_DB, node);
+ return NULL;
+}
+
+static struct ls_attributes *ls_parse_attributes(struct stream *s)
+{
+ struct ls_attributes *attr;
+ 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));
+ STREAM_GETL(s, attr->flags);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NAME)) {
+ STREAM_GETC(s, len);
+ STREAM_GET(attr->name, s, len);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_METRIC))
+ STREAM_GETL(s, attr->standard.metric);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC))
+ STREAM_GETL(s, attr->standard.te_metric);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP))
+ STREAM_GETL(s, attr->standard.admin_group);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR))
+ attr->standard.local.s_addr = stream_get_ipv4(s);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR))
+ attr->standard.remote.s_addr = stream_get_ipv4(s);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6))
+ STREAM_GET(&attr->standard.local6, s, IPV6_MAX_BYTELEN);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6))
+ STREAM_GET(&attr->standard.remote6, s, IPV6_MAX_BYTELEN);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID))
+ STREAM_GETL(s, attr->standard.local_id);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID))
+ STREAM_GETL(s, attr->standard.remote_id);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_BW))
+ STREAM_GETF(s, attr->standard.max_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_RSV_BW))
+ STREAM_GETF(s, attr->standard.max_rsv_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_UNRSV_BW))
+ for (len = 0; len < MAX_CLASS_TYPE; len++)
+ STREAM_GETF(s, attr->standard.unrsv_bw[len]);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_AS))
+ STREAM_GETL(s, attr->standard.remote_as);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR))
+ attr->standard.remote_addr.s_addr = stream_get_ipv4(s);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR6))
+ STREAM_GET(&attr->standard.remote_addr6, s, IPV6_MAX_BYTELEN);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_DELAY))
+ STREAM_GETL(s, attr->extended.delay);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_MIN_MAX_DELAY)) {
+ STREAM_GETL(s, attr->extended.min_delay);
+ STREAM_GETL(s, attr->extended.max_delay);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_JITTER))
+ STREAM_GETL(s, attr->extended.jitter);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_PACKET_LOSS))
+ STREAM_GETL(s, attr->extended.pkt_loss);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_AVA_BW))
+ STREAM_GETF(s, attr->extended.ava_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_RSV_BW))
+ STREAM_GETF(s, attr->extended.rsv_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_USE_BW))
+ STREAM_GETF(s, attr->extended.used_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID)) {
+ STREAM_GETL(s, attr->adj_sid[0].sid);
+ STREAM_GETC(s, attr->adj_sid[0].flags);
+ STREAM_GETC(s, attr->adj_sid[0].weight);
+ if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2)
+ STREAM_GET(attr->adj_sid[0].neighbor.sysid, s,
+ ISO_SYS_ID_LEN);
+ else if (attr->adv.origin == OSPFv2)
+ attr->adj_sid[0].neighbor.addr.s_addr =
+ stream_get_ipv4(s);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) {
+ STREAM_GETL(s, attr->adj_sid[1].sid);
+ STREAM_GETC(s, attr->adj_sid[1].flags);
+ STREAM_GETC(s, attr->adj_sid[1].weight);
+ if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2)
+ STREAM_GET(attr->adj_sid[1].neighbor.sysid, s,
+ ISO_SYS_ID_LEN);
+ else if (attr->adv.origin == OSPFv2)
+ attr->adj_sid[1].neighbor.addr.s_addr =
+ stream_get_ipv4(s);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) {
+ STREAM_GETC(s, len);
+ attr->srlgs = XCALLOC(MTYPE_LS_DB, len*sizeof(uint32_t));
+ attr->srlg_len = len;
+ for (len = 0; len < attr->srlg_len; len++)
+ STREAM_GETL(s, attr->srlgs[len]);
+ }
+
+ return attr;
+
+stream_failure:
+ zlog_err("LS(%s): Could not parse Link State Attributes. Abort!",
+ __func__);
+ /* Clean memeory allocation */
+ if (attr->srlgs != NULL)
+ XFREE(MTYPE_LS_DB, attr->srlgs);
+ XFREE(MTYPE_LS_DB, attr);
+ return NULL;
+
+}
+
+static struct ls_prefix *ls_parse_prefix(struct stream *s)
+{
+ struct ls_prefix *ls_pref;
+ 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);
+ STREAM_GETC(s, ls_pref->pref.family);
+ STREAM_GETW(s, ls_pref->pref.prefixlen);
+ len = prefix_blen(&ls_pref->pref);
+ STREAM_GET(&ls_pref->pref.u.prefix, s, len);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_IGP_FLAG))
+ STREAM_GETC(s, ls_pref->igp_flag);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_ROUTE_TAG))
+ STREAM_GETL(s, ls_pref->route_tag);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_EXTENDED_TAG))
+ STREAM_GETQ(s, ls_pref->extended_tag);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_METRIC))
+ STREAM_GETL(s, ls_pref->metric);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_SR)) {
+ STREAM_GETL(s, ls_pref->sr.sid);
+ STREAM_GETC(s, ls_pref->sr.sid_flag);
+ STREAM_GETC(s, ls_pref->sr.algo);
+ }
+
+ return ls_pref;
+
+stream_failure:
+ zlog_err("LS(%s): Could not parse Link State Prefix. Abort!", __func__);
+ XFREE(MTYPE_LS_DB, ls_pref);
+ return NULL;
+}
+
+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);
+ STREAM_GETC(s, msg->type);
+ STREAM_GET(&msg->remote_id, s, sizeof(struct ls_node_id));
+
+ /* Read Message Payload */
+ switch (msg->type) {
+ case LS_MSG_TYPE_NODE:
+ msg->data.node = ls_parse_node(s);
+ break;
+ case LS_MSG_TYPE_ATTRIBUTES:
+ msg->data.attr = ls_parse_attributes(s);
+ break;
+ case LS_MSG_TYPE_PREFIX:
+ msg->data.prefix = ls_parse_prefix(s);
+ break;
+ default:
+ zlog_err("Unsupported Payload");
+ goto stream_failure;
+ }
+
+ if (msg->data.node == NULL || msg->data.attr == NULL
+ || msg->data.prefix == NULL)
+ goto stream_failure;
+
+ return msg;
+
+stream_failure:
+ zlog_err("LS(%s): Could not parse LS message. Abort!", __func__);
+ XFREE(MTYPE_LS_DB, msg);
+ return NULL;
+}
+
+static int ls_format_node(struct stream *s, struct ls_node *node)
+{
+ size_t len;
+
+ /* Push Advertise node information first */
+ stream_put(s, &node->adv, sizeof(struct ls_node_id));
+
+ /* Push Flags & Origin then Node information if there are present */
+ stream_putw(s, node->flags);
+ if (CHECK_FLAG(node->flags, LS_NODE_NAME)) {
+ len = strlen(node->name);
+ stream_putc(s, len + 1);
+ stream_put(s, node->name, len);
+ stream_putc(s, '\0');
+ }
+ if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID))
+ stream_put_ipv4(s, node->router_id.s_addr);
+ if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID6))
+ stream_put(s, &node->router6_id, IPV6_MAX_BYTELEN);
+ if (CHECK_FLAG(node->flags, LS_NODE_FLAG))
+ stream_putc(s, node->node_flag);
+ if (CHECK_FLAG(node->flags, LS_NODE_TYPE))
+ stream_putc(s, node->type);
+ if (CHECK_FLAG(node->flags, LS_NODE_AS_NUMBER))
+ stream_putl(s, node->as_number);
+ if (CHECK_FLAG(node->flags, LS_NODE_SR)) {
+ stream_putl(s, node->srgb.lower_bound);
+ stream_putl(s, node->srgb.range_size);
+ stream_putc(s, node->srgb.flag);
+ stream_put(s, node->algo, 2);
+ }
+ if (CHECK_FLAG(node->flags, LS_NODE_SRLB)) {
+ stream_putl(s, node->srlb.lower_bound);
+ stream_putl(s, node->srlb.range_size);
+ }
+ if (CHECK_FLAG(node->flags, LS_NODE_MSD))
+ stream_putc(s, node->msd);
+
+ return 0;
+}
+
+static int ls_format_attributes(struct stream *s, struct ls_attributes *attr)
+{
+ size_t len;
+
+ /* Push Advertise node information first */
+ stream_put(s, &attr->adv, sizeof(struct ls_node_id));
+
+ /* Push Flags & Origin then LS attributes if there are present */
+ stream_putl(s, attr->flags);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NAME)) {
+ len = strlen(attr->name);
+ stream_putc(s, len + 1);
+ stream_put(s, attr->name, len);
+ stream_putc(s, '\0');
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_METRIC))
+ stream_putl(s, attr->standard.metric);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC))
+ stream_putl(s, attr->standard.te_metric);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP))
+ stream_putl(s, attr->standard.admin_group);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR))
+ stream_put_ipv4(s, attr->standard.local.s_addr);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR))
+ stream_put_ipv4(s, attr->standard.remote.s_addr);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6))
+ stream_put(s, &attr->standard.local6, IPV6_MAX_BYTELEN);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6))
+ stream_put(s, &attr->standard.remote6, IPV6_MAX_BYTELEN);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID))
+ stream_putl(s, attr->standard.local_id);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID))
+ stream_putl(s, attr->standard.remote_id);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_BW))
+ stream_putf(s, attr->standard.max_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_RSV_BW))
+ stream_putf(s, attr->standard.max_rsv_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_UNRSV_BW))
+ for (len = 0; len < MAX_CLASS_TYPE; len++)
+ stream_putf(s, attr->standard.unrsv_bw[len]);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_AS))
+ stream_putl(s, attr->standard.remote_as);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR))
+ stream_put_ipv4(s, attr->standard.remote_addr.s_addr);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR6))
+ stream_put(s, &attr->standard.remote_addr6, IPV6_MAX_BYTELEN);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_DELAY))
+ stream_putl(s, attr->extended.delay);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_MIN_MAX_DELAY)) {
+ stream_putl(s, attr->extended.min_delay);
+ stream_putl(s, attr->extended.max_delay);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_JITTER))
+ stream_putl(s, attr->extended.jitter);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_PACKET_LOSS))
+ stream_putl(s, attr->extended.pkt_loss);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_AVA_BW))
+ stream_putf(s, attr->extended.ava_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_RSV_BW))
+ stream_putf(s, attr->extended.rsv_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_USE_BW))
+ stream_putf(s, attr->extended.used_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID)) {
+ stream_putl(s, attr->adj_sid[0].sid);
+ stream_putc(s, attr->adj_sid[0].flags);
+ stream_putc(s, attr->adj_sid[0].weight);
+ if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2)
+ stream_put(s, attr->adj_sid[0].neighbor.sysid,
+ ISO_SYS_ID_LEN);
+ else if (attr->adv.origin == OSPFv2)
+ stream_put_ipv4(s,
+ attr->adj_sid[0].neighbor.addr.s_addr);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) {
+ stream_putl(s, attr->adj_sid[1].sid);
+ stream_putc(s, attr->adj_sid[1].flags);
+ stream_putc(s, attr->adj_sid[1].weight);
+ if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2)
+ stream_put(s, attr->adj_sid[1].neighbor.sysid,
+ ISO_SYS_ID_LEN);
+ else if (attr->adv.origin == OSPFv2)
+ stream_put_ipv4(s,
+ attr->adj_sid[1].neighbor.addr.s_addr);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) {
+ stream_putc(s, attr->srlg_len);
+ for (len = 0; len < attr->srlg_len; len++)
+ stream_putl(s, attr->srlgs[len]);
+ }
+
+ return 0;
+}
+
+static int ls_format_prefix(struct stream *s, struct ls_prefix *ls_pref)
+{
+ size_t len;
+
+ /* Push Advertise node information first */
+ stream_put(s, &ls_pref->adv, sizeof(struct ls_node_id));
+
+ /* Push Flags, Origin & Prefix then information if there are present */
+ stream_putw(s, ls_pref->flags);
+ stream_putc(s, ls_pref->pref.family);
+ stream_putw(s, ls_pref->pref.prefixlen);
+ len = prefix_blen(&ls_pref->pref);
+ stream_put(s, &ls_pref->pref.u.prefix, len);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_IGP_FLAG))
+ stream_putc(s, ls_pref->igp_flag);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_ROUTE_TAG))
+ stream_putl(s, ls_pref->route_tag);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_EXTENDED_TAG))
+ stream_putq(s, ls_pref->extended_tag);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_METRIC))
+ stream_putl(s, ls_pref->metric);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_SR)) {
+ stream_putl(s, ls_pref->sr.sid);
+ stream_putc(s, ls_pref->sr.sid_flag);
+ stream_putc(s, ls_pref->sr.algo);
+ }
+
+ return 0;
+}
+
+static int ls_format_msg(struct stream *s, struct ls_message *msg)
+{
+
+ /* Prepare Link State header */
+ stream_putc(s, msg->event);
+ stream_putc(s, msg->type);
+ stream_put(s, &msg->remote_id, sizeof(struct ls_node_id));
+
+ /* Add Message Payload */
+ switch (msg->type) {
+ case LS_MSG_TYPE_NODE:
+ return ls_format_node(s, msg->data.node);
+ case LS_MSG_TYPE_ATTRIBUTES:
+ return ls_format_attributes(s, msg->data.attr);
+ case LS_MSG_TYPE_PREFIX:
+ return ls_format_prefix(s, msg->data.prefix);
+ default:
+ zlog_warn("Unsupported Payload");
+ break;
+ }
+
+ return -1;
+}
+
+int ls_send_msg(struct zclient *zclient, struct ls_message *msg,
+ struct zapi_opaque_reg_info *dst)
+{
+ struct stream *s;
+ uint16_t flags = 0;
+
+ /* Check buffer size */
+ if (STREAM_SIZE(zclient->obuf) <
+ (ZEBRA_HEADER_SIZE + sizeof(uint32_t) + sizeof(msg)))
+ return -1;
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT);
+
+ /* Send sub-type, flags and destination for unicast message */
+ stream_putl(s, LINK_STATE_UPDATE);
+ if (dst != NULL) {
+ SET_FLAG(flags, ZAPI_OPAQUE_FLAG_UNICAST);
+ stream_putw(s, flags);
+ /* Send destination client info */
+ stream_putc(s, dst->proto);
+ stream_putw(s, dst->instance);
+ stream_putl(s, dst->session_id);
+ } else
+ stream_putw(s, flags);
+
+ /* Format Link State message */
+ if (ls_format_msg(s, msg) < 0) {
+ stream_reset(s);
+ return -1;
+ }
+
+ /* Put length into the header at the start of the stream. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zclient_send_message(zclient);
+}
+
+struct ls_message *ls_vertex2msg(struct ls_message *msg,
+ struct ls_vertex *vertex)
+{
+ /* Allocate space if needed */
+ if (msg == NULL)
+ msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message));
+ else
+ memset(msg, 0, sizeof(*msg));
+
+ msg->type = LS_MSG_TYPE_NODE;
+ msg->data.node = vertex->node;
+ msg->remote_id.origin = NONE;
+
+ return msg;
+}
+
+struct ls_message *ls_edge2msg(struct ls_message *msg, struct ls_edge *edge)
+{
+ /* Allocate space if needed */
+ if (msg == NULL)
+ msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message));
+ else
+ memset(msg, 0, sizeof(*msg));
+
+ msg->type = LS_MSG_TYPE_ATTRIBUTES;
+ msg->data.attr = edge->attributes;
+ if (edge->destination != NULL)
+ msg->remote_id = edge->destination->node->adv;
+ else
+ msg->remote_id.origin = NONE;
+
+ return msg;
+}
+
+struct ls_message *ls_subnet2msg(struct ls_message *msg,
+ struct ls_subnet *subnet)
+{
+ /* Allocate space if needed */
+ if (msg == NULL)
+ msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message));
+ else
+ memset(msg, 0, sizeof(*msg));
+
+ msg->type = LS_MSG_TYPE_PREFIX;
+ msg->data.prefix = subnet->ls_pref;
+ msg->remote_id.origin = NONE;
+
+ return msg;
+}
+
+void ls_delete_msg(struct ls_message *msg)
+{
+ if (msg == NULL)
+ return;
+
+ switch (msg->type) {
+ case LS_MSG_TYPE_NODE:
+ if (msg->data.node)
+ XFREE(MTYPE_LS_DB, msg->data.node);
+ break;
+ case LS_MSG_TYPE_ATTRIBUTES:
+ if (msg->data.attr)
+ XFREE(MTYPE_LS_DB, msg->data.attr);
+ break;
+ case LS_MSG_TYPE_PREFIX:
+ if (msg->data.prefix)
+ XFREE(MTYPE_LS_DB, msg->data.prefix);
+ break;
+ default:
+ break;
+ }
+
+ XFREE(MTYPE_LS_DB, msg);
+}
+
+int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient,
+ struct zapi_opaque_reg_info *dst)
+{
+ struct ls_vertex *vertex;
+ struct ls_edge *edge;
+ struct ls_subnet *subnet;
+ struct ls_message msg;
+
+ /* Prepare message */
+ msg.event = LS_MSG_EVENT_SYNC;
+
+ /* Loop TED, start sending Node, then Attributes and finally Prefix */
+ frr_each(vertices, &ted->vertices, vertex) {
+ ls_vertex2msg(&msg, vertex);
+ ls_send_msg(zclient, &msg, dst);
+ }
+ frr_each(edges, &ted->edges, edge) {
+ ls_edge2msg(&msg, edge);
+ ls_send_msg(zclient, &msg, dst);
+ }
+ frr_each(subnets, &ted->subnets, subnet) {
+ ls_subnet2msg(&msg, subnet);
+ ls_send_msg(zclient, &msg, dst);
+ }
+ return 0;
+}
+
+void ls_dump_ted(struct ls_ted *ted)
+{
+ struct ls_vertex *vertex;
+ struct ls_edge *edge;
+ struct ls_subnet *subnet;
+ struct ls_message msg;
+
+ zlog_debug("(%s) Ted init", __func__);
+ /* Prepare message */
+ msg.event = LS_MSG_EVENT_SYNC;
+
+ /* Loop TED, start printing Node, then Attributes and finally Prefix */
+ frr_each(vertices, &ted->vertices, vertex) {
+ ls_vertex2msg(&msg, vertex);
+ zlog_debug(" Ted node (%s %pI4 %s)",
+ vertex->node->name[0] ? vertex->node->name
+ : "no name node",
+ &vertex->node->router_id,
+ vertex->node->adv.origin == DIRECT ? "DIRECT"
+ : "NO DIRECT");
+ struct listnode *lst_node;
+ struct ls_edge *vertex_edge;
+
+ for (ALL_LIST_ELEMENTS_RO(vertex->incoming_edges, lst_node,
+ vertex_edge)) {
+ zlog_debug(
+ " inc edge key:%lldn attr key:%pI4 loc:(%pI4) rmt:(%pI4)",
+ vertex_edge->key,
+ &vertex_edge->attributes->adv.id.ip.addr,
+ &vertex_edge->attributes->standard.local,
+ &vertex_edge->attributes->standard.remote);
+ }
+ for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, lst_node,
+ vertex_edge)) {
+ zlog_debug(
+ " out edge key:%lld attr key:%pI4 loc:(%pI4) rmt:(%pI4)",
+ vertex_edge->key,
+ &vertex_edge->attributes->adv.id.ip.addr,
+ &vertex_edge->attributes->standard.local,
+ &vertex_edge->attributes->standard.remote);
+ }
+ }
+ frr_each(edges, &ted->edges, edge) {
+ ls_edge2msg(&msg, edge);
+ zlog_debug(" Ted edge key:%lld src:%s dst:%s", edge->key,
+ edge->source ? edge->source->node->name
+ : "no_source",
+ edge->destination ? edge->destination->node->name
+ : "no_dest");
+ }
+ frr_each(subnets, &ted->subnets, subnet) {
+ ls_subnet2msg(&msg, subnet);
+ zlog_debug(
+ " Ted subnet key:%pFX vertex:%pI4 pfx:%pFX",
+ &subnet->key,
+ &subnet->vertex->node->adv.id.ip.addr,
+ &subnet->ls_pref->pref);
+ }
+ zlog_debug("(%s) Ted end", __func__);
+}
diff --git a/lib/link_state.h b/lib/link_state.h
new file mode 100644
index 0000000000..93669f5b23
--- /dev/null
+++ b/lib/link_state.h
@@ -0,0 +1,780 @@
+/*
+ * Link State Database definition - ted.h
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ *
+ * Copyright (C) 2020 Orange http://www.orange.com
+ *
+ * This file is part of Free Range Routing (FRR).
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_LINK_STATE_H_
+#define _FRR_LINK_STATE_H_
+
+#include "typesafe.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This file defines the model used to implement a Link State Database
+ * suitable to be used by various protocol like RSVP-TE, BGP-LS, PCEP ...
+ * This database is normally fulfill by the link state routing protocol,
+ * commonly OSPF or ISIS, carrying Traffic Engineering information within
+ * Link State Attributes. See, RFC3630.(OSPF-TE) and RFC5305 (ISIS-TE).
+ *
+ * At least, 3 types of Link State structure are defined:
+ * - Link State Node that groups all information related to a node
+ * - Link State Attributes that groups all information related to a link
+ * - Link State Prefix that groups all information related to a prefix
+ *
+ * These 3 types of structures are those handled by BGP-LS (see RFC7752).
+ *
+ * Each structure, in addition to the specific parameters, embed the node
+ * identifier which advertises the Link State and a bit mask as flags to
+ * indicates which parameters are valid i.e. for which the value corresponds
+ * to a Link State information convey by the routing protocol.
+ * Node identifier is composed of the route id as IPv4 address plus the area
+ * id for OSPF and the ISO System id plus the IS-IS level for IS-IS.
+ */
+
+/* Link State Common definitions */
+#define MAX_NAME_LENGTH 256
+#define ISO_SYS_ID_LEN 6
+
+/* Type of Node */
+enum ls_node_type {
+ STANDARD, /* a P or PE node */
+ ABR, /* an Array Border Node */
+ ASBR, /* an Autonomous System Border Node */
+ PSEUDO, /* a Pseudo Node */
+};
+
+/* Origin of the Link State information */
+enum ls_origin {NONE = 0, ISIS_L1, ISIS_L2, OSPFv2, DIRECT, STATIC};
+
+/**
+ * Link State Node Identifier as:
+ * - IPv4 address + Area ID for OSPF
+ * - ISO System ID + ISIS Level for ISIS
+ */
+struct ls_node_id {
+ enum ls_origin origin; /* Origin of the LS information */
+ union {
+ struct {
+ struct in_addr addr; /* OSPF Router IS */
+ struct in_addr area_id; /* OSPF Area ID */
+ } ip;
+ struct {
+ uint8_t sys_id[ISO_SYS_ID_LEN]; /* ISIS System ID */
+ uint8_t level; /* ISIS Level */
+ uint8_t padding;
+ } iso;
+ } id __attribute__((aligned(8)));
+};
+
+/* Link State flags to indicate which Node parameters are valid */
+#define LS_NODE_UNSET 0x0000
+#define LS_NODE_NAME 0x0001
+#define LS_NODE_ROUTER_ID 0x0002
+#define LS_NODE_ROUTER_ID6 0x0004
+#define LS_NODE_FLAG 0x0008
+#define LS_NODE_TYPE 0x0010
+#define LS_NODE_AS_NUMBER 0x0020
+#define LS_NODE_SR 0x0040
+#define LS_NODE_SRLB 0x0080
+#define LS_NODE_MSD 0x0100
+
+/* Link State Node structure */
+struct ls_node {
+ uint16_t flags; /* Flag for parameters validity */
+ struct ls_node_id adv; /* Adv. Router of this Link State */
+ char name[MAX_NAME_LENGTH]; /* Name of the Node (IS-IS only) */
+ struct in_addr router_id; /* IPv4 Router ID */
+ struct in6_addr router6_id; /* IPv6 Router ID */
+ uint8_t node_flag; /* IS-IS or OSPF Node flag */
+ enum node_type type; /* Type of Node */
+ uint32_t as_number; /* Local or neighbor AS number */
+ struct { /* Segment Routing Global Block */
+ uint32_t lower_bound; /* MPLS label lower bound */
+ uint32_t range_size; /* MPLS label range size */
+ uint8_t flag; /* IS-IS SRGB flags */
+ } srgb;
+#define LS_NODE_SRGB_SIZE 9
+ struct { /* Segment Routing Local Block */
+ uint32_t lower_bound; /* MPLS label lower bound */
+ uint32_t range_size; /* MPLS label range size */
+ } srlb;
+#define LS_NODE_SRLB_SIZE 8
+ uint8_t algo[2]; /* Segment Routing Algorithms */
+ uint8_t msd; /* Maximum Stack Depth */
+};
+
+/* Link State flags to indicate which Attribute parameters are valid */
+#define LS_ATTR_UNSET 0x00000000
+#define LS_ATTR_NAME 0x00000001
+#define LS_ATTR_METRIC 0x00000002
+#define LS_ATTR_TE_METRIC 0x00000004
+#define LS_ATTR_ADM_GRP 0x00000008
+#define LS_ATTR_LOCAL_ADDR 0x00000010
+#define LS_ATTR_NEIGH_ADDR 0x00000020
+#define LS_ATTR_LOCAL_ADDR6 0x00000040
+#define LS_ATTR_NEIGH_ADDR6 0x00000080
+#define LS_ATTR_LOCAL_ID 0x00000100
+#define LS_ATTR_NEIGH_ID 0x00000200
+#define LS_ATTR_MAX_BW 0x00000400
+#define LS_ATTR_MAX_RSV_BW 0x00000800
+#define LS_ATTR_UNRSV_BW 0x00001000
+#define LS_ATTR_REMOTE_AS 0x00002000
+#define LS_ATTR_REMOTE_ADDR 0x00004000
+#define LS_ATTR_REMOTE_ADDR6 0x00008000
+#define LS_ATTR_DELAY 0x00010000
+#define LS_ATTR_MIN_MAX_DELAY 0x00020000
+#define LS_ATTR_JITTER 0x00040000
+#define LS_ATTR_PACKET_LOSS 0x00080000
+#define LS_ATTR_AVA_BW 0x00100000
+#define LS_ATTR_RSV_BW 0x00200000
+#define LS_ATTR_USE_BW 0x00400000
+#define LS_ATTR_ADJ_SID 0x00800000
+#define LS_ATTR_BCK_ADJ_SID 0x01000000
+#define LS_ATTR_SRLG 0x02000000
+
+/* Link State Attributes */
+struct ls_attributes {
+ uint32_t flags; /* Flag for parameters validity */
+ struct ls_node_id adv; /* Adv. Router of this Link State */
+ char name[MAX_NAME_LENGTH]; /* Name of the Edge. Could be null */
+ struct { /* Standard TE metrics */
+ uint32_t metric; /* IGP standard metric */
+ uint32_t te_metric; /* Traffic Engineering metric */
+ uint32_t admin_group; /* Administrative Group */
+ struct in_addr local; /* Local IPv4 address */
+ struct in_addr remote; /* Remote IPv4 address */
+ struct in6_addr local6; /* Local IPv6 address */
+ struct in6_addr remote6; /* Remote IPv6 address */
+ uint32_t local_id; /* Local Identifier */
+ uint32_t remote_id; /* Remote Identifier */
+ float max_bw; /* Maximum Link Bandwidth */
+ float max_rsv_bw; /* Maximum Reservable BW */
+ float unrsv_bw[8]; /* Unreserved BW per CT (8) */
+ uint32_t remote_as; /* Remote AS number */
+ struct in_addr remote_addr; /* Remote IPv4 address */
+ struct in6_addr remote_addr6; /* Remote IPv6 address */
+ } standard;
+#define LS_ATTR_STANDARD_SIZE 124
+ struct { /* Extended TE Metrics */
+ uint32_t delay; /* Unidirectional average delay */
+ uint32_t min_delay; /* Unidirectional minimum delay */
+ uint32_t max_delay; /* Unidirectional maximum delay */
+ uint32_t jitter; /* Unidirectional delay variation */
+ uint32_t pkt_loss; /* Unidirectional packet loss */
+ float ava_bw; /* Available Bandwidth */
+ float rsv_bw; /* Reserved Bandwidth */
+ float used_bw; /* Utilized Bandwidth */
+ } extended;
+#define LS_ATTR_EXTENDED_SIZE 32
+ struct { /* (LAN)-Adjacency SID for OSPF */
+ uint32_t sid; /* SID as MPLS label or index */
+ uint8_t flags; /* Flags */
+ uint8_t weight; /* Administrative weight */
+ union {
+ struct in_addr addr; /* Neighbor @IP for OSPF */
+ uint8_t sysid[ISO_SYS_ID_LEN]; /* or Sys-ID for ISIS */
+ } neighbor;
+ } adj_sid[2]; /* Primary & Backup (LAN)-Adj. SID */
+#define LS_ATTR_ADJ_SID_SIZE 120
+ uint32_t *srlgs; /* List of Shared Risk Link Group */
+ uint8_t srlg_len; /* number of SRLG in the list */
+};
+
+/* Link State flags to indicate which Prefix parameters are valid */
+#define LS_PREF_UNSET 0x00
+#define LS_PREF_IGP_FLAG 0x01
+#define LS_PREF_ROUTE_TAG 0x02
+#define LS_PREF_EXTENDED_TAG 0x04
+#define LS_PREF_METRIC 0x08
+#define LS_PREF_SR 0x10
+
+/* Link State Prefix */
+struct ls_prefix {
+ uint8_t flags; /* Flag for parameters validity */
+ struct ls_node_id adv; /* Adv. Router of this Link State */
+ struct prefix pref; /* IPv4 or IPv6 prefix */
+ uint8_t igp_flag; /* IGP Flags associated to the prefix */
+ uint32_t route_tag; /* IGP Route Tag */
+ uint64_t extended_tag; /* IGP Extended Route Tag */
+ uint32_t metric; /* Route metric for this prefix */
+ struct {
+ uint32_t sid; /* Segment Routing ID */
+ uint8_t sid_flag; /* Segment Routing Flags */
+ uint8_t algo; /* Algorithm for Segment Routing */
+ } sr;
+};
+
+/**
+ * Create a new Link State Node. Structure is dynamically allocated.
+ *
+ * @param adv Mandatory Link State Node ID i.e. advertise router information
+ * @param rid Router ID as IPv4 address
+ * @param rid6 Router ID as IPv6 address
+ *
+ * @return New Link State Node
+ */
+extern struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr rid,
+ struct in6_addr rid6);
+
+/**
+ * Remove Link State Node. Data structure is freed.
+ *
+ * @param node Pointer to a valid Link State Node structure
+ */
+extern void ls_node_del(struct ls_node *node);
+
+/**
+ * Check if two Link State Nodes are equal. Note that this routine has the same
+ * return value sense as '==' (which is different from a comparison).
+ *
+ * @param n1 First Link State Node to be compare
+ * @param n2 Second Link State Node to be compare
+ *
+ * @return 1 if equal, 0 otherwise
+ */
+extern int ls_node_same(struct ls_node *n1, struct ls_node *n2);
+
+/**
+ * Create a new Link State Attributes. Structure is dynamically allocated.
+ * At least one of parameters MUST be valid and not equal to 0.
+ *
+ * @param adv Mandatory Link State Node ID i.e. advertise router ID
+ * @param local Local IPv4 address
+ * @param local6 Local Ipv6 address
+ * @param local_id Local Identifier
+ *
+ * @return New Link State Attributes
+ */
+extern struct ls_attributes *ls_attributes_new(struct ls_node_id adv,
+ struct in_addr local,
+ struct in6_addr local6,
+ uint32_t local_id);
+
+/**
+ * Remove Link State Attributes. Data structure is freed.
+ *
+ * @param attr Pointer to a valid Link State Attribute structure
+ */
+extern void ls_attributes_del(struct ls_attributes *attr);
+
+/**
+ * Check if two Link State Attributes are equal. Note that this routine has the
+ * same return value sense as '==' (which is different from a comparison).
+ *
+ * @param a1 First Link State Attributes to be compare
+ * @param a2 Second Link State Attributes to be compare
+ *
+ * @return 1 if equal, 0 otherwise
+ */
+extern int ls_attributes_same(struct ls_attributes *a1,
+ struct ls_attributes *a2);
+
+/**
+ * In addition a Graph model is defined as an overlay on top of link state
+ * database in order to ease Path Computation algorithm implementation.
+ * Denoted G(V, E), a graph is composed by a list of Vertices (V) which
+ * represents the network Node and a list of Edges (E) which represents node
+ * Link. An additional list of prefixes (P) is also added.
+ * A prefix (P) is also attached to the Vertex (V) which advertise it.
+ *
+ * Vertex (V) contains the list of outgoing Edges (E) that connect this Vertex
+ * with its direct neighbors and the list of incoming Edges (E) that connect
+ * the direct neighbors to this Vertex. Indeed, the Edge (E) is unidirectional,
+ * thus, it is necessary to add 2 Edges to model a bidirectional relation
+ * between 2 Vertices.
+ *
+ * Edge (E) contains the source and destination Vertex that this Edge
+ * is connecting.
+ *
+ * A unique Key is used to identify both Vertices and Edges within the Graph.
+ * An easy way to build this key is to used the IP address: i.e. loopback
+ * address for Vertices and link IP address for Edges.
+ *
+ * -------------- --------------------------- --------------
+ * | Connected |---->| Connected Edge Va to Vb |--->| Connected |
+ * --->| Vertex | --------------------------- | Vertex |---->
+ * | | | |
+ * | - Key (Va) | | - Key (Vb) |
+ * <---| - Vertex | --------------------------- | - Vertex |<----
+ * | |<----| Connected Edge Vb to Va |<---| |
+ * -------------- --------------------------- --------------
+ *
+ */
+
+/* Link State Vertex structure */
+PREDECL_RBTREE_UNIQ(vertices)
+struct ls_vertex {
+ struct vertices_item entry; /* Entry in RB Tree */
+ uint64_t key; /* Unique Key identifier */
+ struct ls_node *node; /* Link State Node */
+ struct list *incoming_edges; /* List of incoming Link State links */
+ struct list *outgoing_edges; /* List of outgoing Link State links */
+ struct list *prefixes; /* List of advertised prefix */
+};
+
+/* Link State Edge structure */
+PREDECL_RBTREE_UNIQ(edges)
+struct ls_edge {
+ struct edges_item entry; /* Entry in RB tree */
+ uint64_t key; /* Unique Key identifier */
+ struct ls_attributes *attributes; /* Link State attributes */
+ struct ls_vertex *source; /* Pointer to the source Vertex */
+ struct ls_vertex *destination; /* Pointer to the destination Vertex */
+};
+
+/* Link State Subnet structure */
+PREDECL_RBTREE_UNIQ(subnets)
+struct ls_subnet {
+ struct subnets_item entry; /* Entry in RB tree */
+ struct prefix key; /* Unique Key identifier */
+ struct ls_vertex *vertex; /* Back pointer to the Vertex owner */
+ struct ls_prefix *ls_pref; /* Link State Prefix */
+};
+
+/* Declaration of Vertices, Edges and Prefixes RB Trees */
+macro_inline int vertex_cmp(const struct ls_vertex *node1,
+ const struct ls_vertex *node2)
+{
+ return (node1->key - node2->key);
+}
+DECLARE_RBTREE_UNIQ(vertices, struct ls_vertex, entry, vertex_cmp)
+
+macro_inline int edge_cmp(const struct ls_edge *edge1,
+ const struct ls_edge *edge2)
+{
+ return (edge1->key - edge2->key);
+}
+DECLARE_RBTREE_UNIQ(edges, struct ls_edge, entry, edge_cmp)
+
+macro_inline int subnet_cmp(const struct ls_subnet *a,
+ const struct ls_subnet *b)
+{
+ return prefix_cmp(&a->key, &b->key);
+}
+DECLARE_RBTREE_UNIQ(subnets, struct ls_subnet, entry, subnet_cmp)
+
+/* Link State TED Structure */
+struct ls_ted {
+ uint32_t key; /* Unique identifier */
+ char name[MAX_NAME_LENGTH]; /* Name of this graph. Could be null */
+ uint32_t as_number; /* AS number of the modeled network */
+ struct ls_vertex *self; /* Vertex of the FRR instance */
+ struct vertices_head vertices; /* List of Vertices */
+ struct edges_head edges; /* List of Edges */
+ struct subnets_head subnets; /* List of Subnets */
+};
+
+/**
+ * Create a new Link State Vertex structure and initialize is with the Link
+ * State Node parameter.
+ *
+ * @param node Link State Node
+ *
+ * @return New Vertex
+ */
+extern struct ls_vertex *ls_vertex_new(struct ls_node *node);
+
+/**
+ * Delete Link State Vertex. This function clean internal Vertex lists (incoming
+ * and outgoing Link State Edge and Link State Subnet). Note that referenced
+ * objects of the different lists (Edges & SubNet) are not removed as they could
+ * be connected to other Vertices.
+ *
+ * @param vertex Link State Vertex to be removed
+ */
+extern void ls_vertex_del(struct ls_vertex *vertex);
+
+/**
+ * Add new vertex to the Link State DB. Vertex is created from the Link State
+ * Node. Vertex data structure is dynamically allocated.
+ *
+ * @param ted Traffic Engineering Database structure
+ * @param node Link State Node
+ *
+ * @return New Vertex or NULL in case of error
+ */
+extern struct ls_vertex *ls_vertex_add(struct ls_ted *ted,
+ struct ls_node *node);
+
+/**
+ * Update Vertex with the Link State Node. A new vertex is created if no one
+ * corresponds to the Link State Node.
+ *
+ * @param ted Link State Data Base
+ * @param node Link State Node to be updated
+ *
+ * @return Updated Link State Vertex or Null in case of error
+ */
+extern struct ls_vertex *ls_vertex_update(struct ls_ted *ted,
+ struct ls_node *node);
+
+/**
+ * Remove Vertex from the Link State DB. Vertex Data structure is freed but
+ * not the Link State Node. Link State DB is not modified if Vertex is NULL or
+ * not found in the Data Base.
+ *
+ * @param ted Link State Data Base
+ * @param vertex Vertex to be removed
+ */
+extern void ls_vertex_remove(struct ls_ted *ted, struct ls_vertex *vertex);
+
+/**
+ * Find Vertex in the Link State DB by its unique key.
+ *
+ * @param ted Link State Data Base
+ * @param key Vertex Key different from 0
+ *
+ * @return Vertex if found, NULL otherwise
+ */
+extern struct ls_vertex *ls_find_vertex_by_key(struct ls_ted *ted,
+ const uint64_t key);
+
+/**
+ * Find Vertex in the Link State DB by its Link State Node.
+ *
+ * @param ted Link State Data Base
+ * @param nid Link State Node ID
+ *
+ * @return Vertex if found, NULL otherwise
+ */
+extern struct ls_vertex *ls_find_vertex_by_id(struct ls_ted *ted,
+ struct ls_node_id nid);
+
+/**
+ * Check if two Vertices are equal. Note that this routine has the same return
+ * value sense as '==' (which is different from a comparison).
+ *
+ * @param v1 First vertex to compare
+ * @param v2 Second vertex to compare
+ *
+ * @return 1 if equal, 0 otherwise
+ */
+extern int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2);
+
+/**
+ * Add new Edge to the Link State DB. Edge is created from the Link State
+ * Attributes. Edge data structure is dynamically allocated.
+ *
+ * @param ted Link State Data Base
+ * @param attributes Link State attributes
+ *
+ * @return New Edge or NULL in case of error
+ */
+extern struct ls_edge *ls_edge_add(struct ls_ted *ted,
+ struct ls_attributes *attributes);
+
+/**
+ * Update the Link State Attributes information of an existing Edge. If there is
+ * no corresponding Edge in the Link State Data Base, a new Edge is created.
+ *
+ * @param ted Link State Data Base
+ * @param attributes Link State Attributes
+ *
+ * @return Updated Link State Edge, or NULL in case of error
+ */
+extern struct ls_edge *ls_edge_update(struct ls_ted *ted,
+ struct ls_attributes *attributes);
+
+/**
+ * Remove Edge from the Link State DB. Edge data structure is freed but not the
+ * Link State Attributes data structure. Link State DB is not modified if Edge
+ * is NULL or not found in the Data Base.
+ *
+ * @param ted Link State Data Base
+ * @param edge Edge to be removed
+ */
+extern void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge);
+
+/**
+ * Find Edge in the Link State Data Base by Edge key.
+ *
+ * @param ted Link State Data Base
+ * @param key Edge key
+ *
+ * @return Edge if found, NULL otherwise
+ */
+extern struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted,
+ const uint64_t key);
+
+/**
+ * Find Edge in the Link State Data Base by the source (local IPv4 or IPv6
+ * address or local ID) informations of the Link
+ * State Attributes
+ *
+ * @param ted Link State Data Base
+ * @param attributes Link State Attributes
+ *
+ * @return Edge if found, NULL otherwise
+ */
+extern struct ls_edge *
+ls_find_edge_by_source(struct ls_ted *ted, struct ls_attributes *attributes);
+
+/**
+ * Find Edge in the Link State Data Base by the destination (remote IPv4 or IPv6
+ * address of remote ID) information of the Link State Attributes
+ *
+ * @param ted Link State Data Base
+ * @param attributes Link State Attributes
+ *
+ * @return Edge if found, NULL otherwise
+ */
+extern struct ls_edge *
+ls_find_edge_by_destination(struct ls_ted *ted,
+ struct ls_attributes *attributes);
+
+/**
+ * Add new Subnet to the Link State DB. Subnet is created from the Link State
+ * prefix. Subnet data structure is dynamically allocated.
+ *
+ * @param ted Link State Data Base
+ * @param pref Link State Prefix
+ *
+ * @return New Subnet
+ */
+extern struct ls_subnet *ls_subnet_add(struct ls_ted *ted,
+ struct ls_prefix *pref);
+
+/**
+ * Remove Subnet from the Link State DB. Subnet data structure is freed but
+ * not the Link State prefix data structure. Link State DB is not modified
+ * if Subnet is NULL or not found in the Data Base.
+ *
+ * @param ted Link State Data Base
+ * @param subnet Subnet to be removed
+ */
+extern void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet);
+
+/**
+ * Find Subnet in the Link State Data Base by prefix.
+ *
+ * @param ted Link State Data Base
+ * @param prefix Link State Prefix
+ *
+ * @return Subnet if found, NULL otherwise
+ */
+extern struct ls_subnet *ls_find_subnet(struct ls_ted *ted,
+ const struct prefix prefix);
+
+/**
+ * Create a new Link State Data Base.
+ *
+ * @param key Unique key of the data base. Must be different from 0
+ * @param name Name of the data base (may be NULL)
+ * @param asn AS Number for this data base. Must be different from 0
+ *
+ * @return New Link State Database or NULL in case of error
+ */
+extern struct ls_ted *ls_ted_new(const uint32_t key, const char *name,
+ uint32_t asn);
+
+/**
+ * Delete existing Link State Data Base.
+ *
+ * @param ted Link State Data Base
+ */
+extern void ls_ted_del(struct ls_ted *ted);
+
+/**
+ * Connect Source and Destination Vertices by given Edge. Only non NULL source
+ * and destination vertices are connected.
+ *
+ * @param src Link State Source Vertex
+ * @param dst Link State Destination Vertex
+ * @param edge Link State Edge. Must not be NULL
+ */
+extern void ls_connect_vertices(struct ls_vertex *src, struct ls_vertex *dst,
+ struct ls_edge *edge);
+
+/**
+ * Connect Link State Edge to the Link State Vertex which could be a Source or
+ * a Destination Vertex.
+ *
+ * @param vertex Link State Vertex to be connected. Must not be NULL
+ * @param edge Link State Edge connection. Must not be NULL
+ * @param source True for a Source, false for a Destination Vertex
+ */
+extern void ls_connect(struct ls_vertex *vertex, struct ls_edge *edge,
+ bool source);
+
+/**
+ * Disconnect Link State Edge from the Link State Vertex which could be a
+ * Source or a Destination Vertex.
+ *
+ * @param vertex Link State Vertex to be connected. Must not be NULL
+ * @param edge Link State Edge connection. Must not be NULL
+ * @param source True for a Source, false for a Destination Vertex
+ */
+extern void ls_disconnect(struct ls_vertex *vertex, struct ls_edge *edge,
+ bool source);
+
+/**
+ * Disconnect Link State Edge from both Source and Destination Vertex.
+ *
+ * @param edge Link State Edge to be disconnected
+ */
+extern void ls_disconnect_edge(struct ls_edge *edge);
+
+
+/**
+ * The Link State Message is defined to convey Link State parameters from
+ * the routing protocol (OSPF or IS-IS) to other daemons e.g. BGP.
+ *
+ * The structure is composed of:
+ * - Event of the message:
+ * - Sync: Send the whole LS DB following a request
+ * - Add: Send the a new Link State element
+ * - Update: Send an update of an existing Link State element
+ * - Delete: Indicate that the given Link State element is removed
+ * - Type of Link State element: Node, Attribute or Prefix
+ * - Remote node id when known
+ * - Data: Node, Attributes or Prefix
+ *
+ * A Link State Message can carry only one Link State Element (Node, Attributes
+ * of Prefix) at once, and only one Link State Message is sent through ZAPI
+ * Opaque Link State type at once.
+ */
+
+/* ZAPI Opaque Link State Message Event */
+#define LS_MSG_EVENT_SYNC 1
+#define LS_MSG_EVENT_ADD 2
+#define LS_MSG_EVENT_UPDATE 3
+#define LS_MSG_EVENT_DELETE 4
+
+/* ZAPI Opaque Link State Message sub-Type */
+#define LS_MSG_TYPE_NODE 1
+#define LS_MSG_TYPE_ATTRIBUTES 2
+#define LS_MSG_TYPE_PREFIX 3
+
+/* Link State Message */
+struct ls_message {
+ uint8_t event; /* Message Event: Sync, Add, Update, Delete */
+ uint8_t type; /* Message Data Type: Node, Attribute, Prefix */
+ struct ls_node_id remote_id; /* Remote Link State Node ID */
+ union {
+ struct ls_node *node; /* Link State Node */
+ struct ls_attributes *attr; /* Link State Attributes */
+ struct ls_prefix *prefix; /* Link State Prefix */
+ } data;
+};
+
+/**
+ * Parse Link State Message from stream. Used this function once receiving a
+ * new ZAPI Opaque message of type Link State.
+ *
+ * @param s Stream buffer. Must not be NULL.
+ *
+ * @return New Link State Message or NULL in case of error
+ */
+extern struct ls_message *ls_parse_msg(struct stream *s);
+
+/**
+ * Delete existing message, freeing all substructure.
+ *
+ * @param msg Link state message to be deleted
+ */
+extern void ls_delete_msg(struct ls_message *msg);
+
+/**
+ * Send Link State Message as new ZAPI Opaque message of type Link State.
+ * If destination is not NULL, message is sent as Unicast otherwise it is
+ * broadcast to all registered daemon.
+ *
+ * @param zclient Zebra Client
+ * @param msg Link State Message to be sent
+ * @param dst Destination daemon for unicast message,
+ * NULL for broadcast message
+ *
+ * @return 0 on success, -1 otherwise
+ */
+extern int ls_send_msg(struct zclient *zclient, struct ls_message *msg,
+ struct zapi_opaque_reg_info *dst);
+
+/**
+ * Create a new Link State Message from a Link State Vertex. If Link State
+ * Message is NULL, a new data structure is dynamically allocated.
+ *
+ * @param msg Link State Message to be filled or NULL
+ * @param vertex Link State Vertex. Must not be NULL
+ *
+ * @return New Link State Message msg parameter is NULL or pointer
+ * to the provided Link State Message
+ */
+extern struct ls_message *ls_vertex2msg(struct ls_message *msg,
+ struct ls_vertex *vertex);
+
+/**
+ * Create a new Link State Message from a Link State Edge. If Link State
+ * Message is NULL, a new data structure is dynamically allocated.
+ *
+ * @param msg Link State Message to be filled or NULL
+ * @param edge Link State Edge. Must not be NULL
+ *
+ * @return New Link State Message msg parameter is NULL or pointer
+ * to the provided Link State Message
+ */
+extern struct ls_message *ls_edge2msg(struct ls_message *msg,
+ struct ls_edge *edge);
+
+/**
+ * Create a new Link State Message from a Link State Subnet. If Link State
+ * Message is NULL, a new data structure is dynamically allocated.
+ *
+ * @param msg Link State Message to be filled or NULL
+ * @param subnet Link State Subnet. Must not be NULL
+ *
+ * @return New Link State Message msg parameter is NULL or pointer
+ * to the provided Link State Message
+ */
+extern struct ls_message *ls_subnet2msg(struct ls_message *msg,
+ struct ls_subnet *subnet);
+
+/**
+ * Send all the content of the Link State Data Base to the given destination.
+ * Link State content is sent is this order: Vertices, Edges, Subnet.
+ * This function must be used when a daemon request a Link State Data Base
+ * Synchronization.
+ *
+ * @param ted Link State Data Base. Must not be NULL
+ * @param zclient Zebra Client. Must not be NULL
+ * @param dst Destination FRR daemon. Must not be NULL
+ *
+ * @return 0 on success, -1 otherwise
+ */
+extern int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient,
+ struct zapi_opaque_reg_info *dst);
+
+/**
+ * Dump all Link State Data Base elements for debugging purposes
+ *
+ * @param ted Link State Data Base. Must not be NULL
+ *
+ */
+extern void ls_dump_ted(struct ls_ted *ted);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FRR_LINK_STATE_H_ */
diff --git a/lib/log.c b/lib/log.c
index 03ed23a04b..b86d3022b4 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -161,8 +161,9 @@ void zlog_signal(int signo, const char *action, void *siginfo_v,
if (!tc)
bprintfrr(&fb, "no thread information available\n");
else
- bprintfrr(&fb, "in thread %s scheduled from %s:%d\n",
- tc->funcname, tc->schedfrom, tc->schedfrom_line);
+ bprintfrr(&fb, "in thread %s scheduled from %s:%d %s()\n",
+ tc->xref->funcname, tc->xref->xref.file,
+ tc->xref->xref.line, tc->xref->xref.func);
zlog_sigsafe(fb.buf, fb.pos - fb.buf);
}
@@ -179,6 +180,9 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter)
unw_word_t ip, off, sp;
Dl_info dlinfo;
+ memset(&uc, 0, sizeof(uc));
+ memset(&cursor, 0, sizeof(cursor));
+
unw_getcontext(&uc);
unw_init_local(&cursor, &uc);
while (unw_step(&cursor) > 0) {
@@ -300,8 +304,9 @@ void zlog_thread_info(int log_level)
if (tc)
zlog(log_level,
- "Current thread function %s, scheduled from file %s, line %u",
- tc->funcname, tc->schedfrom, tc->schedfrom_line);
+ "Current thread function %s, scheduled from file %s, line %u in %s()",
+ tc->xref->funcname, tc->xref->xref.file,
+ tc->xref->xref.line, tc->xref->xref.func);
else
zlog(log_level, "Current thread not known/applicable");
}
diff --git a/lib/log.h b/lib/log.h
index 3d2f0ed829..7147253644 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -62,16 +62,6 @@ struct message {
const char *str;
};
-/* For logs which have error codes associated with them */
-#define flog_err(ferr_id, format, ...) \
- zlog_err("[EC %u] " format, ferr_id, ##__VA_ARGS__)
-#define flog_err_sys(ferr_id, format, ...) \
- flog_err(ferr_id, format, ##__VA_ARGS__)
-#define flog_warn(ferr_id, format, ...) \
- zlog_warn("[EC %u] " format, ferr_id, ##__VA_ARGS__)
-#define flog(priority, ferr_id, format, ...) \
- zlog(priority, "[EC %u] " format, ferr_id, ##__VA_ARGS__)
-
extern void zlog_thread_info(int log_level);
#define ZLOG_FILTERS_MAX 100 /* Max # of filters at once */
diff --git a/lib/module.c b/lib/module.c
index 14d5cfd44f..3d299a6a2e 100644
--- a/lib/module.c
+++ b/lib/module.c
@@ -43,6 +43,8 @@ union _frrmod_runtime_u frrmod_default = {
},
};
+XREF_SETUP()
+
// if defined(HAVE_SYS_WEAK_ALIAS_ATTRIBUTE)
// union _frrmod_runtime_u _frrmod_this_module
// __attribute__((weak, alias("frrmod_default")));
diff --git a/lib/module.h b/lib/module.h
index 79cf52d75a..5d8d9cfbcc 100644
--- a/lib/module.h
+++ b/lib/module.h
@@ -20,6 +20,9 @@
#include <stdint.h>
#include <stdbool.h>
+#include "compiler.h"
+#include "xref.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -75,7 +78,10 @@ extern union _frrmod_runtime_u _frrmod_this_module;
DSO_LOCAL union _frrmod_runtime_u _frrmod_this_module = {{ \
NULL, \
&_frrmod_info, \
- }};
+ }}; \
+ XREF_SETUP() \
+ /* end */
+
#define FRR_MODULE_SETUP(...) \
FRR_COREMOD_SETUP(__VA_ARGS__) \
DSO_SELF struct frrmod_runtime *frr_module = &_frrmod_this_module.r;
diff --git a/lib/network.c b/lib/network.c
index d2482bd55e..411661a5e1 100644
--- a/lib/network.c
+++ b/lib/network.c
@@ -121,21 +121,3 @@ float ntohf(float net)
{
return htonf(net);
}
-
-/**
- * Helper function that returns a random long value. The main purpose of
- * this function is to hide a `random()` call that gets flagged by coverity
- * scan and put it into one place.
- *
- * The main usage of this function should be for generating jitter or weak
- * random values for simple purposes.
- *
- * See 'man 3 random' for more information.
- *
- * \returns random long integer.
- */
-long frr_weak_random(void)
-{
- /* coverity[dont_call] */
- return random();
-}
diff --git a/lib/network.h b/lib/network.h
index 83c9e59e76..4a9666984f 100644
--- a/lib/network.h
+++ b/lib/network.h
@@ -45,7 +45,23 @@ extern int set_cloexec(int fd);
extern float htonf(float);
extern float ntohf(float);
-extern long frr_weak_random(void);
+/**
+ * Helper function that returns a random long value. The main purpose of
+ * this function is to hide a `random()` call that gets flagged by coverity
+ * scan and put it into one place.
+ *
+ * The main usage of this function should be for generating jitter or weak
+ * random values for simple purposes.
+ *
+ * See 'man 3 random' for more information.
+ *
+ * \returns random long integer.
+ */
+static inline long frr_weak_random(void)
+{
+ /* coverity[dont_call] */
+ return random();
+}
#ifdef __cplusplus
}
diff --git a/lib/northbound.h b/lib/northbound.h
index 3f6e4dc46e..8dd6b4c337 100644
--- a/lib/northbound.h
+++ b/lib/northbound.h
@@ -552,7 +552,7 @@ struct nb_node {
* from working properly on shared libraries. For those compilers, use a fixed
* size array to work around the problem.
*/
-#define YANG_MODULE_MAX_NODES 1400
+#define YANG_MODULE_MAX_NODES 2000
struct frr_yang_module_info {
/* YANG module name. */
@@ -600,6 +600,7 @@ enum nb_client {
NB_CLIENT_CONFD,
NB_CLIENT_SYSREPO,
NB_CLIENT_GRPC,
+ NB_CLIENT_PCEP,
};
/* Northbound context. */
@@ -621,6 +622,8 @@ struct nb_context {
} sysrepo;
struct {
} grpc;
+ struct {
+ } pcep;
} client_data;
#endif
};
diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c
index 7048df99fb..ad7dad5cb2 100644
--- a/lib/northbound_cli.c
+++ b/lib/northbound_cli.c
@@ -693,6 +693,12 @@ static int nb_write_config(struct nb_config *config, enum nb_cfg_format format,
__func__, safe_strerror(errno));
return -1;
}
+ if (fchmod(fd, CONFIGFILE_MASK) != 0) {
+ flog_warn(EC_LIB_SYSTEM_CALL,
+ "%s: fchmod() failed: %s(%d):", __func__,
+ safe_strerror(errno), errno);
+ return -1;
+ }
/* Make vty for configuration file. */
file_vty = vty_new();
@@ -1820,20 +1826,20 @@ static struct cmd_node nb_debug_node = {
void nb_cli_install_default(int node)
{
- install_element(node, &show_config_candidate_section_cmd);
+ _install_element(node, &show_config_candidate_section_cmd);
if (frr_get_cli_mode() != FRR_CLI_TRANSACTIONAL)
return;
- install_element(node, &config_commit_cmd);
- install_element(node, &config_commit_comment_cmd);
- install_element(node, &config_commit_check_cmd);
- install_element(node, &config_update_cmd);
- install_element(node, &config_discard_cmd);
- install_element(node, &show_config_running_cmd);
- install_element(node, &show_config_candidate_cmd);
- install_element(node, &show_config_compare_cmd);
- install_element(node, &show_config_transaction_cmd);
+ _install_element(node, &config_commit_cmd);
+ _install_element(node, &config_commit_comment_cmd);
+ _install_element(node, &config_commit_check_cmd);
+ _install_element(node, &config_update_cmd);
+ _install_element(node, &config_discard_cmd);
+ _install_element(node, &show_config_running_cmd);
+ _install_element(node, &show_config_candidate_cmd);
+ _install_element(node, &show_config_compare_cmd);
+ _install_element(node, &show_config_transaction_cmd);
}
/* YANG module autocomplete. */
diff --git a/lib/printf/printf-pos.c b/lib/printf/printf-pos.c
index cc03f7ef9a..ac775bea4e 100644
--- a/lib/printf/printf-pos.c
+++ b/lib/printf/printf-pos.c
@@ -384,6 +384,7 @@ reswitch: switch (ch) {
goto error;
break;
#endif /* !NO_FLOATING_POINT */
+#ifdef DANGEROUS_PERCENT_N
case 'n':
if (flags & INTMAXT)
error = addtype(&types, TP_INTMAXT);
@@ -404,6 +405,7 @@ reswitch: switch (ch) {
if (error)
goto error;
continue; /* no output */
+#endif
case 'O':
flags |= LONGINT;
/*FALLTHROUGH*/
@@ -576,6 +578,7 @@ reswitch: switch (ch) {
goto error;
break;
#endif /* !NO_FLOATING_POINT */
+#ifdef DANGEROUS_PERCENT_N
case 'n':
if (flags & INTMAXT)
error = addtype(&types, TP_INTMAXT);
@@ -596,6 +599,7 @@ reswitch: switch (ch) {
if (error)
goto error;
continue; /* no output */
+#endif
case 'O':
flags |= LONGINT;
/*FALLTHROUGH*/
diff --git a/lib/printf/vfprintf.c b/lib/printf/vfprintf.c
index 6ffccb3811..a0634cde4b 100644
--- a/lib/printf/vfprintf.c
+++ b/lib/printf/vfprintf.c
@@ -503,6 +503,11 @@ reswitch: switch (ch) {
size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp);
sign = '\0';
break;
+#ifdef DANGEROUS_PERCENT_N
+ /* FRR does not use %n in printf formats. This is just left
+ * here in case someone tries to use %n and starts debugging
+ * why the f* it doesn't work
+ */
case 'n':
/*
* Assignment-like behavior is specified if the
@@ -526,6 +531,7 @@ reswitch: switch (ch) {
else
*GETARG(int *) = ret;
continue; /* no output */
+#endif
case 'O':
flags |= LONGINT;
/*FALLTHROUGH*/
diff --git a/lib/privs.c b/lib/privs.c
index 1bb5d059c8..5ca3c0d886 100644
--- a/lib/privs.c
+++ b/lib/privs.c
@@ -587,6 +587,8 @@ void zprivs_preinit(struct zebra_privs_t *zprivs)
}
}
+struct zebra_privs_t *lib_privs;
+
void zprivs_init(struct zebra_privs_t *zprivs)
{
gid_t groups[NGROUPS_MAX] = {};
@@ -598,6 +600,8 @@ void zprivs_init(struct zebra_privs_t *zprivs)
|| zprivs->cap_num_i))
return;
+ lib_privs = zprivs;
+
if (zprivs->user) {
ngroups = array_size(groups);
if (getgrouplist(zprivs->user, zprivs_state.zgid, groups,
@@ -701,6 +705,8 @@ void zprivs_terminate(struct zebra_privs_t *zprivs)
{
struct zebra_privs_refs_t *refs;
+ lib_privs = NULL;
+
if (!zprivs) {
fprintf(stderr, "%s: no privs struct given, terminating",
__func__);
diff --git a/lib/privs.h b/lib/privs.h
index 18ba8e8888..2dcdbe2e6c 100644
--- a/lib/privs.h
+++ b/lib/privs.h
@@ -100,6 +100,8 @@ struct zprivs_ids_t {
gid_t gid_vty; /* vty gid */
};
+extern struct zebra_privs_t *lib_privs;
+
/* initialise zebra privileges */
extern void zprivs_preinit(struct zebra_privs_t *zprivs);
extern void zprivs_init(struct zebra_privs_t *zprivs);
diff --git a/lib/resolver.c b/lib/resolver.c
index e5caadb2d0..c01284e29e 100644
--- a/lib/resolver.c
+++ b/lib/resolver.c
@@ -19,6 +19,9 @@
#include "lib_errors.h"
#include "resolver.h"
#include "command.h"
+#include "xref.h"
+
+XREF_SETUP()
struct resolver_state {
ares_channel channel;
diff --git a/lib/route_types.pl b/lib/route_types.pl
index 759e9b4729..8c216e13bd 100755
--- a/lib/route_types.pl
+++ b/lib/route_types.pl
@@ -130,7 +130,7 @@ sub codelist {
$str =~ s/ $//;
push @lines, $str . "\\n\" \\\n";
push @lines, " \" > - selected route, * - FIB route, q - queued, r - rejected, b - backup\\n\"";
- push @lines, " \" t - trapped, o - offload failure\\n\"";
+ push @lines, " \" t - trapped, o - offload failure\\n\\n\"";
return join("", @lines);
diff --git a/lib/routemap.c b/lib/routemap.c
index 004beb3628..7714086672 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -1347,7 +1347,7 @@ enum rmap_compile_rets route_map_add_match(struct route_map_index *index,
get_route_map_delete_event(type);
route_map_upd8_dependency(
delete_rmap_event_type,
- rule_key,
+ rule->rule_str,
index->map->name);
}
@@ -2240,7 +2240,7 @@ static void route_map_pentry_update(route_map_event_t event,
}
}
-static void route_map_pentry_process_dependency(struct hash_bucket *backet,
+static void route_map_pentry_process_dependency(struct hash_bucket *bucket,
void *data)
{
char *rmap_name = NULL;
@@ -2253,7 +2253,7 @@ static void route_map_pentry_process_dependency(struct hash_bucket *backet,
(struct route_map_pentry_dep *)data;
unsigned char family = pentry_dep->pentry->prefix.family;
- dep_data = (struct route_map_dep_data *)backet->data;
+ dep_data = (struct route_map_dep_data *)bucket->data;
if (!dep_data)
return;
@@ -2399,6 +2399,7 @@ route_map_result_t route_map_apply(struct route_map *map,
index = route_map_get_index(map, prefix, object,
(uint8_t *)&match_ret);
if (index) {
+ index->applied++;
if (rmap_debug)
zlog_debug(
"Best match route-map: %s, sequence: %d for pfx: %pFX, result: %s",
@@ -2586,22 +2587,23 @@ static void route_map_clear_reference(struct hash_bucket *bucket, void *arg)
struct route_map_dep *dep = bucket->data;
struct route_map_dep_data *dep_data = NULL, tmp_dep_data;
- if (arg) {
- memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data));
- tmp_dep_data.rname = arg;
- dep_data = hash_release(dep->dep_rmap_hash,
- &tmp_dep_data);
- if (dep_data) {
- XFREE(MTYPE_ROUTE_MAP_NAME, dep_data->rname);
- XFREE(MTYPE_ROUTE_MAP_DEP_DATA, dep_data);
- }
- if (!dep->dep_rmap_hash->count) {
- dep = hash_release(dep->this_hash,
- (void *)dep->dep_name);
- hash_free(dep->dep_rmap_hash);
- XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name);
- XFREE(MTYPE_ROUTE_MAP_DEP, dep);
- }
+ memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data));
+ tmp_dep_data.rname = arg;
+ dep_data = hash_release(dep->dep_rmap_hash, &tmp_dep_data);
+ if (dep_data) {
+ if (rmap_debug)
+ zlog_debug("Clearing reference for %s to %s count: %d",
+ dep->dep_name, tmp_dep_data.rname,
+ dep_data->refcnt);
+
+ XFREE(MTYPE_ROUTE_MAP_NAME, dep_data->rname);
+ XFREE(MTYPE_ROUTE_MAP_DEP_DATA, dep_data);
+ }
+ if (!dep->dep_rmap_hash->count) {
+ dep = hash_release(dep->this_hash, (void *)dep->dep_name);
+ hash_free(dep->dep_rmap_hash);
+ XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name);
+ XFREE(MTYPE_ROUTE_MAP_DEP, dep);
}
}
@@ -2609,6 +2611,9 @@ static void route_map_clear_all_references(char *rmap_name)
{
int i;
+ if (rmap_debug)
+ zlog_debug("Clearing references for %s", rmap_name);
+
for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) {
hash_iterate(route_map_dep_hash[i], route_map_clear_reference,
(void *)rmap_name);
@@ -2763,12 +2768,19 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name,
memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data));
tmp_dep_data.rname = rname;
dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data);
-
- if (!dep_data)
+ /*
+ * If dep_data is NULL then something has gone seriously
+ * wrong in route-map handling. Note it and prevent
+ * the crash.
+ */
+ if (!dep_data) {
+ zlog_warn(
+ "route-map dependency for route-map %s: %s is not correct",
+ rmap_name, dep_name);
goto out;
+ }
- if (dep_data->refcnt)
- dep_data->refcnt--;
+ dep_data->refcnt--;
if (!dep_data->refcnt) {
ret_dep_data = hash_release(dep->dep_rmap_hash,
diff --git a/lib/sigevent.c b/lib/sigevent.c
index 8d583096f6..64cec1385d 100644
--- a/lib/sigevent.c
+++ b/lib/sigevent.c
@@ -258,7 +258,7 @@ core_handler(int signo, siginfo_t *siginfo, void *context)
static void trap_default_signals(void)
{
static const int core_signals[] = {
- SIGQUIT, SIGILL,
+ SIGQUIT, SIGILL, SIGABRT,
#ifdef SIGEMT
SIGEMT,
#endif
diff --git a/lib/smux.h b/lib/smux.h
index 6896f02354..e07df2369f 100644
--- a/lib/smux.h
+++ b/lib/smux.h
@@ -44,6 +44,29 @@ extern "C" {
#define IN_ADDR_SIZE sizeof(struct in_addr)
+/* IANAipRouteProtocol */
+#define IANAIPROUTEPROTOCOLOTHER 1
+#define IANAIPROUTEPROTOCOLLOCAL 2
+#define IANAIPROUTEPROTOCOLNETMGMT 3
+#define IANAIPROUTEPROTOCOLICMP 4
+#define IANAIPROUTEPROTOCOLEGP 5
+#define IANAIPROUTEPROTOCOLGGP 6
+#define IANAIPROUTEPROTOCOLHELLO 7
+#define IANAIPROUTEPROTOCOLRIP 8
+#define IANAIPROUTEPROTOCOLISIS 9
+#define IANAIPROUTEPROTOCOLESIS 10
+#define IANAIPROUTEPROTOCOLCISCOIGRP 11
+#define IANAIPROUTEPROTOCOLBBNSPFIGP 12
+#define IANAIPROUTEPROTOCOLOSPF 13
+#define IANAIPROUTEPROTOCOLBGP 14
+#define IANAIPROUTEPROTOCOLIDPR 15
+#define IANAIPROUTEPROTOCOLCISCOEIGRP 16
+#define IANAIPROUTEPROTOCOLDVMRP 17
+
+#define INETADDRESSTYPEUNKNOWN 0
+#define INETADDRESSTYPEIPV4 1
+#define INETADDRESSTYPEIPV6 2
+
#undef REGISTER_MIB
#define REGISTER_MIB(descr, var, vartype, theoid) \
smux_register_mib(descr, (struct variable *)var, \
@@ -56,19 +79,29 @@ struct trap_object {
oid name[MAX_OID_LEN];
};
+struct index_oid {
+ int indexlen;
+ oid indexname[MAX_OID_LEN];
+};
/* Declare SMUX return value. */
#define SNMP_LOCAL_VARIABLES \
static long snmp_int_val __attribute__((unused)); \
static struct in_addr snmp_in_addr_val __attribute__((unused));
-
+ static uint8_t snmp_octet_val __attribute__((unused));
#define SNMP_INTEGER(V) \
(*var_len = sizeof(snmp_int_val), snmp_int_val = V, \
(uint8_t *)&snmp_int_val)
+#define SNMP_OCTET(V) \
+ (*var_len = sizeof(snmp_octet_val), snmp_octet_val = V, \
+ (uint8_t *)&snmp_octet_val)
+
#define SNMP_IPADDRESS(V) \
(*var_len = sizeof(struct in_addr), snmp_in_addr_val = V, \
(uint8_t *)&snmp_in_addr_val)
+#define SNMP_IP6ADDRESS(V) (*var_len = sizeof(struct in6_addr), (uint8_t *)&V)
+
extern void smux_init(struct thread_master *tm);
extern void smux_register_mib(const char *, struct variable *, size_t, int,
oid[], size_t);
@@ -98,14 +131,24 @@ extern int smux_header_table(struct variable *, oid *, size_t *, int, size_t *,
The use of the arguments may differ depending on the implementation
used.
*/
-extern int smux_trap(struct variable *, size_t, const oid *, size_t,
- const oid *, size_t, const oid *, size_t,
- const struct trap_object *, size_t, uint8_t);
-
+extern void smux_trap(struct variable *, size_t, const oid *, size_t,
+ const oid *, size_t, const oid *, size_t,
+ const struct trap_object *, size_t, uint8_t);
+
+extern int smux_trap_multi_index(struct variable *vp, size_t vp_len,
+ const oid *ename, size_t enamelen,
+ const oid *name, size_t namelen,
+ struct index_oid *iname, size_t index_len,
+ const struct trap_object *trapobj,
+ size_t trapobjlen, uint8_t sptrap);
extern int oid_compare(const oid *, int, const oid *, int);
extern void oid2in_addr(oid[], int, struct in_addr *);
+extern void oid2int(oid oid[], int *dest);
extern void *oid_copy(void *, const void *, size_t);
extern void oid_copy_addr(oid[], const struct in_addr *, int);
+extern void oid_copy_int(oid oid[], int *val);
+extern void oid2string(oid oid[], int len, char *string);
+extern void oid_copy_str(oid oid[], const char *string, int len);
#ifdef __cplusplus
}
diff --git a/lib/snmp.c b/lib/snmp.c
index 736a3c62b8..e92f622bb9 100644
--- a/lib/snmp.c
+++ b/lib/snmp.c
@@ -64,6 +64,19 @@ void oid2in_addr(oid oid[], int len, struct in_addr *addr)
*pnt++ = oid[i];
}
+void oid2int(oid oid[], int *dest)
+{
+ uint8_t i;
+ uint8_t *pnt;
+ int network_dest;
+
+ pnt = (uint8_t *)&network_dest;
+
+ for (i = 0; i < sizeof(int); i++)
+ *pnt++ = oid[i];
+ *dest = ntohl(network_dest);
+}
+
void oid_copy_addr(oid oid[], const struct in_addr *addr, int len)
{
int i;
@@ -78,6 +91,47 @@ void oid_copy_addr(oid oid[], const struct in_addr *addr, int len)
oid[i] = *pnt++;
}
+void oid_copy_int(oid oid[], int *val)
+{
+ uint8_t i;
+ const uint8_t *pnt;
+ int network_val;
+
+ network_val = htonl(*val);
+ pnt = (uint8_t *)&network_val;
+
+ for (i = 0; i < sizeof(int); i++)
+ oid[i] = *pnt++;
+}
+
+void oid2string(oid oid[], int len, char *string)
+{
+ int i;
+ uint8_t *pnt;
+
+ if (len == 0)
+ return;
+
+ pnt = (uint8_t *)string;
+
+ for (i = 0; i < len; i++)
+ *pnt++ = (uint8_t)oid[i];
+}
+
+void oid_copy_str(oid oid[], const char *string, int len)
+{
+ int i;
+ const uint8_t *pnt;
+
+ if (len == 0)
+ return;
+
+ pnt = (uint8_t *)string;
+
+ for (i = 0; i < len; i++)
+ oid[i] = *pnt++;
+}
+
int smux_header_generic(struct variable *v, oid *name, size_t *length,
int exact, size_t *var_len, WriteMethod **write_method)
{
diff --git a/lib/sockunion.c b/lib/sockunion.c
index c999845659..1dbf77efa4 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -708,3 +708,20 @@ static ssize_t printfrr_psu(char *buf, size_t bsz, const char *fmt,
fb.pos[0] = '\0';
return consumed;
}
+
+int sockunion_is_null(const union sockunion *su)
+{
+ unsigned char null_s6_addr[16] = {0};
+
+ switch (sockunion_family(su)) {
+ case AF_UNSPEC:
+ return 1;
+ case AF_INET:
+ return (su->sin.sin_addr.s_addr == 0);
+ case AF_INET6:
+ return !memcmp(su->sin6.sin6_addr.s6_addr, null_s6_addr,
+ sizeof(null_s6_addr));
+ default:
+ return 0;
+ }
+}
diff --git a/lib/sockunion.h b/lib/sockunion.h
index 72f12b77ca..5e80ba1090 100644
--- a/lib/sockunion.h
+++ b/lib/sockunion.h
@@ -102,6 +102,7 @@ extern union sockunion *sockunion_getpeername(int);
extern union sockunion *sockunion_dup(const union sockunion *);
extern void sockunion_free(union sockunion *);
extern void sockunion_init(union sockunion *);
+extern int sockunion_is_null(const union sockunion *su);
#ifdef _FRR_ATTRIBUTE_PRINTFRR
#pragma FRR printfrr_ext "%pSU" (union sockunion *)
diff --git a/lib/stream.c b/lib/stream.c
index e4e37b7315..ef73c2fdc9 100644
--- a/lib/stream.c
+++ b/lib/stream.c
@@ -57,7 +57,7 @@ DEFINE_MTYPE_STATIC(LIB, STREAM_FIFO, "Stream FIFO")
#define STREAM_WARN_OFFSETS(S) \
do { \
flog_warn(EC_LIB_STREAM, \
- "&(struct stream): %p, size: %lu, getp: %lu, endp: %lu\n", \
+ "&(struct stream): %p, size: %lu, getp: %lu, endp: %lu", \
(void *)(S), (unsigned long)(S)->size, \
(unsigned long)(S)->getp, (unsigned long)(S)->endp); \
zlog_backtrace(LOG_WARNING); \
@@ -93,7 +93,7 @@ DEFINE_MTYPE_STATIC(LIB, STREAM_FIFO, "Stream FIFO")
if (((S)->endp + (Z)) > (S)->size) { \
flog_warn( \
EC_LIB_STREAM, \
- "CHECK_SIZE: truncating requested size %lu\n", \
+ "CHECK_SIZE: truncating requested size %lu", \
(unsigned long)(Z)); \
STREAM_WARN_OFFSETS(S); \
(Z) = (S)->size - (S)->endp; \
diff --git a/lib/subdir.am b/lib/subdir.am
index 038282a99b..d5ffa08546 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -26,6 +26,7 @@ lib_libfrr_la_SOURCES = \
lib/filter_nb.c \
lib/frrcu.c \
lib/frrlua.c \
+ lib/frrscript.c \
lib/frr_pthread.c \
lib/frrstr.c \
lib/getopt.c \
@@ -48,6 +49,7 @@ lib_libfrr_la_SOURCES = \
lib/libfrr.c \
lib/libfrr_trace.c \
lib/linklist.c \
+ lib/link_state.c \
lib/log.c \
lib/log_filter.c \
lib/log_vty.c \
@@ -101,6 +103,7 @@ lib_libfrr_la_SOURCES = \
lib/vty.c \
lib/wheel.c \
lib/workqueue.c \
+ lib/xref.c \
lib/yang.c \
lib/yang_translator.c \
lib/yang_wrappers.c \
@@ -184,6 +187,7 @@ pkginclude_HEADERS += \
lib/filter.h \
lib/freebsd-queue.h \
lib/frrlua.h \
+ lib/frrscript.h \
lib/frr_pthread.h \
lib/frratomic.h \
lib/frrcu.h \
@@ -208,6 +212,7 @@ pkginclude_HEADERS += \
lib/libfrr_trace.h \
lib/libospf.h \
lib/linklist.h \
+ lib/link_state.h \
lib/log.h \
lib/log_vty.h \
lib/md5.h \
@@ -264,6 +269,7 @@ pkginclude_HEADERS += \
lib/vxlan.h \
lib/wheel.h \
lib/workqueue.h \
+ lib/xref.h \
lib/yang.h \
lib/yang_translator.h \
lib/yang_wrappers.h \
@@ -407,6 +413,7 @@ lib_clippy_CFLAGS = $(PYTHON_CFLAGS)
lib_clippy_LDADD = $(PYTHON_LIBS) $(UST_LIBS)
lib_clippy_LDFLAGS = -export-dynamic
lib_clippy_SOURCES = \
+ lib/jhash.c \
lib/clippy.c \
lib/command_graph.c \
lib/command_lex.l \
diff --git a/lib/thread.c b/lib/thread.c
index c886058355..e0d734a951 100644
--- a/lib/thread.c
+++ b/lib/thread.c
@@ -119,7 +119,7 @@ static void vty_out_cpu_thread_history(struct vty *vty,
a->total_active, a->cpu.total / 1000, a->cpu.total % 1000,
a->total_calls, (a->cpu.total / a->total_calls), a->cpu.max,
(a->real.total / a->total_calls), a->real.max);
- vty_out(vty, " %c%c%c%c%c %s\n",
+ vty_out(vty, " %c%c%c%c%c %s\n",
a->types & (1 << THREAD_READ) ? 'R' : ' ',
a->types & (1 << THREAD_WRITE) ? 'W' : ' ',
a->types & (1 << THREAD_TIMER) ? 'T' : ' ',
@@ -188,7 +188,7 @@ static void cpu_record_print(struct vty *vty, uint8_t filter)
name);
vty_out(vty, "-------------------------------%s\n",
underline);
- vty_out(vty, "%21s %18s %18s\n", "",
+ vty_out(vty, "%30s %18s %18s\n", "",
"CPU (user+system):", "Real (wall-clock):");
vty_out(vty,
"Active Runtime(ms) Invoked Avg uSec Max uSecs");
@@ -211,7 +211,7 @@ static void cpu_record_print(struct vty *vty, uint8_t filter)
vty_out(vty, "\n");
vty_out(vty, "Total thread statistics\n");
vty_out(vty, "-------------------------\n");
- vty_out(vty, "%21s %18s %18s\n", "",
+ vty_out(vty, "%30s %18s %18s\n", "",
"CPU (user+system):", "Real (wall-clock):");
vty_out(vty, "Active Runtime(ms) Invoked Avg uSec Max uSecs");
vty_out(vty, " Avg uSec Max uSecs");
@@ -342,7 +342,7 @@ static void show_thread_poll_helper(struct vty *vty, struct thread_master *m)
if (!thread)
vty_out(vty, "ERROR ");
else
- vty_out(vty, "%s ", thread->funcname);
+ vty_out(vty, "%s ", thread->xref->funcname);
} else
vty_out(vty, " ");
@@ -352,7 +352,7 @@ static void show_thread_poll_helper(struct vty *vty, struct thread_master *m)
if (!thread)
vty_out(vty, "ERROR\n");
else
- vty_out(vty, "%s\n", thread->funcname);
+ vty_out(vty, "%s\n", thread->xref->funcname);
} else
vty_out(vty, "\n");
}
@@ -633,9 +633,6 @@ unsigned long thread_timer_remain_second(struct thread *thread)
return thread_timer_remain_msec(thread) / 1000LL;
}
-#define debugargdef const char *funcname, const char *schedfrom, int fromln
-#define debugargpass funcname, schedfrom, fromln
-
struct timeval thread_timer_remain(struct thread *thread)
{
struct timeval remain;
@@ -678,7 +675,7 @@ char *thread_timer_to_hhmmss(char *buf, int buf_size,
/* Get new thread. */
static struct thread *thread_get(struct thread_master *m, uint8_t type,
int (*func)(struct thread *), void *arg,
- debugargdef)
+ const struct xref_threadsched *xref)
{
struct thread *thread = thread_list_pop(&m->unuse);
struct cpu_thread_history tmp;
@@ -707,18 +704,17 @@ static struct thread *thread_get(struct thread_master *m, uint8_t type,
* This hopefully saves us some serious
* hash_get lookups.
*/
- if (thread->funcname != funcname || thread->func != func) {
+ if ((thread->xref && thread->xref->funcname != xref->funcname)
+ || thread->func != func) {
tmp.func = func;
- tmp.funcname = funcname;
+ tmp.funcname = xref->funcname;
thread->hist =
hash_get(m->cpu_record, &tmp,
(void *(*)(void *))cpu_record_hash_alloc);
}
thread->hist->total_active++;
thread->func = func;
- thread->funcname = funcname;
- thread->schedfrom = schedfrom;
- thread->schedfrom_line = fromln;
+ thread->xref = xref;
return thread;
}
@@ -832,21 +828,23 @@ done:
}
/* Add new read thread. */
-struct thread *funcname_thread_add_read_write(int dir, struct thread_master *m,
- int (*func)(struct thread *),
- void *arg, int fd,
- struct thread **t_ptr,
- debugargdef)
+struct thread *_thread_add_read_write(const struct xref_threadsched *xref,
+ struct thread_master *m,
+ int (*func)(struct thread *),
+ void *arg, int fd, struct thread **t_ptr)
{
+ int dir = xref->thread_type;
struct thread *thread = NULL;
struct thread **thread_array;
if (dir == THREAD_READ)
- frrtrace(9, frr_libfrr, schedule_read, m, funcname, schedfrom,
- fromln, t_ptr, fd, 0, arg, 0);
+ frrtrace(9, frr_libfrr, schedule_read, m,
+ xref->funcname, xref->xref.file, xref->xref.line,
+ t_ptr, fd, 0, arg, 0);
else
- frrtrace(9, frr_libfrr, schedule_write, m, funcname, schedfrom,
- fromln, t_ptr, fd, 0, arg, 0);
+ frrtrace(9, frr_libfrr, schedule_write, m,
+ xref->funcname, xref->xref.file, xref->xref.line,
+ t_ptr, fd, 0, arg, 0);
assert(fd >= 0 && fd < m->fd_limit);
frr_with_mutex(&m->mtx) {
@@ -882,7 +880,7 @@ struct thread *funcname_thread_add_read_write(int dir, struct thread_master *m,
/* make sure we have room for this fd + pipe poker fd */
assert(queuepos + 1 < m->handler.pfdsize);
- thread = thread_get(m, dir, func, arg, debugargpass);
+ thread = thread_get(m, dir, func, arg, xref);
m->handler.pfds[queuepos].fd = fd;
m->handler.pfds[queuepos].events |=
@@ -910,10 +908,10 @@ struct thread *funcname_thread_add_read_write(int dir, struct thread_master *m,
}
static struct thread *
-funcname_thread_add_timer_timeval(struct thread_master *m,
- int (*func)(struct thread *), int type,
- void *arg, struct timeval *time_relative,
- struct thread **t_ptr, debugargdef)
+_thread_add_timer_timeval(const struct xref_threadsched *xref,
+ struct thread_master *m, int (*func)(struct thread *),
+ int type, void *arg, struct timeval *time_relative,
+ struct thread **t_ptr)
{
struct thread *thread;
@@ -922,7 +920,8 @@ funcname_thread_add_timer_timeval(struct thread_master *m,
assert(type == THREAD_TIMER);
assert(time_relative);
- frrtrace(9, frr_libfrr, schedule_timer, m, funcname, schedfrom, fromln,
+ frrtrace(9, frr_libfrr, schedule_timer, m,
+ xref->funcname, xref->xref.file, xref->xref.line,
t_ptr, 0, 0, arg, (long)time_relative->tv_sec);
frr_with_mutex(&m->mtx) {
@@ -930,7 +929,7 @@ funcname_thread_add_timer_timeval(struct thread_master *m,
/* thread is already scheduled; don't reschedule */
return NULL;
- thread = thread_get(m, type, func, arg, debugargpass);
+ thread = thread_get(m, type, func, arg, xref);
frr_with_mutex(&thread->mtx) {
monotime(&thread->u.sands);
@@ -951,10 +950,10 @@ funcname_thread_add_timer_timeval(struct thread_master *m,
/* Add timer event thread. */
-struct thread *funcname_thread_add_timer(struct thread_master *m,
- int (*func)(struct thread *),
- void *arg, long timer,
- struct thread **t_ptr, debugargdef)
+struct thread *_thread_add_timer(const struct xref_threadsched *xref,
+ struct thread_master *m,
+ int (*func)(struct thread *),
+ void *arg, long timer, struct thread **t_ptr)
{
struct timeval trel;
@@ -963,16 +962,16 @@ struct thread *funcname_thread_add_timer(struct thread_master *m,
trel.tv_sec = timer;
trel.tv_usec = 0;
- return funcname_thread_add_timer_timeval(m, func, THREAD_TIMER, arg,
- &trel, t_ptr, debugargpass);
+ return _thread_add_timer_timeval(xref, m, func, THREAD_TIMER, arg,
+ &trel, t_ptr);
}
/* Add timer event thread with "millisecond" resolution */
-struct thread *funcname_thread_add_timer_msec(struct thread_master *m,
- int (*func)(struct thread *),
- void *arg, long timer,
- struct thread **t_ptr,
- debugargdef)
+struct thread *_thread_add_timer_msec(const struct xref_threadsched *xref,
+ struct thread_master *m,
+ int (*func)(struct thread *),
+ void *arg, long timer,
+ struct thread **t_ptr)
{
struct timeval trel;
@@ -981,29 +980,31 @@ struct thread *funcname_thread_add_timer_msec(struct thread_master *m,
trel.tv_sec = timer / 1000;
trel.tv_usec = 1000 * (timer % 1000);
- return funcname_thread_add_timer_timeval(m, func, THREAD_TIMER, arg,
- &trel, t_ptr, debugargpass);
+ return _thread_add_timer_timeval(xref, m, func, THREAD_TIMER, arg,
+ &trel, t_ptr);
}
/* Add timer event thread with "millisecond" resolution */
-struct thread *funcname_thread_add_timer_tv(struct thread_master *m,
- int (*func)(struct thread *),
- void *arg, struct timeval *tv,
- struct thread **t_ptr, debugargdef)
+struct thread *_thread_add_timer_tv(const struct xref_threadsched *xref,
+ struct thread_master *m,
+ int (*func)(struct thread *),
+ void *arg, struct timeval *tv,
+ struct thread **t_ptr)
{
- return funcname_thread_add_timer_timeval(m, func, THREAD_TIMER, arg, tv,
- t_ptr, debugargpass);
+ return _thread_add_timer_timeval(xref, m, func, THREAD_TIMER, arg, tv,
+ t_ptr);
}
/* Add simple event thread. */
-struct thread *funcname_thread_add_event(struct thread_master *m,
- int (*func)(struct thread *),
- void *arg, int val,
- struct thread **t_ptr, debugargdef)
+struct thread *_thread_add_event(const struct xref_threadsched *xref,
+ struct thread_master *m,
+ int (*func)(struct thread *),
+ void *arg, int val, struct thread **t_ptr)
{
struct thread *thread = NULL;
- frrtrace(9, frr_libfrr, schedule_event, m, funcname, schedfrom, fromln,
+ frrtrace(9, frr_libfrr, schedule_event, m,
+ xref->funcname, xref->xref.file, xref->xref.line,
t_ptr, 0, val, arg, 0);
assert(m != NULL);
@@ -1013,7 +1014,7 @@ struct thread *funcname_thread_add_event(struct thread_master *m,
/* thread is already scheduled; don't reschedule */
break;
- thread = thread_get(m, THREAD_EVENT, func, arg, debugargpass);
+ thread = thread_get(m, THREAD_EVENT, func, arg, xref);
frr_with_mutex(&thread->mtx) {
thread->u.val = val;
thread_list_add_tail(&m->event, thread);
@@ -1239,8 +1240,9 @@ void thread_cancel(struct thread **thread)
master = (*thread)->master;
- frrtrace(9, frr_libfrr, thread_cancel, master, (*thread)->funcname,
- (*thread)->schedfrom, (*thread)->schedfrom_line, NULL, (*thread)->u.fd,
+ frrtrace(9, frr_libfrr, thread_cancel, master,
+ (*thread)->xref->funcname, (*thread)->xref->xref.file,
+ (*thread)->xref->xref.line, NULL, (*thread)->u.fd,
(*thread)->u.val, (*thread)->arg, (*thread)->u.sands.tv_sec);
assert(master->owner == pthread_self());
@@ -1287,8 +1289,8 @@ void thread_cancel_async(struct thread_master *master, struct thread **thread,
if (thread && *thread)
frrtrace(9, frr_libfrr, thread_cancel_async, master,
- (*thread)->funcname, (*thread)->schedfrom,
- (*thread)->schedfrom_line, NULL, (*thread)->u.fd,
+ (*thread)->xref->funcname, (*thread)->xref->xref.file,
+ (*thread)->xref->xref.line, NULL, (*thread)->u.fd,
(*thread)->u.val, (*thread)->arg,
(*thread)->u.sands.tv_sec);
else
@@ -1363,7 +1365,7 @@ static int thread_process_io_helper(struct thread_master *m,
if (!thread) {
if ((actual_state & (POLLHUP|POLLIN)) != POLLHUP)
flog_err(EC_LIB_NO_THREAD,
- "Attempting to process an I/O event but for fd: %d(%d) no thread to handle this!\n",
+ "Attempting to process an I/O event but for fd: %d(%d) no thread to handle this!",
m->handler.pfds[pos].fd, actual_state);
return 0;
}
@@ -1673,8 +1675,9 @@ void thread_call(struct thread *thread)
GETRUSAGE(&before);
thread->real = before.real;
- frrtrace(9, frr_libfrr, thread_call, thread->master, thread->funcname,
- thread->schedfrom, thread->schedfrom_line, NULL, thread->u.fd,
+ frrtrace(9, frr_libfrr, thread_call, thread->master,
+ thread->xref->funcname, thread->xref->xref.file,
+ thread->xref->xref.line, NULL, thread->u.fd,
thread->u.val, thread->arg, thread->u.sands.tv_sec);
pthread_setspecific(thread_current, thread);
@@ -1724,7 +1727,7 @@ void thread_call(struct thread *thread)
flog_warn(
EC_LIB_SLOW_THREAD,
"SLOW THREAD: task %s (%lx) ran for %lums (cpu time %lums)",
- thread->funcname, (unsigned long)thread->func,
+ thread->xref->funcname, (unsigned long)thread->func,
realtime / 1000, cputime / 1000);
}
#endif /* CONSUMED_TIME_CHECK */
@@ -1732,15 +1735,15 @@ void thread_call(struct thread *thread)
}
/* Execute thread */
-void funcname_thread_execute(struct thread_master *m,
- int (*func)(struct thread *), void *arg, int val,
- debugargdef)
+void _thread_execute(const struct xref_threadsched *xref,
+ struct thread_master *m, int (*func)(struct thread *),
+ void *arg, int val)
{
struct thread *thread;
/* Get or allocate new thread to execute. */
frr_with_mutex(&m->mtx) {
- thread = thread_get(m, THREAD_EVENT, func, arg, debugargpass);
+ thread = thread_get(m, THREAD_EVENT, func, arg, xref);
/* Set its event value. */
frr_with_mutex(&thread->mtx) {
diff --git a/lib/thread.h b/lib/thread.h
index eb1b107e7b..6b510fc4c9 100644
--- a/lib/thread.h
+++ b/lib/thread.h
@@ -27,6 +27,7 @@
#include "monotime.h"
#include "frratomic.h"
#include "typesafe.h"
+#include "xref.h"
#ifdef __cplusplus
extern "C" {
@@ -66,6 +67,14 @@ struct cancel_req {
struct thread **threadref;
};
+struct xref_threadsched {
+ struct xref xref;
+
+ const char *funcname;
+ const char *dest;
+ uint32_t thread_type;
+};
+
/* Master of the theads. */
struct thread_master {
char *name;
@@ -107,9 +116,7 @@ struct thread {
struct timeval real;
struct cpu_thread_history *hist; /* cache pointer to cpu_history */
unsigned long yield; /* yield time in microseconds */
- const char *funcname; /* name of thread function */
- const char *schedfrom; /* source file thread was scheduled from */
- int schedfrom_line; /* line number of source file */
+ const struct xref_threadsched *xref; /* origin location */
pthread_mutex_t mtx; /* mutex for thread.c functions */
};
@@ -156,17 +163,45 @@ struct cpu_thread_history {
thread_cancel(&(thread)); \
} while (0)
-#define debugargdef const char *funcname, const char *schedfrom, int fromln
-
-#define thread_add_read(m,f,a,v,t) funcname_thread_add_read_write(THREAD_READ,m,f,a,v,t,#f,__FILE__,__LINE__)
-#define thread_add_write(m,f,a,v,t) funcname_thread_add_read_write(THREAD_WRITE,m,f,a,v,t,#f,__FILE__,__LINE__)
-#define thread_add_timer(m,f,a,v,t) funcname_thread_add_timer(m,f,a,v,t,#f,__FILE__,__LINE__)
-#define thread_add_timer_msec(m,f,a,v,t) funcname_thread_add_timer_msec(m,f,a,v,t,#f,__FILE__,__LINE__)
-#define thread_add_timer_tv(m,f,a,v,t) funcname_thread_add_timer_tv(m,f,a,v,t,#f,__FILE__,__LINE__)
-#define thread_add_event(m,f,a,v,t) funcname_thread_add_event(m,f,a,v,t,#f,__FILE__,__LINE__)
-#define thread_execute(m,f,a,v) funcname_thread_execute(m,f,a,v,#f,__FILE__,__LINE__)
-#define thread_execute_name(m, f, a, v, n) \
- funcname_thread_execute(m, f, a, v, n, __FILE__, __LINE__)
+/*
+ * Macro wrappers to generate xrefs for all thread add calls. Includes
+ * file/line/function info for debugging/tracing.
+ */
+#include "lib/xref.h"
+
+#define _xref_t_a(addfn, type, m, f, a, v, t) \
+ ({ \
+ static const struct xref_threadsched _xref \
+ __attribute__((used)) = { \
+ .xref = XREF_INIT(XREFT_THREADSCHED, NULL, __func__), \
+ .funcname = #f, \
+ .dest = #t, \
+ .thread_type = THREAD_ ## type, \
+ }; \
+ XREF_LINK(_xref.xref); \
+ _thread_add_ ## addfn(&_xref, m, f, a, v, t); \
+ }) \
+ /* end */
+
+#define thread_add_read(m,f,a,v,t) _xref_t_a(read_write, READ, m,f,a,v,t)
+#define thread_add_write(m,f,a,v,t) _xref_t_a(read_write, WRITE, m,f,a,v,t)
+#define thread_add_timer(m,f,a,v,t) _xref_t_a(timer, TIMER, m,f,a,v,t)
+#define thread_add_timer_msec(m,f,a,v,t) _xref_t_a(timer_msec, TIMER, m,f,a,v,t)
+#define thread_add_timer_tv(m,f,a,v,t) _xref_t_a(timer_tv, TIMER, m,f,a,v,t)
+#define thread_add_event(m,f,a,v,t) _xref_t_a(event, TIMER, m,f,a,v,t)
+
+#define thread_execute(m,f,a,v) \
+ ({ \
+ static const struct xref_threadsched _xref \
+ __attribute__((used)) = { \
+ .xref = XREF_INIT(XREFT_THREADSCHED, NULL, __func__), \
+ .funcname = #f, \
+ .dest = NULL, \
+ .thread_type = THREAD_EXECUTE, \
+ }; \
+ XREF_LINK(_xref.xref); \
+ _thread_execute(&_xref, m, f, a, v); \
+ }) /* end */
/* Prototypes. */
extern struct thread_master *thread_master_create(const char *);
@@ -174,35 +209,30 @@ void thread_master_set_name(struct thread_master *master, const char *name);
extern void thread_master_free(struct thread_master *);
extern void thread_master_free_unused(struct thread_master *);
-extern struct thread *
-funcname_thread_add_read_write(int dir, struct thread_master *,
- int (*)(struct thread *), void *, int,
- struct thread **, debugargdef);
-
-extern struct thread *funcname_thread_add_timer(struct thread_master *,
- int (*)(struct thread *),
- void *, long, struct thread **,
- debugargdef);
-
-extern struct thread *
-funcname_thread_add_timer_msec(struct thread_master *, int (*)(struct thread *),
- void *, long, struct thread **, debugargdef);
-
-extern struct thread *funcname_thread_add_timer_tv(struct thread_master *,
- int (*)(struct thread *),
- void *, struct timeval *,
- struct thread **,
- debugargdef);
-
-extern struct thread *funcname_thread_add_event(struct thread_master *,
- int (*)(struct thread *),
- void *, int, struct thread **,
- debugargdef);
-
-extern void funcname_thread_execute(struct thread_master *,
- int (*)(struct thread *), void *, int,
- debugargdef);
-#undef debugargdef
+extern struct thread *_thread_add_read_write(
+ const struct xref_threadsched *xref, struct thread_master *master,
+ int (*fn)(struct thread *), void *arg, int fd, struct thread **tref);
+
+extern struct thread *_thread_add_timer(
+ const struct xref_threadsched *xref, struct thread_master *master,
+ int (*fn)(struct thread *), void *arg, long t, struct thread **tref);
+
+extern struct thread *_thread_add_timer_msec(
+ const struct xref_threadsched *xref, struct thread_master *master,
+ int (*fn)(struct thread *), void *arg, long t, struct thread **tref);
+
+extern struct thread *_thread_add_timer_tv(
+ const struct xref_threadsched *xref, struct thread_master *master,
+ int (*fn)(struct thread *), void *arg, struct timeval *tv,
+ struct thread **tref);
+
+extern struct thread *_thread_add_event(
+ const struct xref_threadsched *xref, struct thread_master *master,
+ int (*fn)(struct thread *), void *arg, int val, struct thread **tref);
+
+extern void _thread_execute(const struct xref_threadsched *xref,
+ struct thread_master *master,
+ int (*fn)(struct thread *), void *arg, int val);
extern void thread_cancel(struct thread **event);
extern void thread_cancel_async(struct thread_master *, struct thread **,
diff --git a/lib/vrf.c b/lib/vrf.c
index 1a9cd7e451..136938783f 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -214,6 +214,53 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name)
return vrf;
}
+/* Update a VRF. If not found, create one.
+ * Arg:
+ * name - The name of the vrf.
+ * vrf_id - The vrf_id of the vrf.
+ * Description: This function first finds the vrf using its name. If the vrf is
+ * found and the vrf-id of the existing vrf does not match the new vrf id, it
+ * will disable the existing vrf and update it with new vrf-id. If the vrf is
+ * not found, it will create the vrf with given name and the new vrf id.
+ */
+struct vrf *vrf_update(vrf_id_t new_vrf_id, const char *name)
+{
+ struct vrf *vrf = NULL;
+
+ /*Treat VRF add for existing vrf as update
+ * Update VRF ID and also update in VRF ID table
+ */
+ if (name)
+ vrf = vrf_lookup_by_name(name);
+ if (vrf && new_vrf_id != VRF_UNKNOWN && vrf->vrf_id != VRF_UNKNOWN
+ && vrf->vrf_id != new_vrf_id) {
+ if (debug_vrf) {
+ zlog_debug(
+ "Vrf Update event: %s old id: %u, new id: %u",
+ name, vrf->vrf_id, new_vrf_id);
+ }
+
+ /*Disable the vrf to simulate implicit delete
+ * so that all stale routes are deleted
+ * This vrf will be enabled down the line
+ */
+ vrf_disable(vrf);
+
+
+ RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf);
+ vrf->vrf_id = new_vrf_id;
+ RB_INSERT(vrf_id_head, &vrfs_by_id, vrf);
+
+ } else {
+
+ /*
+ * vrf_get is implied creation if it does not exist
+ */
+ vrf = vrf_get(new_vrf_id, name);
+ }
+ return vrf;
+}
+
/* Delete a VRF. This is called when the underlying VRF goes away, a
* pre-configured VRF is deleted or when shutting down (vrf_terminate()).
*/
@@ -628,12 +675,12 @@ int vrf_handler_create(struct vty *vty, const char *vrfname,
if (strlen(vrfname) > VRF_NAMSIZ) {
if (vty)
vty_out(vty,
- "%% VRF name %s invalid: length exceeds %d bytes\n",
+ "%% VRF name %s invalid: length exceeds %d bytes",
vrfname, VRF_NAMSIZ);
else
flog_warn(
EC_LIB_VRF_LENGTH,
- "%% VRF name %s invalid: length exceeds %d bytes\n",
+ "%% VRF name %s invalid: length exceeds %d bytes",
vrfname, VRF_NAMSIZ);
return CMD_WARNING_CONFIG_FAILED;
}
@@ -1064,6 +1111,7 @@ static int lib_vrf_create(struct nb_cb_create_args *args)
vrfp = vrf_get(VRF_UNKNOWN, vrfname);
+ vrf_set_user_cfged(vrfp);
nb_running_set_entry(args->dnode, vrfp);
return NB_OK;
@@ -1089,7 +1137,7 @@ static int lib_vrf_destroy(struct nb_cb_destroy_args *args)
vrfp = nb_running_unset_entry(args->dnode);
/* Clear configured flag and invoke delete. */
- UNSET_FLAG(vrfp->status, VRF_CONFIGURED);
+ vrf_reset_user_cfged(vrfp);
vrf_delete(vrfp);
break;
}
diff --git a/lib/vrf.h b/lib/vrf.h
index c636b9ea7e..32e6fb4289 100644
--- a/lib/vrf.h
+++ b/lib/vrf.h
@@ -114,6 +114,7 @@ extern struct vrf_name_head vrfs_by_name;
extern struct vrf *vrf_lookup_by_id(vrf_id_t);
extern struct vrf *vrf_lookup_by_name(const char *);
extern struct vrf *vrf_get(vrf_id_t, const char *);
+extern struct vrf *vrf_update(vrf_id_t new_vrf_id, const char *name);
extern const char *vrf_id_to_name(vrf_id_t vrf_id);
extern vrf_id_t vrf_name_to_id(const char *);
@@ -167,6 +168,20 @@ static inline void vrf_reset_user_cfged(struct vrf *vrf)
UNSET_FLAG(vrf->status, VRF_CONFIGURED);
}
+static inline uint32_t vrf_interface_count(struct vrf *vrf)
+{
+ uint32_t count = 0;
+ struct interface *ifp;
+
+ RB_FOREACH (ifp, if_name_head, &vrf->ifaces_by_name) {
+ /* skip the l3mdev */
+ if (strncmp(ifp->name, vrf->name, VRF_NAMSIZ) == 0)
+ continue;
+ count++;
+ }
+ return count;
+}
+
/*
* Utilities to obtain the user data
*/
diff --git a/lib/vty.c b/lib/vty.c
index 21b3d47b09..65f8d78a96 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -73,7 +73,8 @@ enum event {
#endif /* VTYSH */
};
-static void vty_event(enum event, int, struct vty *);
+static void vty_event_serv(enum event event, int sock);
+static void vty_event(enum event, struct vty *);
/* Extern host structure from command.c */
extern struct host host;
@@ -396,16 +397,6 @@ static void vty_do_window_size(struct vty *vty)
vty_out(vty, "%s", cmd);
}
-#if 0 /* Currently not used. */
-/* Make don't use lflow vty interface. */
-static void
-vty_dont_lflow_ahead (struct vty *vty)
-{
- unsigned char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' };
- vty_out (vty, "%s", cmd);
-}
-#endif /* 0 */
-
/* Authentication of vty */
static void vty_auth(struct vty *vty, char *buf)
{
@@ -1090,11 +1081,6 @@ static void vty_describe_command(struct vty *vty)
vector_free(varcomps);
}
-#if 0
- vty_out (vty, " %-*s %s\n", width
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- desc->str ? desc->str : "");
-#endif /* 0 */
}
if ((token = token_cr)) {
@@ -1299,6 +1285,7 @@ static int vty_execute(struct vty *vty)
#define VTY_NORMAL 0
#define VTY_PRE_ESCAPE 1
#define VTY_ESCAPE 2
+#define VTY_CR 3
/* Escape character command map. */
static void vty_escape_map(unsigned char c, struct vty *vty)
@@ -1340,14 +1327,13 @@ static int vty_read(struct thread *thread)
int nbytes;
unsigned char buf[VTY_READ_BUFSIZ];
- int vty_sock = THREAD_FD(thread);
struct vty *vty = THREAD_ARG(thread);
/* Read raw data from socket */
if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
if (nbytes < 0) {
if (ERRNO_IO_RETRY(errno)) {
- vty_event(VTY_READ, vty_sock, vty);
+ vty_event(VTY_READ, vty);
return 0;
}
vty->monitor = 0; /* disable monitoring to avoid
@@ -1396,12 +1382,6 @@ static int vty_read(struct thread *thread)
case 'Q':
vty_buffer_reset(vty);
break;
-#if 0 /* More line does not work for "show ip bgp". */
- case '\n':
- case '\r':
- vty->status = VTY_MORELINE;
- break;
-#endif
default:
break;
}
@@ -1444,6 +1424,17 @@ static int vty_read(struct thread *thread)
continue;
}
+ if (vty->escape == VTY_CR) {
+ /* if we get CR+NL, the NL results in an extra empty
+ * prompt line being printed without this; just drop
+ * the NL if it immediately follows CR.
+ */
+ vty->escape = VTY_NORMAL;
+
+ if (buf[i] == '\n')
+ continue;
+ }
+
switch (buf[i]) {
case CONTROL('A'):
vty_beginning_of_line(vty);
@@ -1488,9 +1479,12 @@ static int vty_read(struct thread *thread)
case CONTROL('Z'):
vty_end_config(vty);
break;
- case '\n':
case '\r':
+ vty->escape = VTY_CR;
+ /* fallthru */
+ case '\n':
vty_out(vty, "\n");
+ buffer_flush_available(vty->obuf, vty->wfd);
vty_execute(vty);
break;
case '\t':
@@ -1521,8 +1515,8 @@ static int vty_read(struct thread *thread)
if (vty->status == VTY_CLOSE)
vty_close(vty);
else {
- vty_event(VTY_WRITE, vty->wfd, vty);
- vty_event(VTY_READ, vty_sock, vty);
+ vty_event(VTY_WRITE, vty);
+ vty_event(VTY_READ, vty);
}
return 0;
}
@@ -1532,7 +1526,6 @@ static int vty_flush(struct thread *thread)
{
int erase;
buffer_status_t flushrc;
- int vty_sock = THREAD_FD(thread);
struct vty *vty = THREAD_ARG(thread);
/* Tempolary disable read thread. */
@@ -1544,20 +1537,20 @@ static int vty_flush(struct thread *thread)
/* N.B. if width is 0, that means we don't know the window size. */
if ((vty->lines == 0) || (vty->width == 0) || (vty->height == 0))
- flushrc = buffer_flush_available(vty->obuf, vty_sock);
+ flushrc = buffer_flush_available(vty->obuf, vty->wfd);
else if (vty->status == VTY_MORELINE)
- flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width,
+ flushrc = buffer_flush_window(vty->obuf, vty->wfd, vty->width,
1, erase, 0);
else
flushrc = buffer_flush_window(
- vty->obuf, vty_sock, vty->width,
+ vty->obuf, vty->wfd, vty->width,
vty->lines >= 0 ? vty->lines : vty->height, erase, 0);
switch (flushrc) {
case BUFFER_ERROR:
vty->monitor =
0; /* disable monitoring to avoid infinite recursion */
- zlog_info("buffer_flush failed on vty client fd %d, closing",
- vty->fd);
+ zlog_info("buffer_flush failed on vty client fd %d/%d, closing",
+ vty->fd, vty->wfd);
buffer_reset(vty->lbuf);
buffer_reset(vty->obuf);
vty_close(vty);
@@ -1568,14 +1561,14 @@ static int vty_flush(struct thread *thread)
else {
vty->status = VTY_NORMAL;
if (vty->lines == 0)
- vty_event(VTY_READ, vty_sock, vty);
+ vty_event(VTY_READ, vty);
}
break;
case BUFFER_PENDING:
/* There is more data waiting to be written. */
vty->status = VTY_MORE;
if (vty->lines == 0)
- vty_event(VTY_WRITE, vty_sock, vty);
+ vty_event(VTY_WRITE, vty);
break;
}
@@ -1678,8 +1671,8 @@ static struct vty *vty_create(int vty_sock, union sockunion *su)
vty_prompt(vty);
/* Add read/write thread. */
- vty_event(VTY_WRITE, vty_sock, vty);
- vty_event(VTY_READ, vty_sock, vty);
+ vty_event(VTY_WRITE, vty);
+ vty_event(VTY_READ, vty);
return vty;
}
@@ -1735,7 +1728,6 @@ void vty_stdio_resume(void)
termios = stdio_orig_termios;
termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR
| IGNCR | ICRNL | IXON);
- termios.c_oflag &= ~OPOST;
termios.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN);
termios.c_cflag &= ~(CSIZE | PARENB);
termios.c_cflag |= CS8;
@@ -1746,8 +1738,8 @@ void vty_stdio_resume(void)
vty_prompt(stdio_vty);
/* Add read/write thread. */
- vty_event(VTY_WRITE, 1, stdio_vty);
- vty_event(VTY_READ, 0, stdio_vty);
+ vty_event(VTY_WRITE, stdio_vty);
+ vty_event(VTY_READ, stdio_vty);
}
void vty_stdio_close(void)
@@ -1796,7 +1788,7 @@ static int vty_accept(struct thread *thread)
accept_sock = THREAD_FD(thread);
/* We continue hearing vty socket. */
- vty_event(VTY_SERV, accept_sock, NULL);
+ vty_event_serv(VTY_SERV, accept_sock);
memset(&su, 0, sizeof(union sockunion));
@@ -1826,7 +1818,7 @@ static int vty_accept(struct thread *thread)
close(vty_sock);
/* continue accepting connections */
- vty_event(VTY_SERV, accept_sock, NULL);
+ vty_event_serv(VTY_SERV, accept_sock);
return 0;
}
@@ -1842,7 +1834,7 @@ static int vty_accept(struct thread *thread)
close(vty_sock);
/* continue accepting connections */
- vty_event(VTY_SERV, accept_sock, NULL);
+ vty_event_serv(VTY_SERV, accept_sock);
return 0;
}
@@ -1915,7 +1907,7 @@ static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port)
continue;
}
- vty_event(VTY_SERV, sock, NULL);
+ vty_event_serv(VTY_SERV, sock);
} while ((ainfo = ainfo->ai_next) != NULL);
freeaddrinfo(ainfo_save);
@@ -1993,7 +1985,7 @@ static void vty_serv_un(const char *path)
}
}
- vty_event(VTYSH_SERV, sock, NULL);
+ vty_event_serv(VTYSH_SERV, sock);
}
/* #define VTYSH_DEBUG 1 */
@@ -2008,7 +2000,7 @@ static int vtysh_accept(struct thread *thread)
accept_sock = THREAD_FD(thread);
- vty_event(VTYSH_SERV, accept_sock, NULL);
+ vty_event_serv(VTYSH_SERV, accept_sock);
memset(&client, 0, sizeof(struct sockaddr_un));
client_len = sizeof(struct sockaddr_un);
@@ -2042,7 +2034,7 @@ static int vtysh_accept(struct thread *thread)
vty->type = VTY_SHELL_SERV;
vty->node = VIEW_NODE;
- vty_event(VTYSH_READ, sock, vty);
+ vty_event(VTYSH_READ, vty);
return 0;
}
@@ -2051,7 +2043,7 @@ static int vtysh_flush(struct vty *vty)
{
switch (buffer_flush_available(vty->obuf, vty->wfd)) {
case BUFFER_PENDING:
- vty_event(VTYSH_WRITE, vty->wfd, vty);
+ vty_event(VTYSH_WRITE, vty);
break;
case BUFFER_ERROR:
vty->monitor =
@@ -2084,7 +2076,7 @@ static int vtysh_read(struct thread *thread)
if ((nbytes = read(sock, buf, VTY_READ_BUFSIZ)) <= 0) {
if (nbytes < 0) {
if (ERRNO_IO_RETRY(errno)) {
- vty_event(VTYSH_READ, sock, vty);
+ vty_event(VTYSH_READ, vty);
return 0;
}
vty->monitor = 0; /* disable monitoring to avoid
@@ -2150,7 +2142,7 @@ static int vtysh_read(struct thread *thread)
if (vty->status == VTY_CLOSE)
vty_close(vty);
else
- vty_event(VTYSH_READ, sock, vty);
+ vty_event(VTYSH_READ, vty);
return 0;
}
@@ -2657,33 +2649,44 @@ int vty_config_node_exit(struct vty *vty)
/* Master of the threads. */
static struct thread_master *vty_master;
-static void vty_event(enum event event, int sock, struct vty *vty)
+static void vty_event_serv(enum event event, int sock)
{
struct thread *vty_serv_thread = NULL;
switch (event) {
case VTY_SERV:
- vty_serv_thread = thread_add_read(vty_master, vty_accept, vty,
- sock, NULL);
+ vty_serv_thread = thread_add_read(vty_master, vty_accept,
+ NULL, sock, NULL);
vector_set_index(Vvty_serv_thread, sock, vty_serv_thread);
break;
#ifdef VTYSH
case VTYSH_SERV:
- vty_serv_thread = thread_add_read(vty_master, vtysh_accept, vty,
- sock, NULL);
+ vty_serv_thread = thread_add_read(vty_master, vtysh_accept,
+ NULL, sock, NULL);
vector_set_index(Vvty_serv_thread, sock, vty_serv_thread);
break;
+#endif /* VTYSH */
+ default:
+ assert(!"vty_event_serv() called incorrectly");
+ }
+}
+
+static void vty_event(enum event event, struct vty *vty)
+{
+ switch (event) {
+#ifdef VTYSH
case VTYSH_READ:
- thread_add_read(vty_master, vtysh_read, vty, sock,
+ thread_add_read(vty_master, vtysh_read, vty, vty->fd,
&vty->t_read);
break;
case VTYSH_WRITE:
- thread_add_write(vty_master, vtysh_write, vty, sock,
+ thread_add_write(vty_master, vtysh_write, vty, vty->wfd,
&vty->t_write);
break;
#endif /* VTYSH */
case VTY_READ:
- thread_add_read(vty_master, vty_read, vty, sock, &vty->t_read);
+ thread_add_read(vty_master, vty_read, vty, vty->fd,
+ &vty->t_read);
/* Time out treatment. */
if (vty->v_timeout) {
@@ -2693,7 +2696,7 @@ static void vty_event(enum event event, int sock, struct vty *vty)
}
break;
case VTY_WRITE:
- thread_add_write(vty_master, vty_flush, vty, sock,
+ thread_add_write(vty_master, vty_flush, vty, vty->wfd,
&vty->t_write);
break;
case VTY_TIMEOUT_RESET:
@@ -2702,6 +2705,8 @@ static void vty_event(enum event event, int sock, struct vty *vty)
thread_add_timer(vty_master, vty_timeout, vty,
vty->v_timeout, &vty->t_timeout);
break;
+ default:
+ assert(!"vty_event() called incorrectly");
}
}
@@ -2748,7 +2753,7 @@ static int exec_timeout(struct vty *vty, const char *min_str,
vty_timeout_val = timeout;
vty->v_timeout = timeout;
- vty_event(VTY_TIMEOUT_RESET, 0, vty);
+ vty_event(VTY_TIMEOUT_RESET, vty);
return CMD_SUCCESS;
diff --git a/lib/wheel.c b/lib/wheel.c
index f5e5cc52c3..5bdd6292f9 100644
--- a/lib/wheel.c
+++ b/lib/wheel.c
@@ -73,8 +73,7 @@ static int wheel_timer_thread(struct thread *t)
wheel = THREAD_ARG(t);
- thread_execute_name(wheel->master, wheel_timer_thread_helper,
- wheel, 0, wheel->name);
+ thread_execute(wheel->master, wheel_timer_thread_helper, wheel, 0);
return 0;
}
diff --git a/lib/workqueue.c b/lib/workqueue.c
index f8e4677220..8eabdf52e7 100644
--- a/lib/workqueue.c
+++ b/lib/workqueue.c
@@ -377,11 +377,6 @@ stats:
if (yielded)
wq->yields++;
-#if 0
- printf ("%s: cycles %d, new: best %d, worst %d\n",
- __func__, cycles, wq->cycles.best, wq->cycles.granularity);
-#endif
-
/* Is the queue done yet? If it is, call the completion callback. */
if (!work_queue_empty(wq)) {
if (ret == WQ_RETRY_LATER ||
diff --git a/lib/xref.c b/lib/xref.c
new file mode 100644
index 0000000000..40efe51363
--- /dev/null
+++ b/lib/xref.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2017-20 David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <pthread.h>
+#include <signal.h>
+#include <inttypes.h>
+
+#include "xref.h"
+#include "vty.h"
+#include "jhash.h"
+#include "sha256.h"
+#include "memory.h"
+#include "hash.h"
+
+struct xref_block *xref_blocks;
+static struct xref_block **xref_block_last = &xref_blocks;
+
+static void base32(uint8_t **inpos, int *bitpos,
+ char *out, size_t n_chars)
+{
+ static const char base32ch[] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
+
+ char *opos = out;
+ uint8_t *in = *inpos;
+ int bp = *bitpos;
+
+ while (opos < out + n_chars) {
+ uint32_t bits = in[0] | (in[1] << 8);
+
+ if (bp == -1)
+ bits |= 0x10;
+ else
+ bits >>= bp;
+
+ *opos++ = base32ch[bits & 0x1f];
+
+ bp += 5;
+ if (bp >= 8)
+ in++, bp -= 8;
+ }
+ *opos = '\0';
+ *inpos = in;
+ *bitpos = bp;
+}
+
+static void xref_add_one(const struct xref *xref)
+{
+ SHA256_CTX sha;
+ struct xrefdata *xrefdata;
+
+ const char *filename, *p, *q;
+ uint8_t hash[32], *h = hash;
+ uint32_t be_val;
+ int bitpos;
+
+ if (!xref || !xref->xrefdata)
+ return;
+
+ xrefdata = xref->xrefdata;
+ xrefdata->xref = xref;
+
+ if (!xrefdata->hashstr)
+ return;
+
+ /* as far as the unique ID is concerned, only use the last
+ * directory name + filename, e.g. "bgpd/bgp_route.c". This
+ * gives a little leeway in moving things and avoids IDs being
+ * screwed up by out of tree builds or absolute pathnames.
+ */
+ filename = xref->file;
+ p = strrchr(filename, '/');
+ if (p) {
+ q = memrchr(filename, '/', p - filename);
+ if (q)
+ filename = q + 1;
+ else
+ filename = p + 1;
+ }
+
+ SHA256_Init(&sha);
+ SHA256_Update(&sha, filename, strlen(filename));
+ SHA256_Update(&sha, xrefdata->hashstr,
+ strlen(xrefdata->hashstr));
+ be_val = htonl(xrefdata->hashu32[0]);
+ SHA256_Update(&sha, &be_val, sizeof(be_val));
+ be_val = htonl(xrefdata->hashu32[1]);
+ SHA256_Update(&sha, &be_val, sizeof(be_val));
+ SHA256_Final(hash, &sha);
+
+ bitpos = -1;
+ base32(&h, &bitpos, &xrefdata->uid[0], 5);
+ xrefdata->uid[5] = '-';
+ base32(&h, &bitpos, &xrefdata->uid[6], 5);
+}
+
+void xref_gcc_workaround(const struct xref *xref)
+{
+ xref_add_one(xref);
+}
+
+void xref_block_add(struct xref_block *block)
+{
+ const struct xref * const *xrefp;
+
+ *xref_block_last = block;
+ xref_block_last = &block->next;
+
+ for (xrefp = block->start; xrefp < block->stop; xrefp++)
+ xref_add_one(*xrefp);
+}
diff --git a/lib/xref.h b/lib/xref.h
new file mode 100644
index 0000000000..b3243fa058
--- /dev/null
+++ b/lib/xref.h
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2017-20 David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _FRR_XREF_H
+#define _FRR_XREF_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <errno.h>
+#include "compiler.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum xref_type {
+ XREFT_NONE = 0,
+
+ XREFT_THREADSCHED = 0x100,
+
+ XREFT_LOGMSG = 0x200,
+
+ XREFT_DEFUN = 0x300,
+ XREFT_INSTALL_ELEMENT = 0x301,
+};
+
+/* struct xref is the "const" part; struct xrefdata is the writable part. */
+struct xref;
+struct xrefdata;
+
+struct xref {
+ /* this may be NULL, depending on the type of the xref.
+ * if it is NULL, the xref has no unique ID and cannot be accessed
+ * through that mechanism.
+ */
+ struct xrefdata *xrefdata;
+
+ /* type isn't generally needed at runtime */
+ enum xref_type type;
+
+ /* code location */
+ int line;
+ const char *file;
+ const char *func;
+
+ /* -- 32 bytes (on 64bit) -- */
+
+ /* type-specific bits appended by embedding this struct */
+};
+
+struct xrefdata {
+ /* pointer back to the const part; this will be initialized at
+ * program startup by xref_block_add(). (Creating structs with
+ * cyclic pointers to each other is not easily possible for
+ * function-scoped static variables.)
+ *
+ * There is no xrefdata w/o xref, but there are xref w/o xrefdata.
+ */
+ const struct xref *xref;
+
+ /* base32(crockford) of unique ID. not all bytes are used, but
+ * let's pad to 16 for simplicity
+ */
+ char uid[16];
+
+ /* hash/uid input
+ * if hashstr is NULL, no UID is assigned/calculated. Use macro
+ * string concatenation if multiple values need to be fed in.
+ * (This is here to not make the UID calculation independent of
+ * xref type.)
+ */
+ const char *hashstr;
+ uint32_t hashu32[2];
+
+ /* -- 32 bytes (on 64bit) -- */
+};
+
+/* linker "magic" is used to create an array of pointers to struct xref.
+ * the result is a contiguous block of pointers, each pointing to an xref
+ * somewhere in the code. The linker gives us start and end pointers, we
+ * stuff those into the struct below and hook up a constructor to run at
+ * program startup with the struct passed.
+ *
+ * Placing the xrefs themselves into an array doesn't work because they'd
+ * need to be constant size, but we're embedding struct xref into other
+ * container structs with extra data. Also this means that external code
+ * (like the python xref dumper) can safely ignore extra data at the end of
+ * xrefs without needing to account for size in iterating the array.
+ *
+ * If you're curious, this is also how __attribute__((constructor)) (and
+ * destructor) are implemented - there are 2 arrays, ".init_array" and
+ * ".fini_array", containing function pointers. The magic turns out to be
+ * quite mundane, actually ;)
+ *
+ * The slightly tricky bit is that this is a per-object (i.e. per shared
+ * library & daemon) thing and we need a bit of help (in XREF_SETUP) to
+ * initialize correctly.
+ */
+
+struct xref_block {
+ struct xref_block *next;
+ const struct xref * const *start;
+ const struct xref * const *stop;
+};
+
+extern struct xref_block *xref_blocks;
+extern void xref_block_add(struct xref_block *block);
+extern void xref_gcc_workaround(const struct xref *xref);
+
+#ifndef HAVE_SECTION_SYMS
+/* we have a build system patch to use GNU ld on Solaris; if that doesn't
+ * work we end up on Solaris ld which doesn't support the section start/end
+ * symbols.
+ */
+#define XREF_SETUP() \
+ CPP_NOTICE("Missing linker support for section arrays. Solaris ld?")
+#else
+/* the actual symbols that the linker provides for us. Note these are
+ * _symbols_ referring to the actual section start/end, i.e. they are very
+ * much NOT _pointers_, rather the symbol *value* is the pointer. Declaring
+ * them as size-1 arrays is the "best" / "right" thing.
+ */
+extern const struct xref * const __start_xref_array[1] DSO_LOCAL;
+extern const struct xref * const __stop_xref_array[1] DSO_LOCAL;
+
+/* this macro is invoked once for each standalone DSO through
+ * FRR_MODULE_SETUP \
+ * }-> FRR_COREMOD_SETUP -> XREF_SETUP
+ * FRR_DAEMON_INFO /
+ */
+#define XREF_SETUP() \
+ static const struct xref _dummy_xref = { \
+ /* .xrefdata = */ NULL, \
+ /* .type = */ XREFT_NONE, \
+ /* .line = */ __LINE__, \
+ /* .file = */ __FILE__, \
+ /* .func = */ "dummy", \
+ }; \
+ static const struct xref * const _dummy_xref_p \
+ __attribute__((used, section("xref_array"))) \
+ = &_dummy_xref; \
+ static void __attribute__((used, _CONSTRUCTOR(1100))) \
+ _xref_init(void) { \
+ static struct xref_block _xref_block = { \
+ .start = __start_xref_array, \
+ .stop = __stop_xref_array, \
+ }; \
+ xref_block_add(&_xref_block); \
+ } \
+ asm(XREF_NOTE); \
+ /* end */
+
+/* the following blurb emits an ELF note indicating start and end of the xref
+ * array in the binary. This is technically the "correct" entry point for
+ * external tools reading xrefs out of an ELF shared library or executable.
+ *
+ * right now, the extraction tools use the section header for "xref_array"
+ * instead; however, section headers are technically not necessarily preserved
+ * for fully linked libraries or executables. (In practice they are only
+ * stripped by obfuscation tools.)
+ *
+ * conversely, for reading xrefs out of a single relocatable object file (e.g.
+ * bar.o), section headers are the right thing to look at since the note is
+ * only emitted for the final binary once.
+ *
+ * FRR itself does not need this note to operate correctly, so if you have
+ * some build issue with it just add -DFRR_XREF_NO_NOTE to your build flags
+ * to disable it.
+ */
+#ifdef FRR_XREF_NO_NOTE
+#define XREF_NOTE ""
+#else
+
+#if __SIZEOF_POINTER__ == 4
+#define _NOTE_2PTRSIZE "8"
+#define _NOTE_PTR ".long"
+#elif __SIZEOF_POINTER__ == 8
+#define _NOTE_2PTRSIZE "16"
+#define _NOTE_PTR ".quad"
+#else
+#error unsupported pointer size
+#endif
+
+#ifdef __arm__
+# define asmspecial "%"
+#else
+# define asmspecial "@"
+#endif
+
+#define XREF_NOTE \
+ "" "\n"\
+ " .type _frr_xref_note," asmspecial "object" "\n"\
+ " .pushsection .note.FRR,\"a\"," asmspecial "note" "\n"\
+ " .p2align 2" "\n"\
+ "_frr_xref_note:" "\n"\
+ " .long 9" "\n"\
+ " .long " _NOTE_2PTRSIZE "\n"\
+ " .ascii \"XREF\"" "\n"\
+ " .ascii \"FRRouting\\0\\0\\0\"" "\n"\
+ " " _NOTE_PTR " __start_xref_array-." "\n"\
+ " " _NOTE_PTR " __stop_xref_array-." "\n"\
+ " .size _frr_xref_note, .-_frr_xref_note" "\n"\
+ " .popsection" "\n"\
+ "" "\n"\
+ /* end */
+#endif
+
+#endif /* HAVE_SECTION_SYMS */
+
+/* emit the array entry / pointer to xref */
+#if defined(__clang__) || !defined(__cplusplus)
+#define XREF_LINK(dst) \
+ static const struct xref * const NAMECTR(xref_p_) \
+ __attribute__((used, section("xref_array"))) \
+ = &(dst) \
+ /* end */
+
+#else /* GCC && C++ */
+/* workaround for GCC bug 41091 (dated 2009), added in 2021...
+ *
+ * this breaks extraction of xrefs with xrelfo.py (because the xref_array
+ * entry will be missing), but provides full runtime functionality. To get
+ * the proper list of xrefs from C++ code, build with clang...
+ */
+struct _xref_p {
+ const struct xref * const ptr;
+
+ _xref_p(const struct xref *_ptr) : ptr(_ptr)
+ {
+ xref_gcc_workaround(_ptr);
+ }
+};
+
+#define XREF_LINK(dst) \
+ static const struct _xref_p __attribute__((used)) \
+ NAMECTR(xref_p_)(&(dst)) \
+ /* end */
+#endif
+
+/* initializer for a "struct xref" */
+#define XREF_INIT(type_, xrefdata_, func_) \
+ { \
+ /* .xrefdata = */ (xrefdata_), \
+ /* .type = */ (type_), \
+ /* .line = */ __LINE__, \
+ /* .file = */ __FILE__, \
+ /* .func = */ func_, \
+ } \
+ /* end */
+
+/* use with XREF_INIT when outside of a function, i.e. no __func__ */
+#define XREF_NO_FUNC "<global>"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FRR_XREF_H */
diff --git a/lib/yang.c b/lib/yang.c
index a3e2a395d7..383dc9f5eb 100644
--- a/lib/yang.c
+++ b/lib/yang.c
@@ -92,6 +92,7 @@ static const char *const frr_native_modules[] = {
"frr-isisd",
"frr-vrrpd",
"frr-zebra",
+ "frr-pathd",
};
/* clang-format on */
diff --git a/lib/zclient.c b/lib/zclient.c
index cb4555650d..20c285cf7f 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -40,6 +40,7 @@
#include "nexthop_group.h"
#include "lib_errors.h"
#include "srte.h"
+#include "printfrr.h"
DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient")
DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs")
@@ -995,17 +996,24 @@ done:
return ret;
}
-int zapi_nhg_encode(struct stream *s, int cmd, struct zapi_nhg *api_nhg)
+static int zapi_nhg_encode(struct stream *s, int cmd, struct zapi_nhg *api_nhg)
{
int i;
if (cmd != ZEBRA_NHG_DEL && cmd != ZEBRA_NHG_ADD) {
flog_err(EC_LIB_ZAPI_ENCODE,
- "%s: Specified zapi NHG command (%d) doesn't exist\n",
+ "%s: Specified zapi NHG command (%d) doesn't exist",
__func__, cmd);
return -1;
}
+ if (api_nhg->nexthop_num >= MULTIPATH_NUM ||
+ api_nhg->backup_nexthop_num >= MULTIPATH_NUM) {
+ flog_err(EC_LIB_ZAPI_ENCODE,
+ "%s: zapi NHG encode with invalid input", __func__);
+ return -1;
+ }
+
stream_reset(s);
zclient_create_header(s, cmd, VRF_DEFAULT);
@@ -1023,7 +1031,6 @@ int zapi_nhg_encode(struct stream *s, int cmd, struct zapi_nhg *api_nhg)
zapi_nexthop_encode(s, &api_nhg->nexthops[i], 0, 0);
/* Backup nexthops */
-
stream_putw(s, api_nhg->backup_nexthop_num);
for (i = 0; i < api_nhg->backup_nexthop_num; i++)
@@ -1058,7 +1065,7 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
if (api->type >= ZEBRA_ROUTE_MAX) {
flog_err(EC_LIB_ZAPI_ENCODE,
- "%s: Specified route type (%u) is not a legal value\n",
+ "%s: Specified route type (%u) is not a legal value",
__func__, api->type);
return -1;
}
@@ -1070,7 +1077,7 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
if (api->safi < SAFI_UNICAST || api->safi >= SAFI_MAX) {
flog_err(EC_LIB_ZAPI_ENCODE,
- "%s: Specified route SAFI (%u) is not a legal value\n",
+ "%s: Specified route SAFI (%u) is not a legal value",
__func__, api->safi);
return -1;
}
@@ -1285,7 +1292,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
STREAM_GETC(s, api->type);
if (api->type >= ZEBRA_ROUTE_MAX) {
flog_err(EC_LIB_ZAPI_ENCODE,
- "%s: Specified route type: %d is not a legal value\n",
+ "%s: Specified route type: %d is not a legal value",
__func__, api->type);
return -1;
}
@@ -1296,7 +1303,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
STREAM_GETC(s, api->safi);
if (api->safi < SAFI_UNICAST || api->safi >= SAFI_MAX) {
flog_err(EC_LIB_ZAPI_ENCODE,
- "%s: Specified route SAFI (%u) is not a legal value\n",
+ "%s: Specified route SAFI (%u) is not a legal value",
__func__, api->safi);
return -1;
}
@@ -3297,7 +3304,7 @@ static void zclient_capability_decode(ZAPI_CALLBACK_ARGS)
if (vrf_backend < 0 || vrf_configure_backend(vrf_backend)) {
flog_err(EC_LIB_ZAPI_ENCODE,
- "%s: Garbage VRF backend type: %d\n", __func__,
+ "%s: Garbage VRF backend type: %d", __func__,
vrf_backend);
goto stream_failure;
}
@@ -4121,3 +4128,51 @@ uint32_t zclient_get_nhg_start(uint32_t proto)
return ZEBRA_NHG_PROTO_SPACING * proto;
}
+
+char *zclient_dump_route_flags(uint32_t flags, char *buf, size_t len)
+{
+ if (flags == 0) {
+ snprintfrr(buf, len, "None ");
+ return buf;
+ }
+
+ snprintfrr(
+ buf, len, "%s%s%s%s%s%s%s%s%s%s",
+ CHECK_FLAG(flags, ZEBRA_FLAG_ALLOW_RECURSION) ? "Recursion "
+ : "",
+ CHECK_FLAG(flags, ZEBRA_FLAG_SELFROUTE) ? "Self " : "",
+ CHECK_FLAG(flags, ZEBRA_FLAG_IBGP) ? "iBGP " : "",
+ CHECK_FLAG(flags, ZEBRA_FLAG_SELECTED) ? "Selected " : "",
+ CHECK_FLAG(flags, ZEBRA_FLAG_FIB_OVERRIDE) ? "Override " : "",
+ CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE) ? "Evpn " : "",
+ CHECK_FLAG(flags, ZEBRA_FLAG_RR_USE_DISTANCE) ? "RR Distance "
+ : "",
+ CHECK_FLAG(flags, ZEBRA_FLAG_TRAPPED) ? "Trapped " : "",
+ CHECK_FLAG(flags, ZEBRA_FLAG_OFFLOADED) ? "Offloaded " : "",
+ CHECK_FLAG(flags, ZEBRA_FLAG_OFFLOAD_FAILED) ? "Offload Failed "
+ : "");
+ return buf;
+}
+
+char *zclient_evpn_dump_macip_flags(uint8_t flags, char *buf, size_t len)
+{
+ if (flags == 0) {
+ snprintfrr(buf, len, "None ");
+ return buf;
+ }
+
+ snprintfrr(
+ buf, len, "%s%s%s%s%s%s%s",
+ CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY) ? "Sticky MAC " : "",
+ CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW) ? "Gateway MAC " : "",
+ CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG) ? "Router "
+ : "",
+ CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_OVERRIDE_FLAG) ? "Override "
+ : "",
+ CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_SVI_IP) ? "SVI MAC " : "",
+ CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT) ? "Proxy "
+ : "",
+ CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_SYNC_PATH) ? "Sync " : "");
+
+ return buf;
+}
diff --git a/lib/zclient.h b/lib/zclient.h
index 2af448a20c..cf52ea91a0 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -479,6 +479,7 @@ struct zapi_route {
uint8_t type;
unsigned short instance;
+ /* If you add flags, update zclient_dump_route_flags */
uint32_t flags;
/*
* Cause Zebra to consider this routes nexthops recursively
@@ -580,6 +581,8 @@ struct zapi_route {
} opaque;
};
+extern char *zclient_dump_route_flags(uint32_t flags, char *buf, size_t len);
+
struct zapi_labels {
uint8_t message;
#define ZAPI_LABELS_FTN 0x01
@@ -634,6 +637,52 @@ struct zapi_pw_status {
uint32_t status;
};
+/* IGP instance data associated to a RLFA. */
+struct zapi_rlfa_igp {
+ vrf_id_t vrf_id;
+ int protocol;
+ union {
+ struct {
+ char area_tag[32];
+ struct {
+ int tree_id;
+ int level;
+ unsigned int run_id;
+ } spf;
+ } isis;
+ };
+};
+
+/* IGP -> LDP RLFA (un)registration message. */
+struct zapi_rlfa_request {
+ /* IGP instance data. */
+ struct zapi_rlfa_igp igp;
+
+ /* Destination prefix. */
+ struct prefix destination;
+
+ /* PQ node address. */
+ struct in_addr pq_address;
+};
+
+/* LDP -> IGP RLFA label update. */
+struct zapi_rlfa_response {
+ /* IGP instance data. */
+ struct zapi_rlfa_igp igp;
+
+ /* Destination prefix. */
+ struct prefix destination;
+
+ /* Resolved LDP labels. */
+ mpls_label_t pq_label;
+ uint16_t nexthop_num;
+ struct {
+ int family;
+ union g_addr gate;
+ mpls_label_t label;
+ } nexthops[MULTIPATH_NUM];
+};
+
enum zapi_route_notify_owner {
ZAPI_ROUTE_FAIL_INSTALL,
ZAPI_ROUTE_BETTER_ADMIN_WON,
@@ -722,8 +771,11 @@ zapi_rule_notify_owner2str(enum zapi_rule_notify_owner note)
#define ZEBRA_MACIP_TYPE_PROXY_ADVERT 0x20 /* Not locally active */
#define ZEBRA_MACIP_TYPE_SYNC_PATH 0x40 /* sync path */
/* XXX - flags is an u8; that needs to be changed to u32 if you need
- * to allocate past 0x80
+ * to allocate past 0x80. Additionally touch zclient_evpn_dump_macip_flags
*/
+#define MACIP_BUF_SIZE 128
+extern char *zclient_evpn_dump_macip_flags(uint8_t flags, char *buf,
+ size_t len);
/* Zebra ES VTEP flags (ZEBRA_REMOTE_ES_VTEP_ADD) */
/* ESR has been rxed from the VTEP. Only VTEPs that have advertised the
@@ -969,9 +1021,7 @@ bool zapi_ipset_notify_decode(struct stream *s,
uint32_t *unique,
enum zapi_ipset_notify_owner *note);
-
-extern int zapi_nhg_encode(struct stream *s, int cmd, struct zapi_nhg *api_nhg);
-extern int zapi_nhg_decode(struct stream *s, int cmd, struct zapi_nhg *api_nhg);
+/* Nexthop-group message apis */
extern enum zclient_send_status
zclient_nhg_send(struct zclient *zclient, int cmd, struct zapi_nhg *api_nhg);
@@ -1091,6 +1141,12 @@ enum zapi_opaque_registry {
LDP_IGP_SYNC_IF_STATE_UPDATE = 4,
/* Announce that LDP is up */
LDP_IGP_SYNC_ANNOUNCE_UPDATE = 5,
+ /* Register RLFA with LDP */
+ LDP_RLFA_REGISTER = 7,
+ /* Unregister all RLFAs with LDP */
+ LDP_RLFA_UNREGISTER_ALL = 8,
+ /* Announce LDP labels associated to a previously registered RLFA */
+ LDP_RLFA_LABELS = 9,
};
/* Send the hello message.
diff --git a/lib/zlog.c b/lib/zlog.c
index e77feec5f2..51509e24f4 100644
--- a/lib/zlog.c
+++ b/lib/zlog.c
@@ -94,6 +94,7 @@ struct zlog_msg {
const char *fmt;
va_list args;
+ const struct xref_logmsg *xref;
char *stackbuf;
size_t stackbufsz;
@@ -349,12 +350,14 @@ void zlog_tls_buffer_flush(void)
}
-static void vzlog_notls(int prio, const char *fmt, va_list ap)
+static void vzlog_notls(const struct xref_logmsg *xref, int prio,
+ const char *fmt, va_list ap)
{
struct zlog_target *zt;
struct zlog_msg stackmsg = {
.prio = prio & LOG_PRIMASK,
.fmt = fmt,
+ .xref = xref,
}, *msg = &stackmsg;
char stackbuf[512];
@@ -379,8 +382,8 @@ static void vzlog_notls(int prio, const char *fmt, va_list ap)
XFREE(MTYPE_LOG_MESSAGE, msg->text);
}
-static void vzlog_tls(struct zlog_tls *zlog_tls, int prio,
- const char *fmt, va_list ap)
+static void vzlog_tls(struct zlog_tls *zlog_tls, const struct xref_logmsg *xref,
+ int prio, const char *fmt, va_list ap)
{
struct zlog_target *zt;
struct zlog_msg *msg;
@@ -413,6 +416,7 @@ static void vzlog_tls(struct zlog_tls *zlog_tls, int prio,
msg->stackbufsz = TLS_LOG_BUF_SIZE - zlog_tls->bufpos - 1;
msg->fmt = fmt;
msg->prio = prio & LOG_PRIMASK;
+ msg->xref = xref;
if (msg->prio < LOG_INFO)
immediate = true;
@@ -447,7 +451,8 @@ static void vzlog_tls(struct zlog_tls *zlog_tls, int prio,
XFREE(MTYPE_LOG_MESSAGE, msg->text);
}
-void vzlog(int prio, const char *fmt, va_list ap)
+void vzlogx(const struct xref_logmsg *xref, int prio,
+ const char *fmt, va_list ap)
{
struct zlog_tls *zlog_tls = zlog_tls_get();
@@ -480,9 +485,9 @@ void vzlog(int prio, const char *fmt, va_list ap)
#endif
if (zlog_tls)
- vzlog_tls(zlog_tls, prio, fmt, ap);
+ vzlog_tls(zlog_tls, xref, prio, fmt, ap);
else
- vzlog_notls(prio, fmt, ap);
+ vzlog_notls(xref, prio, fmt, ap);
}
void zlog_sigsafe(const char *text, size_t len)
@@ -516,6 +521,11 @@ int zlog_msg_prio(struct zlog_msg *msg)
return msg->prio;
}
+const struct xref_logmsg *zlog_msg_xref(struct zlog_msg *msg)
+{
+ return msg->xref;
+}
+
const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen)
{
if (!msg->text) {
diff --git a/lib/zlog.h b/lib/zlog.h
index 1c5013746b..bdf59fa68e 100644
--- a/lib/zlog.h
+++ b/lib/zlog.h
@@ -38,6 +38,20 @@ extern char zlog_prefix[];
extern size_t zlog_prefixsz;
extern int zlog_tmpdirfd;
+struct xref_logmsg {
+ struct xref xref;
+
+ const char *fmtstring;
+ uint32_t priority;
+ uint32_t ec;
+};
+
+struct xrefdata_logmsg {
+ struct xrefdata xrefdata;
+
+ /* nothing more here right now */
+};
+
/* These functions are set up to write to stdout/stderr without explicit
* initialization and/or before config load. There is no need to call e.g.
* fprintf(stderr, ...) just because it's "too early" at startup. Depending
@@ -45,7 +59,9 @@ extern int zlog_tmpdirfd;
* determine wether something is a log message or something else.
*/
-extern void vzlog(int prio, const char *fmt, va_list ap);
+extern void vzlogx(const struct xref_logmsg *xref, int prio,
+ const char *fmt, va_list ap);
+#define vzlog(prio, ...) vzlogx(NULL, prio, __VA_ARGS__)
PRINTFRR(2, 3)
static inline void zlog(int prio, const char *fmt, ...)
@@ -57,11 +73,61 @@ static inline void zlog(int prio, const char *fmt, ...)
va_end(ap);
}
-#define zlog_err(...) zlog(LOG_ERR, __VA_ARGS__)
-#define zlog_warn(...) zlog(LOG_WARNING, __VA_ARGS__)
-#define zlog_info(...) zlog(LOG_INFO, __VA_ARGS__)
-#define zlog_notice(...) zlog(LOG_NOTICE, __VA_ARGS__)
-#define zlog_debug(...) zlog(LOG_DEBUG, __VA_ARGS__)
+PRINTFRR(2, 3)
+static inline void zlog_ref(const struct xref_logmsg *xref,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vzlogx(xref, xref->priority, fmt, ap);
+ va_end(ap);
+}
+
+#define _zlog_ref(prio, msg, ...) do { \
+ static struct xrefdata _xrefdata = { \
+ .hashstr = (msg), \
+ .hashu32 = { (prio), 0 }, \
+ }; \
+ static const struct xref_logmsg _xref __attribute__((used)) = {\
+ .xref = XREF_INIT(XREFT_LOGMSG, &_xrefdata, __func__), \
+ .fmtstring = (msg), \
+ .priority = (prio), \
+ }; \
+ XREF_LINK(_xref.xref); \
+ zlog_ref(&_xref, (msg), ## __VA_ARGS__); \
+ } while (0)
+
+#define zlog_err(...) _zlog_ref(LOG_ERR, __VA_ARGS__)
+#define zlog_warn(...) _zlog_ref(LOG_WARNING, __VA_ARGS__)
+#define zlog_info(...) _zlog_ref(LOG_INFO, __VA_ARGS__)
+#define zlog_notice(...) _zlog_ref(LOG_NOTICE, __VA_ARGS__)
+#define zlog_debug(...) _zlog_ref(LOG_DEBUG, __VA_ARGS__)
+
+#define _zlog_ecref(ec_, prio, msg, ...) do { \
+ static struct xrefdata _xrefdata = { \
+ .hashstr = (msg), \
+ .hashu32 = { (prio), (ec_) }, \
+ }; \
+ static const struct xref_logmsg _xref __attribute__((used)) = {\
+ .xref = XREF_INIT(XREFT_LOGMSG, &_xrefdata, __func__), \
+ .fmtstring = (msg), \
+ .priority = (prio), \
+ .ec = (ec_), \
+ }; \
+ XREF_LINK(_xref.xref); \
+ zlog_ref(&_xref, "[EC %u] " msg, ec_, ## __VA_ARGS__); \
+ } while (0)
+
+#define flog_err(ferr_id, format, ...) \
+ _zlog_ecref(ferr_id, LOG_ERR, format, ## __VA_ARGS__)
+#define flog_warn(ferr_id, format, ...) \
+ _zlog_ecref(ferr_id, LOG_WARNING, format, ## __VA_ARGS__)
+
+#define flog_err_sys(ferr_id, format, ...) \
+ flog_err(ferr_id, format, ##__VA_ARGS__)
+#define flog(priority, ferr_id, format, ...) \
+ zlog(priority, "[EC %u] " format, ferr_id, ##__VA_ARGS__)
extern void zlog_sigsafe(const char *text, size_t len);
@@ -83,6 +149,7 @@ extern void zlog_sigsafe(const char *text, size_t len);
struct zlog_msg;
extern int zlog_msg_prio(struct zlog_msg *msg);
+extern const struct xref_logmsg *zlog_msg_xref(struct zlog_msg *msg);
/* pass NULL as textlen if you don't need it. */
extern const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen);