summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/agentx.c12
-rw-r--r--lib/bfd.c6
-rw-r--r--lib/bfd.h2
-rw-r--r--lib/command.c4
-rw-r--r--lib/command.h24
-rw-r--r--lib/command_graph.c3
-rw-r--r--lib/command_graph.h3
-rw-r--r--lib/command_py.c253
-rw-r--r--lib/darr.h37
-rw-r--r--lib/debug.c63
-rw-r--r--lib/debug.h49
-rw-r--r--lib/distribute.c35
-rw-r--r--lib/event.c55
-rw-r--r--lib/flex_algo.c21
-rw-r--r--lib/flex_algo.h3
-rw-r--r--lib/frrcu.h2
-rw-r--r--lib/frrlua.c85
-rw-r--r--lib/frrlua.h3
-rw-r--r--lib/hash.c2
-rw-r--r--lib/hash.h12
-rw-r--r--lib/ipaddr.h13
-rw-r--r--lib/libfrr.c93
-rw-r--r--lib/libfrr.h35
-rw-r--r--lib/libospf.h2
-rw-r--r--lib/link_state.c36
-rw-r--r--lib/link_state.h6
-rw-r--r--lib/linklist.h12
-rw-r--r--lib/log.c6
-rw-r--r--lib/memory.c1
-rw-r--r--lib/memory.h1
-rw-r--r--lib/mgmt_be_client.c32
-rw-r--r--lib/mgmt_be_client.h5
-rw-r--r--lib/mgmt_fe_client.c30
-rw-r--r--lib/mgmt_fe_client.h5
-rw-r--r--lib/mgmt_msg_native.c45
-rw-r--r--lib/mgmt_msg_native.h105
-rw-r--r--lib/module.c12
-rw-r--r--lib/module.h1
-rw-r--r--lib/nexthop.c39
-rw-r--r--lib/nexthop.h2
-rw-r--r--lib/northbound.h2
-rw-r--r--lib/northbound_cli.c106
-rw-r--r--lib/northbound_oper.c38
-rw-r--r--lib/northbound_sysrepo.c26
-rw-r--r--lib/openbsd-queue.h16
-rw-r--r--lib/openbsd-tree.h15
-rw-r--r--lib/prefix.c15
-rw-r--r--lib/prefix.h10
-rw-r--r--lib/routemap_cli.c2
-rw-r--r--lib/seqlock.c33
-rw-r--r--lib/smux.h1
-rw-r--r--lib/sockopt.h10
-rw-r--r--lib/srv6.c225
-rw-r--r--lib/srv6.h135
-rw-r--r--lib/stream.c2
-rw-r--r--lib/stream.h2
-rw-r--r--lib/subdir.am25
-rw-r--r--lib/termtable.c54
-rw-r--r--lib/termtable.h17
-rw-r--r--lib/vector.c51
-rw-r--r--lib/vector.h22
-rw-r--r--lib/vty.c62
-rw-r--r--lib/vty.h9
-rw-r--r--lib/yang.c13
-rw-r--r--lib/zclient.c196
-rw-r--r--lib/zclient.h51
66 files changed, 1809 insertions, 484 deletions
diff --git a/lib/agentx.c b/lib/agentx.c
index 19f2a6b7fc..2a3ff2355e 100644
--- a/lib/agentx.c
+++ b/lib/agentx.c
@@ -377,4 +377,16 @@ void smux_events_update(void)
agentx_events_update();
}
+static void smux_events_delete_thread(void *arg)
+{
+ XFREE(MTYPE_TMP, arg);
+}
+
+void smux_terminate(void)
+{
+ if (events) {
+ events->del = smux_events_delete_thread;
+ list_delete(&events);
+ }
+}
#endif /* SNMP_AGENTX */
diff --git a/lib/bfd.c b/lib/bfd.c
index 2222bb9547..4535fc1233 100644
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -1334,3 +1334,9 @@ int bfd_nht_update(const struct prefix *match, const struct zapi_route *route)
return 0;
}
+
+bool bfd_session_is_down(const struct bfd_session_params *session)
+{
+ return session->bss.state == BSS_DOWN ||
+ session->bss.state == BSS_ADMIN_DOWN;
+}
diff --git a/lib/bfd.h b/lib/bfd.h
index bfa5287340..48929a9564 100644
--- a/lib/bfd.h
+++ b/lib/bfd.h
@@ -464,6 +464,8 @@ extern bool bfd_protocol_integration_shutting_down(void);
extern int bfd_nht_update(const struct prefix *match,
const struct zapi_route *route);
+extern bool bfd_session_is_down(const struct bfd_session_params *session);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/command.c b/lib/command.c
index 51f2529e3e..ac8d60118e 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -17,6 +17,7 @@
#include <lib/version.h>
#include "command.h"
+#include "debug.h"
#include "frrstr.h"
#include "memory.h"
#include "log.h"
@@ -2463,8 +2464,7 @@ const char *host_config_get(void)
void cmd_show_lib_debugs(struct vty *vty)
{
route_map_show_debug(vty);
- mgmt_debug_be_client_show_debug(vty);
- mgmt_debug_fe_client_show_debug(vty);
+ debug_status_write(vty);
}
void install_default(enum node_type node)
diff --git a/lib/command.h b/lib/command.h
index e4c575e8d7..61d09973fd 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -84,14 +84,12 @@ enum node_type {
CONFIG_NODE, /* Config node. Default mode of config file. */
PREFIX_NODE, /* ip prefix-list node. */
PREFIX_IPV6_NODE, /* ipv6 prefix-list node. */
+ LIB_DEBUG_NODE, /* frrlib debug node. */
DEBUG_NODE, /* Debug node. */
VRF_DEBUG_NODE, /* Vrf Debug node. */
- NORTHBOUND_DEBUG_NODE, /* Northbound Debug node. */
DEBUG_VNC_NODE, /* Debug VNC node. */
RMAP_DEBUG_NODE, /* Route-map debug node */
RESOLVER_DEBUG_NODE, /* Resolver debug node */
- MGMT_BE_DEBUG_NODE, /* mgmtd backend-client debug node */
- MGMT_FE_DEBUG_NODE, /* mgmtd frontend-client debug node */
AAA_NODE, /* AAA node. */
EXTLOG_NODE, /* RFC5424 & co. extended syslog */
KEYCHAIN_NODE, /* Key-chain node. */
@@ -161,6 +159,9 @@ enum node_type {
SRV6_LOCS_NODE, /* SRv6 locators node */
SRV6_LOC_NODE, /* SRv6 locator node */
SRV6_ENCAP_NODE, /* SRv6 encapsulation node */
+ SRV6_SID_FORMATS_NODE, /* SRv6 SID formats config node */
+ SRV6_SID_FORMAT_USID_F3216_NODE, /* SRv6 uSID f3216 format config node */
+ SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NODE, /* SRv6 uncompressed f4024 format config node */
VTY_NODE, /* Vty node. */
FPM_NODE, /* Dataplane FPM node. */
LINK_PARAMS_NODE, /* Link-parameters node */
@@ -179,6 +180,8 @@ enum node_type {
ISIS_SRV6_NODE_MSD_NODE, /* ISIS SRv6 Node MSDs node */
MGMTD_NODE, /* MGMTD node. */
RPKI_VRF_NODE, /* RPKI node for VRF */
+ PIM_NODE, /* PIM protocol mode */
+ PIM6_NODE, /* PIM protocol for IPv6 mode */
NODE_TYPE_MAX, /* maximum */
};
/* clang-format on */
@@ -247,9 +250,11 @@ struct cmd_node {
/* Argc max counts. */
#define CMD_ARGC_MAX 256
+/* clang-format off */
+
/* helper defines for end-user DEFUN* macros */
#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
- static const struct cmd_element cmdname = { \
+ const struct cmd_element cmdname = { \
.string = cmdstr, \
.func = funcname, \
.doc = helpstr, \
@@ -276,7 +281,7 @@ struct cmd_node {
/* DEFPY variants */
#define DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \
+ static DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \
funcdecl_##funcname
#define DEFPY(funcname, cmdname, cmdstr, helpstr) \
@@ -303,7 +308,7 @@ struct cmd_node {
#define DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
DEFUN_CMD_FUNC_DECL(funcname) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \
+ static DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \
DEFUN_CMD_FUNC_TEXT(funcname)
#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
@@ -340,7 +345,8 @@ struct cmd_node {
/* DEFUN + DEFSH */
#define DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, attr) \
DEFUN_CMD_FUNC_DECL(funcname) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, daemon) \
+ static DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, \
+ daemon) \
DEFUN_CMD_FUNC_TEXT(funcname)
#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \
@@ -352,7 +358,7 @@ struct cmd_node {
/* ALIAS macro which define existing command's alias. */
#define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0)
+ static DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0)
#define ALIAS(funcname, cmdname, cmdstr, helpstr) \
ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, 0)
@@ -371,6 +377,8 @@ struct cmd_node {
#define ALIAS_YANG(funcname, cmdname, cmdstr, helpstr) \
ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG)
+/* clang-format on */
+
/* Some macroes */
/*
diff --git a/lib/command_graph.c b/lib/command_graph.c
index ff3c11db69..20ab6b321b 100644
--- a/lib/command_graph.c
+++ b/lib/command_graph.c
@@ -267,6 +267,9 @@ static bool cmd_nodes_equal(struct graph_node *ga, struct graph_node *gb)
case NEG_ONLY_TKN:
case WORD_TKN:
case ASNUM_TKN:
+#ifdef BUILDING_CLIPPY
+ case CMD_ELEMENT_TKN:
+#endif
return true;
}
diff --git a/lib/command_graph.h b/lib/command_graph.h
index 25aa47db7b..313c97fe87 100644
--- a/lib/command_graph.h
+++ b/lib/command_graph.h
@@ -54,6 +54,9 @@ enum cmd_token_type {
END_TKN, // last token in line
NEG_ONLY_TKN, // filter token, match if "no ..." command
+#ifdef BUILDING_CLIPPY
+ CMD_ELEMENT_TKN, // python bindings only
+#endif
SPECIAL_TKN = FORK_TKN,
};
/* clang-format on */
diff --git a/lib/command_py.c b/lib/command_py.c
index f8abcf8ef4..a77adcd7dc 100644
--- a/lib/command_py.c
+++ b/lib/command_py.c
@@ -29,6 +29,7 @@
struct wrap_graph;
static PyObject *graph_to_pyobj(struct wrap_graph *graph,
struct graph_node *gn);
+static PyObject *graph_to_pyobj_idx(struct wrap_graph *wgraph, size_t i);
/*
* nodes are wrapped as follows:
@@ -44,13 +45,6 @@ struct wrap_graph_node {
bool allowrepeat;
const char *type;
- bool deprecated;
- bool hidden;
- const char *text;
- const char *desc;
- const char *varname;
- long long min, max;
-
struct graph_node *node;
struct wrap_graph *wgraph;
size_t idx;
@@ -68,6 +62,7 @@ struct wrap_graph {
char *definition;
struct graph *graph;
+ size_t n_nodewrappers;
struct wrap_graph_node **nodewrappers;
};
@@ -84,11 +79,75 @@ static PyObject *refuse_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
READONLY, (char *)#name " (" #type ")" \
}
static PyMemberDef members_graph_node[] = {
- member(allowrepeat, T_BOOL), member(type, T_STRING),
- member(deprecated, T_BOOL), member(hidden, T_BOOL),
- member(text, T_STRING), member(desc, T_STRING),
- member(min, T_LONGLONG), member(max, T_LONGLONG),
- member(varname, T_STRING), {},
+ /* clang-format off */
+ member(type, T_STRING),
+ member(idx, T_ULONG),
+ {},
+ /* clang-format on */
+};
+#undef member
+
+static PyObject *graph_node_get_str(PyObject *self, void *poffset)
+{
+ struct wrap_graph_node *wrap = (struct wrap_graph_node *)self;
+ void *offset = (char *)wrap->node->data + (ptrdiff_t)poffset;
+ const char *val = *(const char **)offset;
+
+ if (!val)
+ Py_RETURN_NONE;
+ return PyUnicode_FromString(val);
+}
+
+static PyObject *graph_node_get_bool(PyObject *self, void *poffset)
+{
+ struct wrap_graph_node *wrap = (struct wrap_graph_node *)self;
+ void *offset = (char *)wrap->node->data + (ptrdiff_t)poffset;
+ bool val = *(bool *)offset;
+
+ return PyBool_FromLong(val);
+}
+
+static PyObject *graph_node_get_ll(PyObject *self, void *poffset)
+{
+ struct wrap_graph_node *wrap = (struct wrap_graph_node *)self;
+ void *offset = (char *)wrap->node->data + (ptrdiff_t)poffset;
+ long long val = *(long long *)offset;
+
+ return PyLong_FromLongLong(val);
+}
+
+static PyObject *graph_node_get_u8(PyObject *self, void *poffset)
+{
+ struct wrap_graph_node *wrap = (struct wrap_graph_node *)self;
+ void *offset = (char *)wrap->node->data + (ptrdiff_t)poffset;
+ uint8_t val = *(uint8_t *)offset;
+
+ return PyLong_FromUnsignedLong(val);
+}
+
+/* clang-format off */
+#define member(name, variant) \
+ { \
+ (char *)#name, \
+ graph_node_get_##variant, \
+ NULL, \
+ (char *)#name " (" #variant ")", \
+ (void *)offsetof(struct cmd_token, name), \
+ }
+/* clang-format on */
+
+static PyGetSetDef getset_graph_node[] = {
+ /* clang-format off */
+ member(attr, u8),
+ member(allowrepeat, bool),
+ member(varname_src, u8),
+ member(text, str),
+ member(desc, str),
+ member(min, ll),
+ member(max, ll),
+ member(varname, str),
+ {},
+ /* clang-format on */
};
#undef member
@@ -101,12 +160,30 @@ static PyObject *graph_node_next(PyObject *self, PyObject *args)
struct wrap_graph_node *wrap = (struct wrap_graph_node *)self;
PyObject *pylist;
- if (wrap->node->data
- && ((struct cmd_token *)wrap->node->data)->type == END_TKN)
+ if (wrap->node->data &&
+ ((struct cmd_token *)wrap->node->data)->type == CMD_ELEMENT_TKN)
return PyList_New(0);
pylist = PyList_New(vector_active(wrap->node->to));
for (size_t i = 0; i < vector_active(wrap->node->to); i++) {
struct graph_node *gn = vector_slot(wrap->node->to, i);
+
+ PyList_SetItem(pylist, i, graph_to_pyobj(wrap->wgraph, gn));
+ }
+ return pylist;
+};
+
+static PyObject *graph_node_prev(PyObject *self, PyObject *args)
+{
+ struct wrap_graph_node *wrap = (struct wrap_graph_node *)self;
+ PyObject *pylist;
+
+ if (wrap->node->data &&
+ ((struct cmd_token *)wrap->node->data)->type == START_TKN)
+ return PyList_New(0);
+ pylist = PyList_New(vector_active(wrap->node->from));
+ for (size_t i = 0; i < vector_active(wrap->node->from); i++) {
+ struct graph_node *gn = vector_slot(wrap->node->from, i);
+
PyList_SetItem(pylist, i, graph_to_pyobj(wrap->wgraph, gn));
}
return pylist;
@@ -118,30 +195,60 @@ static PyObject *graph_node_next(PyObject *self, PyObject *args)
static PyObject *graph_node_join(PyObject *self, PyObject *args)
{
struct wrap_graph_node *wrap = (struct wrap_graph_node *)self;
+ struct cmd_token *tok;
if (!wrap->node->data
|| ((struct cmd_token *)wrap->node->data)->type == END_TKN)
Py_RETURN_NONE;
- struct cmd_token *tok = wrap->node->data;
+ tok = wrap->node->data;
if (tok->type != FORK_TKN)
Py_RETURN_NONE;
return graph_to_pyobj(wrap->wgraph, tok->forkjoin);
};
+static PyObject *graph_node_fork(PyObject *self, PyObject *args)
+{
+ struct wrap_graph_node *wrap = (struct wrap_graph_node *)self;
+ struct cmd_token *tok;
+
+ if (!wrap->node->data ||
+ ((struct cmd_token *)wrap->node->data)->type == END_TKN)
+ Py_RETURN_NONE;
+
+ tok = wrap->node->data;
+ if (tok->type != JOIN_TKN)
+ Py_RETURN_NONE;
+
+ return graph_to_pyobj(wrap->wgraph, tok->forkjoin);
+};
+
static PyMethodDef methods_graph_node[] = {
- {"next", graph_node_next, METH_NOARGS, "outbound graph edge list"},
- {"join", graph_node_join, METH_NOARGS, "outbound join node"},
- {}};
+ { "next", graph_node_next, METH_NOARGS, "outbound graph edge list" },
+ { "prev", graph_node_prev, METH_NOARGS, "inbound graph edge list" },
+ { "join", graph_node_join, METH_NOARGS, "outbound join node" },
+ { "fork", graph_node_fork, METH_NOARGS, "inbound fork node" },
+ {}
+};
static void graph_node_wrap_free(void *arg)
{
struct wrap_graph_node *wrap = arg;
+
+ assert(wrap->idx < wrap->wgraph->n_nodewrappers);
wrap->wgraph->nodewrappers[wrap->idx] = NULL;
Py_DECREF(wrap->wgraph);
}
+static PyObject *repr_graph_node(PyObject *arg)
+{
+ struct wrap_graph_node *wrap = (struct wrap_graph_node *)arg;
+
+ return PyUnicode_FromFormat("<_clippy.GraphNode %p [%zu] %s>",
+ wrap->node, wrap->idx, wrap->type);
+}
+
static PyTypeObject typeobj_graph_node = {
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "_clippy.GraphNode",
.tp_basicsize = sizeof(struct wrap_graph_node),
@@ -150,13 +257,14 @@ static PyTypeObject typeobj_graph_node = {
.tp_new = refuse_new,
.tp_free = graph_node_wrap_free,
.tp_members = members_graph_node,
+ .tp_getset = getset_graph_node,
.tp_methods = methods_graph_node,
+ .tp_repr = repr_graph_node,
};
static PyObject *graph_to_pyobj(struct wrap_graph *wgraph,
struct graph_node *gn)
{
- struct wrap_graph_node *wrap;
size_t i;
for (i = 0; i < vector_active(wgraph->graph->nodes); i++)
@@ -166,6 +274,24 @@ static PyObject *graph_to_pyobj(struct wrap_graph *wgraph,
PyErr_SetString(PyExc_ValueError, "cannot find node in graph");
return NULL;
}
+
+ return graph_to_pyobj_idx(wgraph, i);
+}
+
+static PyObject *graph_to_pyobj_idx(struct wrap_graph *wgraph, size_t i)
+{
+ struct wrap_graph_node *wrap;
+ struct graph_node *gn = vector_slot(wgraph->graph->nodes, i);
+
+ if (i >= wgraph->n_nodewrappers) {
+ wgraph->nodewrappers =
+ realloc(wgraph->nodewrappers,
+ (i + 1) * sizeof(wgraph->nodewrappers[0]));
+ memset(wgraph->nodewrappers + wgraph->n_nodewrappers, 0,
+ sizeof(wgraph->nodewrappers[0]) *
+ (i + 1 - wgraph->n_nodewrappers));
+ wgraph->n_nodewrappers = i + 1;
+ }
if (wgraph->nodewrappers[i]) {
PyObject *obj = (PyObject *)wgraph->nodewrappers[i];
Py_INCREF(obj);
@@ -209,19 +335,11 @@ static PyObject *graph_to_pyobj(struct wrap_graph *wgraph,
item(START_TKN);
item(END_TKN);
item(NEG_ONLY_TKN);
+ item(CMD_ELEMENT_TKN);
#undef item
default:
wrap->type = "???";
}
-
- wrap->deprecated = !!(tok->attr & CMD_ATTR_DEPRECATED);
- wrap->hidden = !!(tok->attr & CMD_ATTR_HIDDEN);
- wrap->text = tok->text;
- wrap->desc = tok->desc;
- wrap->varname = tok->varname;
- wrap->min = tok->min;
- wrap->max = tok->max;
- wrap->allowrepeat = tok->allowrepeat;
}
return (PyObject *)wrap;
@@ -246,9 +364,13 @@ static PyObject *graph_first(PyObject *self, PyObject *args)
return graph_to_pyobj(gwrap, gn);
};
+static PyObject *graph_merge(PyObject *self, PyObject *args);
+
static PyMethodDef methods_graph[] = {
- {"first", graph_first, METH_NOARGS, "first graph node"},
- {}};
+ { "first", graph_first, METH_NOARGS, "first graph node" },
+ { "merge", graph_merge, METH_VARARGS, "merge graphs" },
+ {}
+};
static PyObject *graph_parse(PyTypeObject *type, PyObject *args,
PyObject *kwds);
@@ -262,6 +384,30 @@ static void graph_wrap_free(void *arg)
free(wgraph->definition);
}
+static Py_ssize_t graph_length(PyObject *self)
+{
+ struct wrap_graph *gwrap = (struct wrap_graph *)self;
+
+ return vector_active(gwrap->graph->nodes);
+}
+
+static PyObject *graph_item(PyObject *self, Py_ssize_t idx)
+{
+ struct wrap_graph *gwrap = (struct wrap_graph *)self;
+
+ if (idx >= vector_active(gwrap->graph->nodes))
+ return PyErr_Format(PyExc_IndexError,
+ "index %zd past graph size %u", idx,
+ vector_active(gwrap->graph->nodes));
+
+ return graph_to_pyobj_idx(gwrap, idx);
+}
+
+static PySequenceMethods seq_graph = {
+ .sq_length = graph_length,
+ .sq_item = graph_item,
+};
+
static PyTypeObject typeobj_graph = {
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "_clippy.Graph",
.tp_basicsize = sizeof(struct wrap_graph),
@@ -271,35 +417,62 @@ static PyTypeObject typeobj_graph = {
.tp_free = graph_wrap_free,
.tp_members = members_graph,
.tp_methods = methods_graph,
+ .tp_as_sequence = &seq_graph,
};
+static PyObject *graph_merge(PyObject *self, PyObject *args)
+{
+ PyObject *py_other;
+ struct wrap_graph *gwrap = (struct wrap_graph *)self;
+ struct wrap_graph *gother;
+
+ if (!PyArg_ParseTuple(args, "O!", &typeobj_graph, &py_other))
+ return NULL;
+
+ gother = (struct wrap_graph *)py_other;
+ cmd_graph_merge(gwrap->graph, gother->graph, +1);
+ Py_RETURN_NONE;
+}
+
/* top call / entrypoint for python code */
static PyObject *graph_parse(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
- const char *def, *doc = NULL;
+ const char *def, *doc = NULL, *name = NULL;
struct wrap_graph *gwrap;
- static const char *kwnames[] = {"cmddef", "doc", NULL};
+ static const char *const kwnames[] = { "cmddef", "doc", "name", NULL };
gwrap = (struct wrap_graph *)typeobj_graph.tp_alloc(&typeobj_graph, 0);
if (!gwrap)
return NULL;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s", (char **)kwnames,
- &def, &doc))
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "z|ss", (char **)kwnames,
+ &def, &doc, &name))
return NULL;
struct graph *graph = graph_new();
struct cmd_token *token = cmd_token_new(START_TKN, 0, NULL, NULL);
graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
- struct cmd_element cmd = {.string = def, .doc = doc};
- cmd_graph_parse(graph, &cmd);
- cmd_graph_names(graph);
+ if (def) {
+ struct cmd_element cmd = { .string = def, .doc = doc };
+ struct graph_node *last;
+
+ cmd_graph_parse(graph, &cmd);
+ cmd_graph_names(graph);
+
+ last = vector_slot(graph->nodes,
+ vector_active(graph->nodes) - 1);
+ assert(last->data == &cmd);
+
+ last->data = cmd_token_new(CMD_ELEMENT_TKN, 0, name, def);
+ last->del = (void (*)(void *))cmd_token_del;
+
+ gwrap->definition = strdup(def);
+ } else {
+ gwrap->definition = strdup("NULL");
+ }
gwrap->graph = graph;
- gwrap->definition = strdup(def);
- gwrap->nodewrappers = calloc(vector_active(graph->nodes),
- sizeof(gwrap->nodewrappers[0]));
return (PyObject *)gwrap;
}
diff --git a/lib/darr.h b/lib/darr.h
index 404869d9a2..2b9a0a0c02 100644
--- a/lib/darr.h
+++ b/lib/darr.h
@@ -24,6 +24,8 @@
* - darr_ensure_i
* - darr_ensure_i_mt
* - darr_free
+ * - darr_free_free
+ * - darr_free_func
* - darr_insert
* - darr_insert_mt
* - darr_insertz
@@ -218,6 +220,41 @@ void *__darr_resize(void *a, uint count, size_t esize, struct memtype *mt);
} while (0)
/**
+ * Free memory allocated for the dynamic array `A`, calling `darr_free` for
+ * each element of the array first.
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ */
+#define darr_free_free(A) \
+ do { \
+ for (uint __i = 0; __i < darr_len(A); __i++) \
+ if ((A)[__i]) { \
+ struct darr_metadata *__meta = \
+ _darr_meta((A)[__i]); \
+ XFREE(__meta->mtype, __meta); \
+ } \
+ darr_free(A); \
+ } while (0)
+
+/**
+ * Free memory allocated for the dynamic array `A`, calling `F` routine
+ * for each element of the array first.
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ * F: The function to call for each element.
+ */
+
+#define darr_free_func(A, F) \
+ do { \
+ for (uint __i = 0; __i < darr_len(A); __i++) { \
+ F((A)[__i]); \
+ } \
+ darr_free(A); \
+ } while (0)
+
+/**
* Make sure that there is room in the dynamic array `A` to add `C` elements.
*
* Available space is `darr_cap(a) - darr_len(a)`.
diff --git a/lib/debug.c b/lib/debug.c
index 757a47ab99..d25c32d428 100644
--- a/lib/debug.c
+++ b/lib/debug.c
@@ -9,42 +9,75 @@
#include "debug.h"
#include "command.h"
-static struct debug_cb_list_head cb_head;
+static struct debug_list_head debug_head;
-DECLARE_LIST(debug_cb_list, struct debug_callbacks, item);
+DECLARE_LIST(debug_list, struct debug, item);
/* All code in this section should be reentrant and MT-safe */
-DEFUN_NOSH(debug_all, debug_all_cmd, "[no] debug all",
- NO_STR DEBUG_STR "Toggle all debugging output\n")
+DEFUN_NOSH (debug_all,
+ debug_all_cmd,
+ "[no] debug all",
+ NO_STR DEBUG_STR
+ "Toggle all debugging output\n")
{
- struct debug_callbacks *cb;
-
+ struct debug *debug;
bool set = !strmatch(argv[0]->text, "no");
uint32_t mode = DEBUG_NODE2MODE(vty->node);
- frr_each (debug_cb_list, &cb_head, cb)
- cb->debug_set_all(mode, set);
+ frr_each (debug_list, &debug_head, debug) {
+ DEBUG_MODE_SET(debug, mode, set);
+
+ /* If all modes have been turned off, don't preserve options. */
+ if (!DEBUG_MODE_CHECK(debug, DEBUG_MODE_ALL))
+ DEBUG_CLEAR(debug);
+ }
return CMD_SUCCESS;
}
/* ------------------------------------------------------------------------- */
-void debug_init(struct debug_callbacks *cb)
+void debug_status_write(struct vty *vty)
+{
+ struct debug *debug;
+
+ frr_each (debug_list, &debug_head, debug) {
+ if (DEBUG_MODE_CHECK(debug, DEBUG_MODE_ALL))
+ vty_out(vty, " %s debugging is on\n", debug->desc);
+ }
+}
+
+static int config_write_debug(struct vty *vty)
{
- static bool inited = false;
+ struct debug *debug;
- if (!inited) {
- inited = true;
- debug_cb_list_init(&cb_head);
+ frr_each (debug_list, &debug_head, debug) {
+ if (DEBUG_MODE_CHECK(debug, DEBUG_MODE_CONF))
+ vty_out(vty, "%s\n", debug->conf);
}
- debug_cb_list_add_head(&cb_head, cb);
+ return 0;
}
-void debug_init_cli(void)
+static struct cmd_node debug_node = {
+ .name = "debug",
+ .node = LIB_DEBUG_NODE,
+ .prompt = "",
+ .config_write = config_write_debug,
+};
+
+void debug_install(struct debug *debug)
{
+ debug_list_add_tail(&debug_head, debug);
+}
+
+void debug_init(void)
+{
+ debug_list_init(&debug_head);
+
+ install_node(&debug_node);
+
install_element(ENABLE_NODE, &debug_all_cmd);
install_element(CONFIG_NODE, &debug_all_cmd);
}
diff --git a/lib/debug.h b/lib/debug.h
index e9d8a31abd..eee314cff5 100644
--- a/lib/debug.h
+++ b/lib/debug.h
@@ -34,6 +34,7 @@ extern "C" {
#define DEBUG_OPT_NONE 0x00000000
+PREDECL_LIST(debug_list);
/*
* Debugging record.
*
@@ -63,37 +64,18 @@ extern "C" {
* manipulate the flags field in a multithreaded environment results in
* undefined behavior.
*
+ * conf
+ * The configuration string that will be written to the config file.
+ *
* desc
* Human-readable description of this debugging record.
*/
struct debug {
atomic_uint_fast32_t flags;
+ const char *conf;
const char *desc;
-};
-PREDECL_LIST(debug_cb_list);
-/*
- * Callback set for debugging code.
- *
- * debug_set_all
- * Function pointer to call when the user requests that all debugs have a
- * mode set.
- */
-struct debug_callbacks {
- /*
- * Linked list of Callbacks to call
- */
- struct debug_cb_list_item item;
-
- /*
- * flags
- * flags to set on debug flag fields
- *
- * set
- * true: set flags
- * false: unset flags
- */
- void (*debug_set_all)(uint32_t flags, bool set);
+ struct debug_list_item item;
};
/*
@@ -217,22 +199,19 @@ struct debug_callbacks {
#define DEBUGN(name, fmt, ...) DEBUG(notice, name, fmt, ##__VA_ARGS__)
#define DEBUGD(name, fmt, ...) DEBUG(debug, name, fmt, ##__VA_ARGS__)
+/* Show current debugging status. */
+void debug_status_write(struct vty *vty);
+
/*
- * Optional initializer for debugging. Highly recommended.
- *
- * This function installs common debugging commands and allows the caller to
- * specify callbacks to take when these commands are issued, allowing the
- * caller to respond to events such as a request to turn off all debugs.
- *
- * MT-Safe
+ * Register a debug item.
*/
-void debug_init(struct debug_callbacks *cb);
+void debug_install(struct debug *debug);
/*
- * Turn on the cli to turn on/off debugs.
- * Should only be called by libfrr
+ * Initialize debugging.
+ * Should only be called by libfrr.
*/
-void debug_init_cli(void);
+void debug_init(void);
#ifdef __cplusplus
}
diff --git a/lib/distribute.c b/lib/distribute.c
index ccd1f1379e..c0693b0849 100644
--- a/lib/distribute.c
+++ b/lib/distribute.c
@@ -456,8 +456,43 @@ int group_distribute_list_create_helper(
* XPath: /frr-ripd:ripd/instance/distribute-lists/distribute-list/{in,out}/{access,prefix}-list
*/
+static int distribute_list_leaf_update(const struct lyd_node *dnode,
+ int ip_version, bool no);
+
int group_distribute_list_destroy(struct nb_cb_destroy_args *args)
{
+ struct lyd_node *dnode;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ /*
+ * We don't keep the IP version of distribute-list anywhere, so we're
+ * trying to remove both. If one doesn't exist, it's simply skipped by
+ * the remove function.
+ */
+
+ dnode = yang_dnode_get(args->dnode, "in/access-list");
+ if (dnode) {
+ distribute_list_leaf_update(dnode, 4, true);
+ distribute_list_leaf_update(dnode, 6, true);
+ }
+ dnode = yang_dnode_get(args->dnode, "in/prefix-list");
+ if (dnode) {
+ distribute_list_leaf_update(dnode, 4, true);
+ distribute_list_leaf_update(dnode, 6, true);
+ }
+ dnode = yang_dnode_get(args->dnode, "out/access-list");
+ if (dnode) {
+ distribute_list_leaf_update(dnode, 4, true);
+ distribute_list_leaf_update(dnode, 6, true);
+ }
+ dnode = yang_dnode_get(args->dnode, "out/prefix-list");
+ if (dnode) {
+ distribute_list_leaf_update(dnode, 4, true);
+ distribute_list_leaf_update(dnode, 6, true);
+ }
+
nb_running_unset_entry(args->dnode);
return NB_OK;
}
diff --git a/lib/event.c b/lib/event.c
index fc46a11c0b..d925d0d5f0 100644
--- a/lib/event.c
+++ b/lib/event.c
@@ -304,9 +304,6 @@ static uint8_t parse_filter(const char *filterstr)
return filter;
}
-#if CONFDATE > 20240707
- CPP_NOTICE("Remove `show thread ...` commands")
-#endif
DEFUN_NOSH (show_event_cpu,
show_event_cpu_cmd,
"show event cpu [FILTER]",
@@ -332,14 +329,6 @@ DEFUN_NOSH (show_event_cpu,
return CMD_SUCCESS;
}
-ALIAS(show_event_cpu,
- show_thread_cpu_cmd,
- "show thread cpu [FILTER]",
- SHOW_STR
- "Thread information\n"
- "Thread CPU usage\n"
- "Display filter (rwtex)\n")
-
DEFPY (service_cputime_stats,
service_cputime_stats_cmd,
"[no] service cputime-stats",
@@ -440,19 +429,15 @@ DEFUN_NOSH (show_event_poll,
return CMD_SUCCESS;
}
-ALIAS(show_event_poll,
- show_thread_poll_cmd,
- "show thread poll",
- SHOW_STR
- "Thread information\n"
- "Show poll FD's and information\n")
-
-DEFUN (clear_thread_cpu,
- clear_thread_cpu_cmd,
- "clear thread cpu [FILTER]",
+#if CONFDATE > 20241231
+CPP_NOTICE("Remove `clear thread cpu` command")
+#endif
+DEFUN (clear_event_cpu,
+ clear_event_cpu_cmd,
+ "clear event cpu [FILTER]",
"Clear stored data in all pthreads\n"
- "Thread information\n"
- "Thread CPU usage\n"
+ "Event information\n"
+ "Event CPU usage\n"
"Display filter (rwtexb)\n")
{
uint8_t filter = (uint8_t)-1U;
@@ -472,6 +457,14 @@ DEFUN (clear_thread_cpu,
return CMD_SUCCESS;
}
+ALIAS (clear_event_cpu,
+ clear_thread_cpu_cmd,
+ "clear thread cpu [FILTER]",
+ "Clear stored data in all pthreads\n"
+ "Thread information\n"
+ "Thread CPU usage\n"
+ "Display filter (rwtexb)\n")
+
static void show_event_timers_helper(struct vty *vty, struct event_loop *m)
{
const char *name = m->name ? m->name : "main";
@@ -507,26 +500,17 @@ DEFPY_NOSH (show_event_timers,
return CMD_SUCCESS;
}
-ALIAS(show_event_timers,
- show_thread_timers_cmd,
- "show thread timers",
- SHOW_STR
- "Thread information\n"
- "Show all timers and how long they have in the system\n")
-
void event_cmd_init(void)
{
- install_element(VIEW_NODE, &show_thread_cpu_cmd);
install_element(VIEW_NODE, &show_event_cpu_cmd);
- install_element(VIEW_NODE, &show_thread_poll_cmd);
install_element(VIEW_NODE, &show_event_poll_cmd);
install_element(ENABLE_NODE, &clear_thread_cpu_cmd);
+ install_element(ENABLE_NODE, &clear_event_cpu_cmd);
install_element(CONFIG_NODE, &service_cputime_stats_cmd);
install_element(CONFIG_NODE, &service_cputime_warning_cmd);
install_element(CONFIG_NODE, &service_walltime_warning_cmd);
- install_element(VIEW_NODE, &show_thread_timers_cmd);
install_element(VIEW_NODE, &show_event_timers_cmd);
}
/* CLI end ------------------------------------------------------------------ */
@@ -571,8 +555,9 @@ struct event_loop *event_master_create(const char *name)
}
if (rv->fd_limit > STUPIDLY_LARGE_FD_SIZE) {
- zlog_warn("FD Limit set: %u is stupidly large. Is this what you intended? Consider using --limit-fds also limiting size to %u",
- rv->fd_limit, STUPIDLY_LARGE_FD_SIZE);
+ if (frr_is_daemon())
+ zlog_warn("FD Limit set: %u is stupidly large. Is this what you intended? Consider using --limit-fds also limiting size to %u",
+ rv->fd_limit, STUPIDLY_LARGE_FD_SIZE);
rv->fd_limit = STUPIDLY_LARGE_FD_SIZE;
}
diff --git a/lib/flex_algo.c b/lib/flex_algo.c
index f48117ff1b..ab0eef67cb 100644
--- a/lib/flex_algo.c
+++ b/lib/flex_algo.c
@@ -20,9 +20,6 @@
DEFINE_MTYPE_STATIC(LIB, FLEX_ALGO_DATABASE, "Flex-Algo database");
DEFINE_MTYPE_STATIC(LIB, FLEX_ALGO, "Flex-Algo algorithm information");
-static void _flex_algo_delete(struct flex_algos *flex_algos,
- struct flex_algo *fa);
-
struct flex_algos *flex_algos_alloc(flex_algo_allocator_t allocator,
flex_algo_releaser_t releaser)
{
@@ -42,7 +39,7 @@ void flex_algos_free(struct flex_algos *flex_algos)
struct flex_algo *fa;
for (ALL_LIST_ELEMENTS(flex_algos->flex_algos, node, nnode, fa))
- _flex_algo_delete(flex_algos, fa);
+ flex_algo_free(flex_algos, fa);
list_delete(&flex_algos->flex_algos);
XFREE(MTYPE_FLEX_ALGO_DATABASE, flex_algos);
}
@@ -63,8 +60,7 @@ struct flex_algo *flex_algo_alloc(struct flex_algos *flex_algos,
return fa;
}
-static void _flex_algo_delete(struct flex_algos *flex_algos,
- struct flex_algo *fa)
+void flex_algo_free(struct flex_algos *flex_algos, struct flex_algo *fa)
{
if (flex_algos->releaser)
flex_algos->releaser(fa->data);
@@ -75,19 +71,6 @@ static void _flex_algo_delete(struct flex_algos *flex_algos,
XFREE(MTYPE_FLEX_ALGO, fa);
}
-
-void flex_algo_delete(struct flex_algos *flex_algos, uint8_t algorithm)
-{
- struct listnode *node, *nnode;
- struct flex_algo *fa;
-
- for (ALL_LIST_ELEMENTS(flex_algos->flex_algos, node, nnode, fa)) {
- if (fa->algorithm != algorithm)
- continue;
- _flex_algo_delete(flex_algos, fa);
- }
-}
-
/**
* @brief Look up the local flex-algo object by its algorithm number.
* @param algorithm flex-algo algorithm number
diff --git a/lib/flex_algo.h b/lib/flex_algo.h
index e617e7cae8..54b37783e6 100644
--- a/lib/flex_algo.h
+++ b/lib/flex_algo.h
@@ -115,11 +115,10 @@ struct flex_algos *flex_algos_alloc(flex_algo_allocator_t allocator,
void flex_algos_free(struct flex_algos *flex_algos);
struct flex_algo *flex_algo_alloc(struct flex_algos *flex_algos,
uint8_t algorithm, void *arg);
+void flex_algo_free(struct flex_algos *flex_algos, struct flex_algo *fa);
struct flex_algo *flex_algo_lookup(struct flex_algos *flex_algos,
uint8_t algorithm);
-void flex_algos_free(struct flex_algos *flex_algos);
bool flex_algo_definition_cmp(struct flex_algo *fa1, struct flex_algo *fa2);
-void flex_algo_delete(struct flex_algos *flex_algos, uint8_t algorithm);
bool flex_algo_id_valid(uint16_t algorithm);
char *flex_algo_metric_type_print(char *type_str, size_t sz,
enum flex_algo_metric_type metric_type);
diff --git a/lib/frrcu.h b/lib/frrcu.h
index 9f07a69b52..81ab5528a9 100644
--- a/lib/frrcu.h
+++ b/lib/frrcu.h
@@ -156,7 +156,7 @@ extern void rcu_enqueue(struct rcu_head *head, const struct rcu_action *action);
#define rcu_call(func, ptr, field) \
do { \
typeof(ptr) _ptr = (ptr); \
- void (*fptype)(typeof(ptr)); \
+ void (*_fptype)(typeof(ptr)); \
struct rcu_head *_rcu_head = &_ptr->field; \
static const struct rcu_action _rcu_action = { \
.type = RCUA_CALL, \
diff --git a/lib/frrlua.c b/lib/frrlua.c
index 2cab1a5460..ef081e4bd0 100644
--- a/lib/frrlua.c
+++ b/lib/frrlua.c
@@ -323,7 +323,7 @@ void lua_pushnexthop_group(lua_State *L, const struct nexthop_group *ng)
{
lua_newtable(L);
struct nexthop *nexthop;
- int i = 0;
+ int i = 1;
for (ALL_NEXTHOPS_PTR(ng, nexthop)) {
lua_pushnexthop(L, nexthop);
@@ -382,6 +382,12 @@ static const char *frrlua_log_thunk(lua_State *L)
return lua_tostring(L, 1);
}
+static int frrlua_log_trace(lua_State *L)
+{
+ zlog_debug("%s", frrlua_stackdump(L));
+ return 0;
+}
+
static int frrlua_log_debug(lua_State *L)
{
zlog_debug("%s", frrlua_log_thunk(L));
@@ -413,11 +419,12 @@ static int frrlua_log_error(lua_State *L)
}
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},
+ { "trace", frrlua_log_trace },
+ { "debug", frrlua_log_debug },
+ { "info", frrlua_log_info },
+ { "notice", frrlua_log_notice },
+ { "warn", frrlua_log_warn },
+ { "error", frrlua_log_error },
{},
};
@@ -432,6 +439,67 @@ void frrlua_export_logging(lua_State *L)
* Debugging.
*/
+void lua_table_dump(lua_State *L, int index, struct buffer *buf, int level)
+{
+ char tmpbuf[64] = {};
+
+ lua_pushnil(L);
+
+ while (lua_next(L, index) != 0) {
+ int key_type;
+ int value_type;
+
+ for (int i = 0; i < level; i++)
+ buffer_putstr(buf, " ");
+
+ key_type = lua_type(L, -2);
+ if (key_type == LUA_TSTRING) {
+ const char *key = lua_tostring(L, -2);
+
+ buffer_putstr(buf, key);
+ buffer_putstr(buf, ": ");
+ } else if (key_type == LUA_TNUMBER) {
+ snprintf(tmpbuf, sizeof(tmpbuf), "%g",
+ lua_tonumber(L, -2));
+ buffer_putstr(buf, tmpbuf);
+ buffer_putstr(buf, ": ");
+ }
+
+ value_type = lua_type(L, -1);
+ switch (value_type) {
+ case LUA_TSTRING:
+ snprintf(tmpbuf, sizeof(tmpbuf), "\"%s\"\n",
+ lua_tostring(L, -1));
+ buffer_putstr(buf, tmpbuf);
+ break;
+ case LUA_TBOOLEAN:
+ snprintf(tmpbuf, sizeof(tmpbuf), "%s\n",
+ lua_toboolean(L, -1) ? "true" : "false");
+ buffer_putstr(buf, tmpbuf);
+ break;
+ case LUA_TNUMBER:
+ snprintf(tmpbuf, sizeof(tmpbuf), "%g\n",
+ lua_tonumber(L, -1));
+ buffer_putstr(buf, tmpbuf);
+ break;
+ case LUA_TTABLE:
+ buffer_putstr(buf, "{\n");
+ lua_table_dump(L, lua_gettop(L), buf, level + 1);
+ for (int i = 0; i < level; i++)
+ buffer_putstr(buf, " ");
+ buffer_putstr(buf, "}\n");
+ break;
+ default:
+ snprintf(tmpbuf, sizeof(tmpbuf), "%s\n",
+ lua_typename(L, value_type));
+ buffer_putstr(buf, tmpbuf);
+ break;
+ }
+
+ lua_pop(L, 1);
+ }
+}
+
char *frrlua_stackdump(lua_State *L)
{
int top = lua_gettop(L);
@@ -458,6 +526,11 @@ char *frrlua_stackdump(lua_State *L)
lua_tonumber(L, i));
buffer_putstr(buf, tmpbuf);
break;
+ case LUA_TTABLE: /* tables */
+ buffer_putstr(buf, "{\n");
+ lua_table_dump(L, i, buf, 1);
+ buffer_putstr(buf, "}\n");
+ break;
default: /* other values */
snprintf(tmpbuf, sizeof(tmpbuf), "%s\n",
lua_typename(L, t));
diff --git a/lib/frrlua.h b/lib/frrlua.h
index dc0f4d9986..e407a4492f 100644
--- a/lib/frrlua.h
+++ b/lib/frrlua.h
@@ -181,6 +181,9 @@ int frrlua_table_get_integer(lua_State *L, const char *key);
*/
void frrlua_export_logging(lua_State *L);
+/* A helper fuction that dumps the Lua stack */
+void lua_table_dump(lua_State *L, int index, struct buffer *buf, int level);
+
/*
* Dump Lua stack to a string.
*
diff --git a/lib/hash.c b/lib/hash.c
index df56243985..edbfeec464 100644
--- a/lib/hash.c
+++ b/lib/hash.c
@@ -444,7 +444,7 @@ DEFUN_NOSH(show_hash_stats,
ttable_colseps(tt, 0, RIGHT, true, '|');
char *table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
- XFREE(MTYPE_TMP, table);
+ XFREE(MTYPE_TMP_TTABLE, table);
} else
vty_out(vty, "No named hash tables to display.\n");
diff --git a/lib/hash.h b/lib/hash.h
index 2d00a334be..efa7011bc2 100644
--- a/lib/hash.h
+++ b/lib/hash.h
@@ -13,6 +13,18 @@
extern "C" {
#endif
+/*
+ * NOTICE:
+ *
+ * If you are reading this file in an effort to add a new hash structure
+ * this is the wrong place to be using it. Please see the typesafe
+ * data structures, or ask one of the other developers.
+ *
+ * If you are reading this file as a way to update an existing usage
+ * of this data structure, please consider just converting the data
+ * structure to one of the typesafe data structures instead.
+ */
+
/* Default hash table size. */
#define HASH_INITIAL_SIZE 256
/* Expansion threshold */
diff --git a/lib/ipaddr.h b/lib/ipaddr.h
index c86e38c867..888955fba0 100644
--- a/lib/ipaddr.h
+++ b/lib/ipaddr.h
@@ -40,8 +40,9 @@ struct ipaddr {
#define IS_IPADDR_V4(p) ((p)->ipa_type == IPADDR_V4)
#define IS_IPADDR_V6(p) ((p)->ipa_type == IPADDR_V6)
-#define SET_IPADDR_V4(p) (p)->ipa_type = IPADDR_V4
-#define SET_IPADDR_V6(p) (p)->ipa_type = IPADDR_V6
+#define SET_IPADDR_NONE(p) ((p)->ipa_type = IPADDR_NONE)
+#define SET_IPADDR_V4(p) ((p)->ipa_type = IPADDR_V4)
+#define SET_IPADDR_V6(p) ((p)->ipa_type = IPADDR_V6)
#define IPADDRSZ(p) \
(IS_IPADDR_V4((p)) ? sizeof(struct in_addr) : sizeof(struct in6_addr))
@@ -165,9 +166,17 @@ static inline bool ipaddr_is_zero(const struct ipaddr *ip)
return true;
}
+static inline bool ipaddr_is_same(const struct ipaddr *ip1,
+ const struct ipaddr *ip2)
+{
+ return ipaddr_cmp(ip1, ip2) == 0;
+}
+
+/* clang-format off */
#ifdef _FRR_ATTRIBUTE_PRINTFRR
#pragma FRR printfrr_ext "%pIA" (struct ipaddr *)
#endif
+/* clang-format on */
#ifdef __cplusplus
}
diff --git a/lib/libfrr.c b/lib/libfrr.c
index 876efe23a8..0a575abac6 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -102,23 +102,25 @@ static void opt_extend(const struct optspec *os)
#define OPTION_SCRIPTDIR 1009
static const struct option lo_always[] = {
- {"help", no_argument, NULL, 'h'},
- {"version", no_argument, NULL, 'v'},
- {"daemon", no_argument, NULL, 'd'},
- {"module", no_argument, NULL, 'M'},
- {"profile", required_argument, NULL, 'F'},
- {"pathspace", required_argument, NULL, 'N'},
- {"vrfdefaultname", required_argument, NULL, 'o'},
- {"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},
- {"command-log-always", no_argument, NULL, OPTION_LOGGING},
- {"limit-fds", required_argument, NULL, OPTION_LIMIT_FDS},
- {NULL}};
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
+ { "daemon", no_argument, NULL, 'd' },
+ { "module", no_argument, NULL, 'M' },
+ { "profile", required_argument, NULL, 'F' },
+ { "pathspace", required_argument, NULL, 'N' },
+ { "vrfdefaultname", required_argument, NULL, 'o' },
+ { "graceful_restart", optional_argument, NULL, 'K' },
+ { "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 },
+ { "command-log-always", no_argument, NULL, OPTION_LOGGING },
+ { "limit-fds", required_argument, NULL, OPTION_LIMIT_FDS },
+ { NULL }
+};
static const struct optspec os_always = {
- "hvdM:F:N:o:",
+ "hvdM:F:N:o:K::",
" -h, --help Display this help and exit\n"
" -v, --version Print program version\n"
" -d, --daemon Runs in daemon mode\n"
@@ -126,13 +128,15 @@ static const struct optspec os_always = {
" -F, --profile Use specified configuration profile\n"
" -N, --pathspace Insert prefix into config & socket paths\n"
" -o, --vrfdefaultname Set default VRF name.\n"
+ " -K, --graceful_restart FRR starting in Graceful Restart mode, with optional route-cleanup timer\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"
" --limit-fds Limit number of fds supported\n",
- lo_always};
+ lo_always
+};
static bool logging_to_stdout = false; /* set when --log stdout specified */
@@ -358,6 +362,8 @@ 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;
+ di->graceful_restart = false;
+ di->gr_cleanup_time = 0;
/* we may be starting with extra FDs open for whatever purpose,
* e.g. logging, some module, etc. Recording them here allows later
@@ -520,6 +526,11 @@ static int frr_opt(int opt)
di->db_file = optarg;
break;
#endif
+ case 'K':
+ di->graceful_restart = true;
+ if (optarg)
+ di->gr_cleanup_time = atoi(optarg);
+ break;
case 'C':
if (di->flags & FRR_NO_SPLIT_CONFIG)
return 1;
@@ -798,6 +809,7 @@ struct event_loop *frr_init(void)
vty_init(master, di->log_always);
lib_cmd_init();
+ debug_init();
frr_pthread_init();
#ifdef HAVE_SCRIPTING
@@ -814,8 +826,6 @@ struct event_loop *frr_init(void)
"%s: failed to initialize northbound database",
__func__);
- debug_init_cli();
-
return master;
}
@@ -1040,7 +1050,17 @@ void frr_config_fork(void)
zlog_tls_buffer_init();
}
-void frr_vty_serv_start(void)
+static void frr_check_detach(void)
+{
+ if (nodetach_term || nodetach_daemon)
+ return;
+
+ if (daemon_ctl_sock != -1)
+ close(daemon_ctl_sock);
+ daemon_ctl_sock = -1;
+}
+
+void frr_vty_serv_start(bool check_detach)
{
/* allow explicit override of vty_path in the future
* (not currently set anywhere) */
@@ -1063,6 +1083,9 @@ void frr_vty_serv_start(void)
}
vty_serv_start(di->vty_addr, di->vty_port, di->vty_path);
+
+ if (check_detach)
+ frr_check_detach();
}
void frr_vty_serv_stop(void)
@@ -1073,16 +1096,6 @@ void frr_vty_serv_stop(void)
unlink(di->vty_path);
}
-static void frr_check_detach(void)
-{
- if (nodetach_term || nodetach_daemon)
- return;
-
- if (daemon_ctl_sock != -1)
- close(daemon_ctl_sock);
- daemon_ctl_sock = -1;
-}
-
static void frr_terminal_close(int isexit)
{
int nullfd;
@@ -1168,7 +1181,7 @@ void frr_run(struct event_loop *master)
char instanceinfo[64] = "";
if (!(di->flags & FRR_MANUAL_VTY_START))
- frr_vty_serv_start();
+ frr_vty_serv_start(false);
if (di->instance)
snprintf(instanceinfo, sizeof(instanceinfo), "instance %u ",
@@ -1206,7 +1219,8 @@ void frr_run(struct event_loop *master)
close(nullfd);
}
- frr_check_detach();
+ if (!(di->flags & FRR_MANUAL_VTY_START))
+ frr_check_detach();
}
/* end fixed stderr startup logging */
@@ -1252,6 +1266,8 @@ void frr_fini(void)
/* frrmod_init -> nothing needed / hooks */
rcu_shutdown();
+ frrmod_terminate();
+
/* also log memstats to stderr when stderr goes to a file*/
if (debug_memstats_at_exit || !isatty(STDERR_FILENO))
have_leftovers = log_memstats(stderr, di->name);
@@ -1446,7 +1462,10 @@ void _libfrr_version(void)
const char banner[] =
FRR_FULL_NAME " " FRR_VERSION ".\n"
FRR_COPYRIGHT GIT_INFO "\n"
- "configured with:\n " FRR_CONFIG_ARGS "\n";
+#ifdef ENABLE_VERSION_BUILD_CONFIG
+ "configured with:\n " FRR_CONFIG_ARGS "\n"
+#endif
+ ;
write(1, banner, sizeof(banner) - 1);
_exit(0);
}
@@ -1459,3 +1478,11 @@ const char *frr_vers2str(uint32_t version, char *buf, int buflen)
return buf;
}
+
+bool frr_is_daemon(void)
+{
+ if (di)
+ return true;
+
+ return false;
+}
diff --git a/lib/libfrr.h b/lib/libfrr.h
index 77d70448a9..7ed7be4d98 100644
--- a/lib/libfrr.h
+++ b/lib/libfrr.h
@@ -118,6 +118,8 @@ struct frr_daemon_info {
bool dryrun;
bool daemon_mode;
bool terminal;
+ bool graceful_restart;
+ int gr_cleanup_time;
enum frr_cli_mode cli_mode;
struct event *read_in;
@@ -188,7 +190,7 @@ extern const char *frr_get_progname(void);
extern enum frr_cli_mode frr_get_cli_mode(void);
extern uint32_t frr_get_fd_limit(void);
extern bool frr_is_startup_fd(int fd);
-
+extern bool frr_is_daemon(void);
/* call order of these hooks is as ordered here */
DECLARE_HOOK(frr_early_init, (struct event_loop * tm), (tm));
DECLARE_HOOK(frr_late_init, (struct event_loop * tm), (tm));
@@ -200,7 +202,7 @@ extern void frr_config_fork(void);
extern void frr_run(struct event_loop *master);
extern void frr_detach(void);
-extern void frr_vty_serv_start(void);
+extern void frr_vty_serv_start(bool check_detach);
extern void frr_vty_serv_stop(void);
extern bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len,
@@ -220,10 +222,39 @@ extern void frr_fini(void);
extern char config_default[512];
extern char frr_zclientpath[512];
+
+/* refer to lib/config_paths.h (generated during ./configure) for build config
+ * values of the following:
+ */
+
+/* sysconfdir is generally /etc/frr/, some BSDs may use /usr/local/etc/frr/.
+ * Will NOT include "pathspace" (namespace) suffix from -N. (libfrr.c handles
+ * pathspace'ing config files.) Has a slash at the end for "historical"
+ * reasons.
+ */
extern const char frr_sysconfdir[];
+
+/* runstatedir is *ephemeral* across reboots. It may either be a ramdisk,
+ * or be wiped during boot. Use only for pid files, sockets, and the like,
+ * not state. Commonly /run/frr or /var/run/frr.
+ * Will include "pathspace" (namespace) suffix from -N.
+ */
extern char frr_runstatedir[256];
+
+/* libstatedir is *persistent*. It's the place to put state like sequence
+ * numbers or databases. Commonly /var/lib/frr.
+ * Will include "pathspace" (namespace) suffix from -N.
+ */
extern char frr_libstatedir[256];
+
+/* moduledir is something along the lines of /usr/lib/frr/modules or
+ * /usr/lib/x86_64-linux-gnu/frr/modules. It is not guaranteed to be a
+ * subdirectory of the directory that the daemon binaries reside in. (e.g.
+ * the "x86_64-linux-gnu" component will be absent from daemon paths.)
+ */
extern const char frr_moduledir[];
+
+/* scriptdir is for Lua scripts, generally ${frr_sysconfdir}/scripts */
extern const char frr_scriptdir[];
extern char frr_protoname[];
diff --git a/lib/libospf.h b/lib/libospf.h
index 0ac490a00e..8a208beb3c 100644
--- a/lib/libospf.h
+++ b/lib/libospf.h
@@ -58,8 +58,10 @@ extern "C" {
#define OSPF_HELLO_DELAY_DEFAULT 10
#define OSPF_ROUTER_PRIORITY_DEFAULT 1
#define OSPF_RETRANSMIT_INTERVAL_DEFAULT 5
+#define OSPF_RETRANSMIT_WINDOW_DEFAULT 50 /* milliseconds */
#define OSPF_TRANSMIT_DELAY_DEFAULT 1
#define OSPF_DEFAULT_BANDWIDTH 10000 /* Mbps */
+#define OSPF_ACK_DELAY_DEFAULT 1
#define OSPF_DEFAULT_REF_BANDWIDTH 100000 /* Kbps */
diff --git a/lib/link_state.c b/lib/link_state.c
index c758b7f575..3d96c75f6d 100644
--- a/lib/link_state.c
+++ b/lib/link_state.c
@@ -414,6 +414,13 @@ int ls_prefix_same(struct ls_prefix *p1, struct ls_prefix *p2)
|| (p1->sr.sid_flag != p2->sr.sid_flag))
return 0;
}
+ if (CHECK_FLAG(p1->flags, LS_PREF_SRV6)) {
+ if (memcmp(&p1->srv6.sid, &p2->srv6.sid,
+ sizeof(struct in6_addr)) ||
+ (p1->srv6.flags != p2->srv6.flags) ||
+ (p1->srv6.behavior != p2->srv6.behavior))
+ return 0;
+ }
/* OK, p1 & p2 are equal */
return 1;
@@ -1388,6 +1395,11 @@ static struct ls_prefix *ls_parse_prefix(struct stream *s)
STREAM_GETC(s, ls_pref->sr.sid_flag);
STREAM_GETC(s, ls_pref->sr.algo);
}
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_SRV6)) {
+ STREAM_GET(&ls_pref->srv6.sid, s, sizeof(struct in6_addr));
+ STREAM_GETW(s, ls_pref->srv6.behavior);
+ STREAM_GETC(s, ls_pref->srv6.flags);
+ }
return ls_pref;
@@ -1632,6 +1644,11 @@ static int ls_format_prefix(struct stream *s, struct ls_prefix *ls_pref)
stream_putc(s, ls_pref->sr.sid_flag);
stream_putc(s, ls_pref->sr.algo);
}
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_SRV6)) {
+ stream_put(s, &ls_pref->srv6.sid, sizeof(struct in6_addr));
+ stream_putw(s, ls_pref->srv6.behavior);
+ stream_putc(s, ls_pref->srv6.flags);
+ }
return 0;
}
@@ -2748,6 +2765,13 @@ static void ls_show_subnet_vty(struct ls_subnet *subnet, struct vty *vty,
sbuf_push(&sbuf, 4, "SID: %d\tAlgorithm: %d\tFlags: 0x%x\n",
pref->sr.sid, pref->sr.algo, pref->sr.sid_flag);
+ if (CHECK_FLAG(pref->flags, LS_PREF_SRV6))
+ sbuf_push(&sbuf, 4,
+ "SIDv6: %pI6\tEndpoint behavior: %s\tFlags: 0x%x\n",
+ &pref->srv6.sid,
+ seg6local_action2str(pref->srv6.behavior),
+ pref->srv6.flags);
+
end:
vty_out(vty, "%s\n", sbuf_buf(&sbuf));
sbuf_free(&sbuf);
@@ -2757,7 +2781,7 @@ static void ls_show_subnet_json(struct ls_subnet *subnet,
struct json_object *json)
{
struct ls_prefix *pref;
- json_object *jsr;
+ json_object *jsr, *jsrv6;
char buf[INET6_BUFSIZ];
pref = subnet->ls_pref;
@@ -2787,6 +2811,16 @@ static void ls_show_subnet_json(struct ls_subnet *subnet,
snprintfrr(buf, INET6_BUFSIZ, "0x%x", pref->sr.sid_flag);
json_object_string_add(jsr, "flags", buf);
}
+ if (CHECK_FLAG(pref->flags, LS_PREF_SRV6)) {
+ jsrv6 = json_object_new_object();
+ json_object_object_add(json, "segment-routing-ipv6", jsrv6);
+ snprintfrr(buf, INET6_BUFSIZ, "%pI6", &pref->srv6.sid);
+ json_object_string_add(jsrv6, "sid", buf);
+ json_object_string_add(jsrv6, "behavior",
+ seg6local_action2str(pref->srv6.behavior));
+ snprintfrr(buf, INET6_BUFSIZ, "0x%x", pref->srv6.flags);
+ json_object_string_add(jsrv6, "flags", buf);
+ }
}
void ls_show_subnet(struct ls_subnet *subnet, struct vty *vty,
diff --git a/lib/link_state.h b/lib/link_state.h
index d819c20db7..c54e2ec6d9 100644
--- a/lib/link_state.h
+++ b/lib/link_state.h
@@ -243,6 +243,7 @@ struct ls_attributes {
#define LS_PREF_EXTENDED_TAG 0x04
#define LS_PREF_METRIC 0x08
#define LS_PREF_SR 0x10
+#define LS_PREF_SRV6 0x20
/* Link State Prefix */
struct ls_prefix {
@@ -258,6 +259,11 @@ struct ls_prefix {
uint8_t sid_flag; /* Segment Routing Flags */
uint8_t algo; /* Algorithm for Segment Routing */
} sr;
+ struct ls_srv6_sid {
+ struct in6_addr sid; /* Segment Routing ID */
+ uint16_t behavior; /* Endpoint behavior bound to the SID */
+ uint8_t flags; /* Flags */
+ } srv6;
};
/**
diff --git a/lib/linklist.h b/lib/linklist.h
index fd953d0769..f922891df9 100644
--- a/lib/linklist.h
+++ b/lib/linklist.h
@@ -10,6 +10,18 @@
extern "C" {
#endif
+/*
+ * NOTICE:
+ *
+ * If you are reading this file in an effort to add a new list structure
+ * this is the wrong place to be using it. Please see the typesafe
+ * data structures, or ask one of the other developers.
+ *
+ * If you are reading this file as a way to update an existing usage
+ * of this data structure, please consider just converting the data
+ * structure to one of the typesafe data structures instead.
+ */
+
/* listnodes must always contain data to be valid. Adding an empty node
* to a list is invalid
*/
diff --git a/lib/log.c b/lib/log.c
index 969ca79256..880180ae5a 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -436,6 +436,9 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_SRV6_LOCATOR_DELETE),
DESC_ENTRY(ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK),
DESC_ENTRY(ZEBRA_SRV6_MANAGER_RELEASE_LOCATOR_CHUNK),
+ DESC_ENTRY(ZEBRA_SRV6_MANAGER_GET_LOCATOR),
+ DESC_ENTRY(ZEBRA_SRV6_MANAGER_GET_SRV6_SID),
+ DESC_ENTRY(ZEBRA_SRV6_MANAGER_RELEASE_SRV6_SID),
DESC_ENTRY(ZEBRA_ERROR),
DESC_ENTRY(ZEBRA_CLIENT_CAPABILITIES),
DESC_ENTRY(ZEBRA_OPAQUE_MESSAGE),
@@ -461,7 +464,8 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_TC_CLASS_DELETE),
DESC_ENTRY(ZEBRA_TC_FILTER_ADD),
DESC_ENTRY(ZEBRA_TC_FILTER_DELETE),
- DESC_ENTRY(ZEBRA_OPAQUE_NOTIFY)
+ DESC_ENTRY(ZEBRA_OPAQUE_NOTIFY),
+ DESC_ENTRY(ZEBRA_SRV6_SID_NOTIFY)
};
#undef DESC_ENTRY
diff --git a/lib/memory.c b/lib/memory.c
index 8fbe5c4093..ac39516edd 100644
--- a/lib/memory.c
+++ b/lib/memory.c
@@ -25,6 +25,7 @@ struct memgroup **mg_insert = &mg_first;
DEFINE_MGROUP(LIB, "libfrr");
DEFINE_MTYPE(LIB, TMP, "Temporary memory");
+DEFINE_MTYPE(LIB, TMP_TTABLE, "Temporary memory for TTABLE");
DEFINE_MTYPE(LIB, BITFIELD, "Bitfield memory");
static inline void mt_count_alloc(struct memtype *mt, size_t size, void *ptr)
diff --git a/lib/memory.h b/lib/memory.h
index 65b99a5fc9..8e8c61da04 100644
--- a/lib/memory.h
+++ b/lib/memory.h
@@ -138,6 +138,7 @@ struct memgroup {
DECLARE_MGROUP(LIB);
DECLARE_MTYPE(TMP);
+DECLARE_MTYPE(TMP_TTABLE);
extern void *qmalloc(struct memtype *mt, size_t size)
diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c
index 6e2fb05e84..f03006ad0e 100644
--- a/lib/mgmt_be_client.c
+++ b/lib/mgmt_be_client.c
@@ -116,6 +116,7 @@ struct mgmt_be_client {
frr_each_safe (mgmt_be_txns, &(client_ctx)->txn_head, (txn))
struct debug mgmt_dbg_be_client = {
+ .conf = "debug mgmt client backend",
.desc = "Management backend client operations"
};
@@ -1061,7 +1062,7 @@ static void be_client_handle_notify(struct mgmt_be_client *client, void *msgbuf,
struct mgmt_msg_notify_data *notif_msg = msgbuf;
struct nb_node *nb_node;
struct lyd_node *dnode;
- const char *data;
+ const char *data = NULL;
const char *notif;
LY_ERR err;
@@ -1258,31 +1259,6 @@ DEFPY(debug_mgmt_client_be, debug_mgmt_client_be_cmd,
return CMD_SUCCESS;
}
-static int mgmt_debug_be_client_config_write(struct vty *vty)
-{
- if (DEBUG_MODE_CHECK(&mgmt_dbg_be_client, DEBUG_MODE_CONF))
- vty_out(vty, "debug mgmt client backend\n");
-
- return 1;
-}
-
-void mgmt_debug_be_client_show_debug(struct vty *vty)
-{
- if (debug_check_be_client())
- vty_out(vty, "debug mgmt client backend\n");
-}
-
-static struct debug_callbacks mgmt_dbg_be_client_cbs = {
- .debug_set_all = mgmt_debug_client_be_set
-};
-
-static struct cmd_node mgmt_dbg_node = {
- .name = "debug mgmt client backend",
- .node = MGMT_BE_DEBUG_NODE,
- .prompt = "",
- .config_write = mgmt_debug_be_client_config_write,
-};
-
struct mgmt_be_client *mgmt_be_client_create(const char *client_name,
struct mgmt_be_client_cbs *cbs,
uintptr_t user_data,
@@ -1328,8 +1304,8 @@ struct mgmt_be_client *mgmt_be_client_create(const char *client_name,
void mgmt_be_client_lib_vty_init(void)
{
- debug_init(&mgmt_dbg_be_client_cbs);
- install_node(&mgmt_dbg_node);
+ debug_install(&mgmt_dbg_be_client);
+
install_element(ENABLE_NODE, &debug_mgmt_client_be_cmd);
install_element(CONFIG_NODE, &debug_mgmt_client_be_cmd);
}
diff --git a/lib/mgmt_be_client.h b/lib/mgmt_be_client.h
index 7ad0589bdb..6ed8c2a39f 100644
--- a/lib/mgmt_be_client.h
+++ b/lib/mgmt_be_client.h
@@ -122,11 +122,6 @@ mgmt_be_client_create(const char *name, struct mgmt_be_client_cbs *cbs,
extern void mgmt_be_client_lib_vty_init(void);
/*
- * Print enabled debugging commands.
- */
-extern void mgmt_debug_be_client_show_debug(struct vty *vty);
-
-/*
* [Un]-subscribe with MGMTD for one or more YANG subtree(s).
*
* client
diff --git a/lib/mgmt_fe_client.c b/lib/mgmt_fe_client.c
index 8cfb025f72..ced2f2e454 100644
--- a/lib/mgmt_fe_client.c
+++ b/lib/mgmt_fe_client.c
@@ -49,6 +49,7 @@ struct mgmt_fe_client {
frr_each_safe (mgmt_sessions, &(client)->sessions, (session))
struct debug mgmt_dbg_fe_client = {
+ .conf = "debug mgmt client frontend",
.desc = "Management frontend client operations"
};
@@ -805,31 +806,6 @@ DEFPY(debug_mgmt_client_fe, debug_mgmt_client_fe_cmd,
return CMD_SUCCESS;
}
-static int mgmt_debug_fe_client_config_write(struct vty *vty)
-{
- if (DEBUG_MODE_CHECK(&mgmt_dbg_fe_client, DEBUG_MODE_CONF))
- vty_out(vty, "debug mgmt client frontend\n");
-
- return CMD_SUCCESS;
-}
-
-void mgmt_debug_fe_client_show_debug(struct vty *vty)
-{
- if (debug_check_fe_client())
- vty_out(vty, "debug mgmt client frontend\n");
-}
-
-static struct debug_callbacks mgmt_dbg_fe_client_cbs = {
- .debug_set_all = mgmt_debug_client_fe_set
-};
-
-static struct cmd_node mgmt_dbg_node = {
- .name = "debug mgmt client frontend",
- .node = MGMT_FE_DEBUG_NODE,
- .prompt = "",
- .config_write = mgmt_debug_fe_client_config_write,
-};
-
/*
* Initialize library and try connecting with MGMTD.
*/
@@ -870,8 +846,8 @@ struct mgmt_fe_client *mgmt_fe_client_create(const char *client_name,
void mgmt_fe_client_lib_vty_init(void)
{
- debug_init(&mgmt_dbg_fe_client_cbs);
- install_node(&mgmt_dbg_node);
+ debug_install(&mgmt_dbg_fe_client);
+
install_element(ENABLE_NODE, &debug_mgmt_client_fe_cmd);
install_element(CONFIG_NODE, &debug_mgmt_client_fe_cmd);
}
diff --git a/lib/mgmt_fe_client.h b/lib/mgmt_fe_client.h
index 20c87044a5..2b5a25fa0d 100644
--- a/lib/mgmt_fe_client.h
+++ b/lib/mgmt_fe_client.h
@@ -179,11 +179,6 @@ mgmt_fe_client_create(const char *client_name, struct mgmt_fe_client_cbs *cbs,
extern void mgmt_fe_client_lib_vty_init(void);
/*
- * Print enabled debugging commands.
- */
-extern void mgmt_debug_fe_client_show_debug(struct vty *vty);
-
-/*
* Create a new Session for a Frontend Client connection.
*
* lib_hndl
diff --git a/lib/mgmt_msg_native.c b/lib/mgmt_msg_native.c
index 39ce9abae6..b85c7d1b61 100644
--- a/lib/mgmt_msg_native.c
+++ b/lib/mgmt_msg_native.c
@@ -6,6 +6,7 @@
*
*/
#include <zebra.h>
+#include "darr.h"
#include "mgmt_msg_native.h"
DEFINE_MGROUP(MSG_NATIVE, "Native message allocations");
@@ -18,6 +19,33 @@ DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_EDIT, "native edit msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_EDIT_REPLY, "native edit reply msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_RPC, "native RPC msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_RPC_REPLY, "native RPC reply msg");
+DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_SESSION_REQ, "native session-req msg");
+DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_SESSION_REPLY, "native session-reply msg");
+
+
+size_t mgmt_msg_min_sizes[] = {
+ [MGMT_MSG_CODE_ERROR] = sizeof(struct mgmt_msg_error),
+ [MGMT_MSG_CODE_GET_TREE] = sizeof(struct mgmt_msg_get_tree),
+ [MGMT_MSG_CODE_TREE_DATA] = sizeof(struct mgmt_msg_tree_data),
+ [MGMT_MSG_CODE_GET_DATA] = sizeof(struct mgmt_msg_get_data),
+ [MGMT_MSG_CODE_NOTIFY] = sizeof(struct mgmt_msg_notify_data),
+ [MGMT_MSG_CODE_EDIT] = sizeof(struct mgmt_msg_edit),
+ [MGMT_MSG_CODE_EDIT_REPLY] = sizeof(struct mgmt_msg_edit_reply),
+ [MGMT_MSG_CODE_RPC] = sizeof(struct mgmt_msg_rpc),
+ [MGMT_MSG_CODE_RPC_REPLY] = sizeof(struct mgmt_msg_rpc_reply),
+ [MGMT_MSG_CODE_NOTIFY_SELECT] = sizeof(struct mgmt_msg_notify_select),
+ [MGMT_MSG_CODE_SESSION_REQ] = sizeof(struct mgmt_msg_session_req),
+ [MGMT_MSG_CODE_SESSION_REPLY] = sizeof(struct mgmt_msg_session_reply),
+};
+size_t nmgmt_msg_min_sizes = sizeof(mgmt_msg_min_sizes) /
+ sizeof(*mgmt_msg_min_sizes);
+
+size_t mgmt_msg_get_min_size(uint code)
+{
+ if (code >= nmgmt_msg_min_sizes)
+ return 0;
+ return mgmt_msg_min_sizes[code];
+}
int vmgmt_msg_native_send_error(struct msg_conn *conn, uint64_t sess_or_txn_id,
uint64_t req_id, bool short_circuit_ok,
@@ -50,3 +78,20 @@ int vmgmt_msg_native_send_error(struct msg_conn *conn, uint64_t sess_or_txn_id,
mgmt_msg_native_free_msg(msg);
return ret;
}
+
+const char **_mgmt_msg_native_strings_decode(const void *_sdata, int sdlen)
+{
+ const char *sdata = _sdata;
+ const char **strings = NULL;
+ int len;
+
+ if (sdata[sdlen - 1] != 0)
+ return NULL;
+
+ for (; sdlen; sdata += len, sdlen -= len) {
+ *darr_append(strings) = darr_strdup(sdata);
+ len = 1 + darr_strlen(strings[darr_lasti(strings)]);
+ }
+
+ return strings;
+}
diff --git a/lib/mgmt_msg_native.h b/lib/mgmt_msg_native.h
index 21f702cc61..76a52658cd 100644
--- a/lib/mgmt_msg_native.h
+++ b/lib/mgmt_msg_native.h
@@ -163,6 +163,8 @@ DECLARE_MTYPE(MSG_NATIVE_EDIT);
DECLARE_MTYPE(MSG_NATIVE_EDIT_REPLY);
DECLARE_MTYPE(MSG_NATIVE_RPC);
DECLARE_MTYPE(MSG_NATIVE_RPC_REPLY);
+DECLARE_MTYPE(MSG_NATIVE_SESSION_REQ);
+DECLARE_MTYPE(MSG_NATIVE_SESSION_REPLY);
/*
* Native message codes
@@ -176,6 +178,9 @@ DECLARE_MTYPE(MSG_NATIVE_RPC_REPLY);
#define MGMT_MSG_CODE_EDIT_REPLY 6 /* Public API */
#define MGMT_MSG_CODE_RPC 7 /* Public API */
#define MGMT_MSG_CODE_RPC_REPLY 8 /* Public API */
+#define MGMT_MSG_CODE_NOTIFY_SELECT 9 /* Public API */
+#define MGMT_MSG_CODE_SESSION_REQ 10 /* Public API */
+#define MGMT_MSG_CODE_SESSION_REPLY 11 /* Public API */
/*
* Datastores
@@ -426,12 +431,72 @@ _Static_assert(sizeof(struct mgmt_msg_rpc_reply) ==
offsetof(struct mgmt_msg_rpc_reply, data),
"Size mismatch");
+/**
+ * struct mgmt_msg_notify_select - Add notification selectors for FE client.
+ *
+ * Add xpath prefix notification selectors to limit the notifications sent
+ * to the front-end client.
+ *
+ * @selectors: the xpath prefixes to selectors notifications through.
+ * @replace: if true replace existing selectors with `selectors`.
+ */
+struct mgmt_msg_notify_select {
+ struct mgmt_msg_header;
+ uint8_t replace;
+ uint8_t resv2[7];
+
+ alignas(8) char selectors[];
+};
+
+_Static_assert(sizeof(struct mgmt_msg_notify_select) ==
+ offsetof(struct mgmt_msg_notify_select, selectors),
+ "Size mismatch");
+
+/**
+ * struct mgmt_msg_session_req - Create or delete a front-end session.
+ *
+ * @refer_id: Zero for create, otherwise the session-id to delete.
+ * @req_id: For create will use as client-id.
+ * @client_name: For first session request the client name, otherwise empty.
+ */
+struct mgmt_msg_session_req {
+ struct mgmt_msg_header;
+ uint8_t resv2[8]; /* bug in compiler produces error w/o this */
+
+ alignas(8) char client_name[];
+};
+
+_Static_assert(sizeof(struct mgmt_msg_session_req) ==
+ offsetof(struct mgmt_msg_session_req, client_name),
+ "Size mismatch");
+
+/**
+ * struct mgmt_msg_session_reply - Reply to session request message.
+ *
+ * @created: true if this is a reply to a create request, otherwise 0.
+ * @refer_id: The session-id for the action (create or delete) just taken.
+ */
+struct mgmt_msg_session_reply {
+ struct mgmt_msg_header;
+ uint8_t created;
+ uint8_t resv2[7];
+};
+
/*
* Validate that the message ends in a NUL terminating byte
*/
#define MGMT_MSG_VALIDATE_NUL_TERM(msgp, len) \
((len) >= sizeof(*msgp) + 1 && ((char *)msgp)[(len)-1] == 0)
+/**
+ * mgmt_msg_get_min_size() - Get minimum message size given the type
+ * @code: The type of the message (MGMT_MSG_CODE_*)
+ *
+ * Return:
+ * The minimum size of a message of the given type or 0 if the message
+ * code is unknown.
+ */
+size_t mgmt_msg_get_min_size(uint code);
/**
* Send a native message error to the other end of the connection.
@@ -525,6 +590,25 @@ extern int vmgmt_msg_native_send_error(struct msg_conn *conn,
})
/**
+ * mgmt_msg_native_add_str() - Append [another] string to the msg.
+ * @msg: (IN/OUT) Pointer to the native message, variable may be updated.
+ * @s: string to append.
+ *
+ * Append string @s to the native message @msg. @msg is assumed to have a
+ * sequence of NUL-terminated strings at the end of it. This function appends
+ * the string @s and it's NUL terminating octet to the message.
+ *
+ * NOTE: Be aware @msg pointer may change as a result of reallocating the
+ * message to fit the new data. Any other pointers into the old message should
+ * be discarded.
+ */
+#define mgmt_msg_native_add_str(msg, s) \
+ do { \
+ int __len = strlen(s) + 1; \
+ mgmt_msg_native_append(msg, s, __len); \
+ } while (0)
+
+/**
* mgmt_msg_native_send_msg(msg, short_circuit_ok) - Send a native msg.
* @conn: the mgmt_msg connection.
* @msg: the native message.
@@ -689,6 +773,27 @@ extern int vmgmt_msg_native_send_error(struct msg_conn *conn,
#define mgmt_msg_native_data_len_decode(msg, msglen) \
((msglen) - sizeof(*msg) - msg->vsplit)
+/**
+ * mgmt_msg_native_strings_decode() - Get dynamic array of str ptrs from the msg.
+ * @msg: Pointer to the native message.
+ * @msglen: Length of the message.
+ * @sdata: pointer to the variable length string data at end of @msg.
+ *
+ * Given a pointer to a sequence of NUL-terminated strings allocate
+ * and return a dynamic array of dynamic array strings. This function
+ * can be used to decode a message that was built using
+ * mgmt_msg_native_add_str().
+ *
+ * Return: a dynamic array (darr) of string pointers, or NULL if the message
+ * is corrupt.
+ */
+#define mgmt_msg_native_strings_decode(msg, msg_len, sdata) \
+ _mgmt_msg_native_strings_decode(sdata, \
+ (msg_len) - ((sdata) - (char *)(msg)))
+
+extern const char **_mgmt_msg_native_strings_decode(const void *sdata,
+ int sdlen);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/module.c b/lib/module.c
index af7d20c3da..9d178bb0e4 100644
--- a/lib/module.c
+++ b/lib/module.c
@@ -202,3 +202,15 @@ void frrmod_unload(struct frrmod_runtime *module)
{
}
#endif
+
+void frrmod_terminate(void)
+{
+ struct frrmod_runtime *rtinfo = frrmod_list;
+
+ while (rtinfo) {
+ XFREE(MTYPE_MODULE_LOADNAME, rtinfo->load_name);
+ XFREE(MTYPE_MODULE_LOADARGS, rtinfo->load_args);
+
+ rtinfo = rtinfo->next;
+ }
+}
diff --git a/lib/module.h b/lib/module.h
index cd2be474e7..1810b335f6 100644
--- a/lib/module.h
+++ b/lib/module.h
@@ -79,6 +79,7 @@ extern union _frrmod_runtime_u _frrmod_this_module;
extern struct frrmod_runtime *frrmod_list;
extern void frrmod_init(struct frrmod_runtime *modinfo);
+extern void frrmod_terminate(void);
extern struct frrmod_runtime *frrmod_load(const char *spec, const char *dir,
void (*pFerrlog)(const void *,
const char *),
diff --git a/lib/nexthop.c b/lib/nexthop.c
index d6c654a692..98b05295b9 100644
--- a/lib/nexthop.c
+++ b/lib/nexthop.c
@@ -139,7 +139,7 @@ static int _nexthop_source_cmp(const struct nexthop *nh1,
}
static int _nexthop_cmp_no_labels(const struct nexthop *next1,
- const struct nexthop *next2)
+ const struct nexthop *next2, bool use_weight)
{
int ret = 0;
@@ -155,11 +155,13 @@ static int _nexthop_cmp_no_labels(const struct nexthop *next1,
if (next1->type > next2->type)
return 1;
- if (next1->weight < next2->weight)
- return -1;
+ if (use_weight) {
+ if (next1->weight < next2->weight)
+ return -1;
- if (next1->weight > next2->weight)
- return 1;
+ if (next1->weight > next2->weight)
+ return 1;
+ }
switch (next1->type) {
case NEXTHOP_TYPE_IPV4:
@@ -227,11 +229,12 @@ done:
return ret;
}
-int nexthop_cmp(const struct nexthop *next1, const struct nexthop *next2)
+static int nexthop_cmp_internal(const struct nexthop *next1,
+ const struct nexthop *next2, bool use_weight)
{
int ret = 0;
- ret = _nexthop_cmp_no_labels(next1, next2);
+ ret = _nexthop_cmp_no_labels(next1, next2, use_weight);
if (ret != 0)
return ret;
@@ -244,6 +247,17 @@ int nexthop_cmp(const struct nexthop *next1, const struct nexthop *next2)
return ret;
}
+int nexthop_cmp(const struct nexthop *next1, const struct nexthop *next2)
+{
+ return nexthop_cmp_internal(next1, next2, true);
+}
+
+int nexthop_cmp_no_weight(const struct nexthop *next1,
+ const struct nexthop *next2)
+{
+ return nexthop_cmp_internal(next1, next2, false);
+}
+
/*
* More-limited comparison function used to detect duplicate
* nexthops. This is used in places where we don't need the full
@@ -441,7 +455,7 @@ bool nexthop_same_no_labels(const struct nexthop *nh1,
if (nh1 == nh2)
return true;
- if (_nexthop_cmp_no_labels(nh1, nh2) != 0)
+ if (_nexthop_cmp_no_labels(nh1, nh2, true) != 0)
return false;
return true;
@@ -1175,6 +1189,7 @@ void nexthop_json_helper(json_object *json_nexthop,
json_object *json_labels = NULL;
json_object *json_backups = NULL;
json_object *json_seg6local = NULL;
+ json_object *json_seg6local_context = NULL;
json_object *json_seg6 = NULL;
json_object *json_segs = NULL;
int i;
@@ -1340,8 +1355,16 @@ void nexthop_json_helper(json_object *json_nexthop,
seg6local_action2str(
nexthop->nh_srv6
->seg6local_action));
+ json_seg6local_context = json_object_new_object();
json_object_object_add(json_nexthop, "seg6local",
json_seg6local);
+
+ seg6local_context2json(&nexthop->nh_srv6->seg6local_ctx,
+ nexthop->nh_srv6->seg6local_action,
+ json_seg6local_context);
+ json_object_object_add(json_nexthop, "seg6localContext",
+ json_seg6local_context);
+
if (nexthop->nh_srv6->seg6_segs &&
nexthop->nh_srv6->seg6_segs->num_segs == 1) {
json_seg6 = json_object_new_object();
diff --git a/lib/nexthop.h b/lib/nexthop.h
index 457c81972e..02ea4d96f2 100644
--- a/lib/nexthop.h
+++ b/lib/nexthop.h
@@ -207,6 +207,8 @@ extern bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2);
extern bool nexthop_same_no_labels(const struct nexthop *nh1,
const struct nexthop *nh2);
extern int nexthop_cmp(const struct nexthop *nh1, const struct nexthop *nh2);
+extern int nexthop_cmp_no_weight(const struct nexthop *nh1,
+ const struct nexthop *nh2);
extern int nexthop_g_addr_cmp(enum nexthop_types_t type,
const union g_addr *addr1,
const union g_addr *addr2);
diff --git a/lib/northbound.h b/lib/northbound.h
index 34d17a587c..da5f5be1ee 100644
--- a/lib/northbound.h
+++ b/lib/northbound.h
@@ -799,8 +799,6 @@ DECLARE_HOOK(nb_notification_send, (const char *xpath, struct list *arguments),
(xpath, arguments));
DECLARE_HOOK(nb_notification_tree_send,
(const char *xpath, const struct lyd_node *tree), (xpath, tree));
-DECLARE_HOOK(nb_client_debug_config_write, (struct vty *vty), (vty));
-DECLARE_HOOK(nb_client_debug_set_all, (uint32_t flags, bool set), (flags, set));
/* Northbound debugging records */
extern struct debug nb_dbg_cbs_config;
diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c
index 4f962cda5c..f9794bee3c 100644
--- a/lib/northbound_cli.c
+++ b/lib/northbound_cli.c
@@ -22,13 +22,19 @@
#include "northbound_db.h"
#include "lib/northbound_cli_clippy.c"
-struct debug nb_dbg_cbs_config = {0, "Northbound callbacks: configuration"};
-struct debug nb_dbg_cbs_state = {0, "Northbound callbacks: state"};
-struct debug nb_dbg_cbs_rpc = {0, "Northbound callbacks: RPCs"};
-struct debug nb_dbg_cbs_notify = {0, "Northbound callbacks: notifications"};
-struct debug nb_dbg_notif = {0, "Northbound notifications"};
-struct debug nb_dbg_events = {0, "Northbound events"};
-struct debug nb_dbg_libyang = {0, "libyang debugging"};
+struct debug nb_dbg_cbs_config = { 0, "debug northbound callbacks configuration",
+ "Northbound callbacks: configuration" };
+struct debug nb_dbg_cbs_state = { 0, "debug northbound callbacks state",
+ "Northbound callbacks: state" };
+struct debug nb_dbg_cbs_rpc = { 0, "debug northbound callbacks rpc",
+ "Northbound callbacks: RPCs" };
+struct debug nb_dbg_cbs_notify = { 0, "debug northbound callbacks notify",
+ "Northbound callbacks: notifications" };
+struct debug nb_dbg_notif = { 0, "debug northbound notifications",
+ "Northbound notifications" };
+struct debug nb_dbg_events = { 0, "debug northbound events",
+ "Northbound events" };
+struct debug nb_dbg_libyang = { 0, "debug northbound libyang", "libyang" };
struct nb_config *vty_shared_candidate_config;
static struct event_loop *master;
@@ -1380,7 +1386,7 @@ static int nb_cli_show_transactions(struct vty *vty)
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
- XFREE(MTYPE_TMP, table);
+ XFREE(MTYPE_TMP_TTABLE, table);
} else
vty_out(vty, "No configuration transactions to display.\n\n");
@@ -1661,7 +1667,7 @@ DEFPY (show_yang_module,
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
- XFREE(MTYPE_TMP, table);
+ XFREE(MTYPE_TMP_TTABLE, table);
} else
vty_out(vty, "No YANG modules to display.\n\n");
@@ -1771,7 +1777,7 @@ DEFPY (show_yang_module_translator,
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
- XFREE(MTYPE_TMP, table);
+ XFREE(MTYPE_TMP_TTABLE, table);
} else
vty_out(vty, "No YANG module translators to display.\n\n");
@@ -1842,37 +1848,6 @@ DEFPY (rollback_config,
}
/* Debug CLI commands. */
-static struct debug *nb_debugs[] = {
- &nb_dbg_cbs_config, &nb_dbg_cbs_state, &nb_dbg_cbs_rpc,
- &nb_dbg_cbs_notify, &nb_dbg_notif, &nb_dbg_events,
- &nb_dbg_libyang,
-};
-
-static const char *const nb_debugs_conflines[] = {
- "debug northbound callbacks configuration",
- "debug northbound callbacks state",
- "debug northbound callbacks rpc",
- "debug northbound callbacks notify",
- "debug northbound notifications",
- "debug northbound events",
- "debug northbound libyang",
-};
-
-DEFINE_HOOK(nb_client_debug_set_all, (uint32_t flags, bool set), (flags, set));
-
-static void nb_debug_set_all(uint32_t flags, bool set)
-{
- for (unsigned int i = 0; i < array_size(nb_debugs); i++) {
- DEBUG_FLAGS_SET(nb_debugs[i], flags, set);
-
- /* If all modes have been turned off, don't preserve options. */
- if (!DEBUG_MODE_CHECK(nb_debugs[i], DEBUG_MODE_ALL))
- DEBUG_CLEAR(nb_debugs[i]);
- }
-
- hook_call(nb_client_debug_set_all, flags, set);
-}
-
DEFPY (debug_nb,
debug_nb_cmd,
"[no] debug northbound\
@@ -1895,8 +1870,13 @@ DEFPY (debug_nb,
"libyang debugging\n")
{
uint32_t mode = DEBUG_NODE2MODE(vty->node);
+ bool all = false;
+
+ /* no specific debug --> act on all of them */
+ if (strmatch(argv[argc - 1]->text, "northbound"))
+ all = true;
- if (cbs) {
+ if (cbs || all) {
bool none = (!cbs_cfg && !cbs_state && !cbs_rpc && !cbs_notify);
if (none || cbs_cfg)
@@ -1908,45 +1888,18 @@ DEFPY (debug_nb,
if (none || cbs_notify)
DEBUG_MODE_SET(&nb_dbg_cbs_notify, mode, !no);
}
- if (notifications)
+ if (notifications || all)
DEBUG_MODE_SET(&nb_dbg_notif, mode, !no);
- if (events)
+ if (events || all)
DEBUG_MODE_SET(&nb_dbg_events, mode, !no);
- if (libyang) {
+ if (libyang || all) {
DEBUG_MODE_SET(&nb_dbg_libyang, mode, !no);
yang_debugging_set(!no);
}
- /* no specific debug --> act on all of them */
- if (strmatch(argv[argc - 1]->text, "northbound")) {
- nb_debug_set_all(mode, !no);
- yang_debugging_set(!no);
- }
-
return CMD_SUCCESS;
}
-DEFINE_HOOK(nb_client_debug_config_write, (struct vty *vty), (vty));
-
-static int nb_debug_config_write(struct vty *vty)
-{
- for (unsigned int i = 0; i < array_size(nb_debugs); i++)
- if (DEBUG_MODE_CHECK(nb_debugs[i], DEBUG_MODE_CONF))
- vty_out(vty, "%s\n", nb_debugs_conflines[i]);
-
- hook_call(nb_client_debug_config_write, vty);
-
- return 1;
-}
-
-static struct debug_callbacks nb_dbg_cbs = {.debug_set_all = nb_debug_set_all};
-static struct cmd_node nb_debug_node = {
- .name = "northbound debug",
- .node = NORTHBOUND_DEBUG_NODE,
- .prompt = "",
- .config_write = nb_debug_config_write,
-};
-
void nb_cli_install_default(int node)
{
_install_element(node, &show_config_candidate_section_cmd);
@@ -2007,9 +1960,14 @@ void nb_cli_init(struct event_loop *tm)
/* Initialize the shared candidate configuration. */
vty_shared_candidate_config = nb_config_new(NULL);
- debug_init(&nb_dbg_cbs);
+ debug_install(&nb_dbg_cbs_config);
+ debug_install(&nb_dbg_cbs_state);
+ debug_install(&nb_dbg_cbs_rpc);
+ debug_install(&nb_dbg_cbs_notify);
+ debug_install(&nb_dbg_notif);
+ debug_install(&nb_dbg_events);
+ debug_install(&nb_dbg_libyang);
- install_node(&nb_debug_node);
install_element(ENABLE_NODE, &debug_nb_cmd);
install_element(CONFIG_NODE, &debug_nb_cmd);
diff --git a/lib/northbound_oper.c b/lib/northbound_oper.c
index 5f38c970c7..e95f99a2bd 100644
--- a/lib/northbound_oper.c
+++ b/lib/northbound_oper.c
@@ -751,8 +751,8 @@ static const struct lysc_node *nb_op_sib_next(struct nb_op_yield_state *ys,
/*
* If the node info stack is shorter than the schema path then we are
- * doign specific query still on the node from the schema path (should
- * match) so just return NULL (i.e., don't process siblings)
+ * working our way down the specific query path so just return NULL
+ * (i.e., don't process siblings)
*/
if (darr_len(ys->schema_path) > darr_len(ys->node_infos))
return NULL;
@@ -760,21 +760,21 @@ static const struct lysc_node *nb_op_sib_next(struct nb_op_yield_state *ys,
* If sib is on top of the node info stack then
* 1) it's a container node -or-
* 2) it's a list node that we were walking and we've reach the last entry
- * 3) if sib is a list and the list was empty we never would have
+ *
+ * If sib is a list and the list was empty we never would have
* pushed sib on the stack so the top of the stack is the parent
*
* If the query string included this node then we do not process any
* siblings as we are not walking all the parent's children just this
* specified one give by the query string.
*/
- if (sib == darr_last(ys->node_infos)->schema &&
- darr_len(ys->schema_path) >= darr_len(ys->node_infos))
- return NULL;
- /* case (3) */
- else if (sib->nodetype == LYS_LIST &&
- parent == darr_last(ys->node_infos)->schema &&
- darr_len(ys->schema_path) > darr_len(ys->node_infos))
- return NULL;
+ if (darr_len(ys->schema_path) == darr_len(ys->node_infos)) {
+ struct nb_op_node_info *node_infos = darr_last(ys->node_infos);
+
+ assert(node_infos);
+ if (sib == node_infos->schema)
+ return NULL;
+ }
sib = __sib_next(yn, sib->next);
if (sib)
@@ -801,6 +801,7 @@ static const struct lysc_node *nb_op_sib_first(struct nb_op_yield_state *ys,
{
const struct lysc_node *sib = lysc_node_child(parent);
const struct lysc_node *first_sib;
+ struct nb_op_node_info *last = darr_last(ys->node_infos);
/*
* NOTE: when we want to handle root level walks we will need to use
@@ -817,10 +818,9 @@ static const struct lysc_node *nb_op_sib_first(struct nb_op_yield_state *ys,
* base of the user query, return the next schema node from the query
* string (schema_path).
*/
- if (darr_last(ys->node_infos) != NULL &&
- !CHECK_FLAG(darr_last(ys->node_infos)->schema->nodetype,
- LYS_CASE | LYS_CHOICE))
- assert(darr_last(ys->node_infos)->schema == parent);
+ if (last != NULL &&
+ !CHECK_FLAG(last->schema->nodetype, LYS_CASE | LYS_CHOICE))
+ assert(last->schema == parent);
if (darr_lasti(ys->node_infos) < ys->query_base_level)
return ys->schema_path[darr_lasti(ys->node_infos) + 1];
@@ -908,9 +908,10 @@ static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume)
* Otherwise get the first child of the container we are walking,
* starting with non-yielding children.
*/
- if (is_resume)
+ if (is_resume) {
+ assert(darr_last(ys->node_infos) != NULL);
sib = darr_last(ys->node_infos)->schema;
- else {
+ } else {
/*
* Start with non-yielding children first.
*
@@ -1477,7 +1478,8 @@ static void nb_op_walk_continue(struct event *thread)
goto finish;
/* otherwise we are at a resumable node */
- assert(darr_last(ys->node_infos)->has_lookup_next);
+ assert(darr_last(ys->node_infos) &&
+ darr_last(ys->node_infos)->has_lookup_next);
ret = __walk(ys, true);
if (ret == NB_YIELD) {
diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c
index 0ec7610a9a..1f4d036cc2 100644
--- a/lib/northbound_sysrepo.c
+++ b/lib/northbound_sysrepo.c
@@ -19,7 +19,9 @@
#include <sysrepo/values.h>
#include <sysrepo/xpath.h>
-static struct debug nb_dbg_client_sysrepo = {0, "Northbound client: Sysrepo"};
+static struct debug nb_dbg_client_sysrepo = { 0,
+ "debug northbound client sysrepo",
+ "Northbound client: Sysrepo" };
static struct event_loop *master;
static sr_session_ctx_t *session;
@@ -553,29 +555,9 @@ DEFUN (debug_nb_sr,
return CMD_SUCCESS;
}
-static int frr_sr_debug_config_write(struct vty *vty)
-{
- if (DEBUG_MODE_CHECK(&nb_dbg_client_sysrepo, DEBUG_MODE_CONF))
- vty_out(vty, "debug northbound client sysrepo\n");
-
- return 0;
-}
-
-static int frr_sr_debug_set_all(uint32_t flags, bool set)
-{
- DEBUG_FLAGS_SET(&nb_dbg_client_sysrepo, flags, set);
-
- /* If all modes have been turned off, don't preserve options. */
- if (!DEBUG_MODE_CHECK(&nb_dbg_client_sysrepo, DEBUG_MODE_ALL))
- DEBUG_CLEAR(&nb_dbg_client_sysrepo);
-
- return 0;
-}
-
static void frr_sr_cli_init(void)
{
- hook_register(nb_client_debug_config_write, frr_sr_debug_config_write);
- hook_register(nb_client_debug_set_all, frr_sr_debug_set_all);
+ debug_install(&nb_dbg_client_sysrepo);
install_element(ENABLE_NODE, &debug_nb_sr_cmd);
install_element(CONFIG_NODE, &debug_nb_sr_cmd);
diff --git a/lib/openbsd-queue.h b/lib/openbsd-queue.h
index df3bbd720f..a2df521f58 100644
--- a/lib/openbsd-queue.h
+++ b/lib/openbsd-queue.h
@@ -17,6 +17,22 @@ extern "C" {
#endif
/*
+ * NOTICE:
+ *
+ * If you are reading this file in an effort to add a new queue structure
+ * this is the wrong place to be using it. Please see the typesafe
+ * data structures, or ask one of the other developers.
+ *
+ * If you are reading this file as a way to update an existing usage
+ * of this data structure, please consider just converting the data
+ * structure to one of the typesafe data structures instead. However,
+ * among converting datastrucutres, the the BSD ones are the lowest
+ * priority / should be converted last. They are already typesafe and
+ * use inline linking nodes, so the only gain is consistency. Please
+ * convert uses of linklist.h and hash.h first.
+ */
+
+/*
* This file defines five types of data structures: singly-linked lists,
* lists, simple queues, tail queues and XOR simple queues.
*
diff --git a/lib/openbsd-tree.h b/lib/openbsd-tree.h
index 4f3985bbca..ecc3a68f15 100644
--- a/lib/openbsd-tree.h
+++ b/lib/openbsd-tree.h
@@ -10,6 +10,21 @@
#ifdef __cplusplus
extern "C" {
#endif
+/*
+ * NOTICE:
+ *
+ * If you are reading this file in an effort to add a new tree structure
+ * this is the wrong place to be using it. Please see the typesafe
+ * data structures, or ask one of the other developers.
+ *
+ * If you are reading this file as a way to update an existing usage
+ * of this data structure, please consider just converting the data
+ * structure to one of the typesafe data structures instead. However,
+ * among converting datastrucutres, the the BSD ones are the lowest
+ * priority / should be converted last. They are already typesafe and
+ * use inline linking nodes, so the only gain is consistency. Please
+ * convert uses of linklist.h and hash.h first.
+ */
/*
* This file defines data structures for different types of trees:
diff --git a/lib/prefix.c b/lib/prefix.c
index f342c4c1db..2485c3e61b 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -1124,6 +1124,15 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size)
return str;
}
+void prefix_mcast_ip_dump(const char *onfail, const struct ipaddr *addr,
+ char *buf, int buf_size)
+{
+ if (ipaddr_is_zero(addr))
+ strlcpy(buf, "*", buf_size);
+ else
+ (void)snprintfrr(buf, buf_size, "%pIA", addr);
+}
+
static ssize_t prefixhost2str(struct fbuf *fbuf, union prefixconstptr pu)
{
const struct prefix *p = pu.p;
@@ -1166,7 +1175,7 @@ const char *prefix_sg2str(const struct prefix_sg *sg, char *sg_str)
char src_str[INET_ADDRSTRLEN];
char grp_str[INET_ADDRSTRLEN];
- prefix_mcast_inet4_dump("<src?>", sg->src, src_str, sizeof(src_str));
+ prefix_mcast_ip_dump("<src?>", &sg->src, src_str, sizeof(src_str));
prefix_mcast_inet4_dump("<grp?>", sg->grp, grp_str, sizeof(grp_str));
snprintf(sg_str, PREFIX_SG_STR_LEN, "(%s,%s)", src_str, grp_str);
@@ -1637,10 +1646,10 @@ static ssize_t printfrr_psg(struct fbuf *buf, struct printfrr_eargs *ea,
if (!sg)
return bputs(buf, "(null)");
- if (sg->src.s_addr == INADDR_ANY)
+ if (ipaddr_is_zero(&sg->src))
ret += bputs(buf, "(*,");
else
- ret += bprintfrr(buf, "(%pI4,", &sg->src);
+ ret += bprintfrr(buf, "(%pIA,", &sg->src);
if (sg->grp.s_addr == INADDR_ANY)
ret += bputs(buf, "*)");
diff --git a/lib/prefix.h b/lib/prefix.h
index 14f2695933..2d679d0622 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -52,10 +52,10 @@ typedef enum {
/* Maximum number of VTEPs per-ES -
* XXX - temporary limit for allocating strings etc.
*/
-#define ES_VTEP_MAX_CNT 10
-#define ES_VTEP_LIST_STR_SZ (ES_VTEP_MAX_CNT * 16)
+#define ES_VTEP_MAX_CNT 10
+#define ES_VTEP_LIST_STR_SZ (ES_VTEP_MAX_CNT * IPADDR_STRING_SIZE)
-#define ETHER_ADDR_STRLEN (3*ETH_ALEN)
+#define ETHER_ADDR_STRLEN (3 * ETH_ALEN)
/*
* there isn't a portable ethernet address type. We define our
* own to simplify internal handling
@@ -282,7 +282,7 @@ struct prefix_fs {
struct prefix_sg {
uint8_t family;
uint16_t prefixlen;
- struct in_addr src __attribute__((aligned(8)));
+ struct ipaddr src __attribute__((aligned(8)));
struct in_addr grp;
};
@@ -415,6 +415,8 @@ extern int str2prefix(const char *string, struct prefix *prefix);
#define PREFIX2STR_BUFFER PREFIX_STRLEN
+extern void prefix_mcast_ip_dump(const char *onfail, const struct ipaddr *addr,
+ char *buf, int buf_size);
extern void prefix_mcast_inet4_dump(const char *onfail, struct in_addr addr,
char *buf, int buf_size);
extern const char *prefix_sg2str(const struct prefix_sg *sg, char *str);
diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c
index 8e2e497e09..f22d588080 100644
--- a/lib/routemap_cli.c
+++ b/lib/routemap_cli.c
@@ -937,7 +937,7 @@ DEFPY_YANG(no_set_min_metric, no_set_min_metric_cmd,
"no set min-metric [(0-4294967295)]",
NO_STR SET_STR
"Minimum metric value for destination routing protocol\n"
- "Minumum metric value\n")
+ "Minimum metric value\n")
{
const char *xpath =
"./set-action[action='frr-route-map:set-min-metric']";
diff --git a/lib/seqlock.c b/lib/seqlock.c
index 62ce316920..e74e6718bf 100644
--- a/lib/seqlock.c
+++ b/lib/seqlock.c
@@ -26,6 +26,39 @@
* OS specific synchronization wrappers *
****************************************/
+#ifndef __has_feature /* not available on old GCC */
+#define __has_feature(x) 0
+#endif
+
+#if (defined(__SANITIZE_THREAD__) || __has_feature(thread_sanitizer))
+/* TSAN really does not understand what is going on with the low-level
+ * futex/umtx calls. This leads to a whole bunch of warnings, a lot of which
+ * also have _extremely_ misleading text - since TSAN does not understand that
+ * there is in fact a synchronization primitive involved, it can end up pulling
+ * in completely unrelated things.
+ *
+ * What does work is the "unsupported platform" seqlock implementation based
+ * on a pthread mutex + condvar, since TSAN of course suppports these.
+ *
+ * It may be possible to also fix this with TSAN annotations (__tsan_acquire
+ * and __tsan_release), but using those (correctly) is not easy either, and
+ * for now just get things rolling.
+ */
+
+#ifdef HAVE_SYNC_LINUX_FUTEX
+#undef HAVE_SYNC_LINUX_FUTEX
+#endif
+
+#ifdef HAVE_SYNC_OPENBSD_FUTEX
+#undef HAVE_SYNC_OPENBSD_FUTEX
+#endif
+
+#ifdef HAVE_SYNC_UMTX_OP
+#undef HAVE_SYNC_UMTX_OP
+#endif
+
+#endif /* TSAN */
+
/*
* Linux: sys_futex()
*/
diff --git a/lib/smux.h b/lib/smux.h
index 8ec847afd0..0ed41410f5 100644
--- a/lib/smux.h
+++ b/lib/smux.h
@@ -101,6 +101,7 @@ extern bool smux_enabled(void);
extern void libagentx_init(void);
extern void smux_init(struct event_loop *tm);
+extern void smux_terminate(void);
extern void smux_agentx_enable(void);
extern void smux_register_mib(const char *, struct variable *, size_t, int,
oid[], size_t);
diff --git a/lib/sockopt.h b/lib/sockopt.h
index ce7d665fab..e6fb78d5e4 100644
--- a/lib/sockopt.h
+++ b/lib/sockopt.h
@@ -60,6 +60,16 @@ extern int setsockopt_ipv6_tclass(int, int);
(((af) == AF_INET) : SOPT_SIZE_CMSG_IFINDEX_IPV4() \
? SOPT_SIZE_CMSG_PKTINFO_IPV6())
+/*
+ * If not defined then define the value for `TCP_MD5SIG_MAXKEYLEN`. This seems
+ * to be unavailable for NetBSD 8, FreeBSD 11 and FreeBSD 12.
+ *
+ * The value below was copied from `linux/tcp.h` from the Linux kernel headers.
+ */
+#ifndef TCP_MD5SIG_MAXKEYLEN
+#define TCP_MD5SIG_MAXKEYLEN 80
+#endif
+
extern int setsockopt_ipv4_multicast_if(int sock, struct in_addr if_addr,
ifindex_t ifindex);
extern int setsockopt_ipv4_multicast(int sock, int optname,
diff --git a/lib/srv6.c b/lib/srv6.c
index a82103e423..e6fc375fbb 100644
--- a/lib/srv6.c
+++ b/lib/srv6.c
@@ -10,8 +10,11 @@
#include "log.h"
DEFINE_QOBJ_TYPE(srv6_locator);
+DEFINE_QOBJ_TYPE(srv6_sid_format);
DEFINE_MTYPE_STATIC(LIB, SRV6_LOCATOR, "SRV6 locator");
DEFINE_MTYPE_STATIC(LIB, SRV6_LOCATOR_CHUNK, "SRV6 locator chunk");
+DEFINE_MTYPE_STATIC(LIB, SRV6_SID_FORMAT, "SRv6 SID format");
+DEFINE_MTYPE_STATIC(LIB, SRV6_SID_CTX, "SRv6 SID context");
const char *seg6local_action2str(uint32_t action)
{
@@ -68,6 +71,44 @@ int snprintf_seg6_segs(char *str,
return strlen(str);
}
+void seg6local_context2json(const struct seg6local_context *ctx,
+ uint32_t action, json_object *json)
+{
+ switch (action) {
+ case ZEBRA_SEG6_LOCAL_ACTION_END:
+ json_object_boolean_add(json, "USP", true);
+ return;
+ case ZEBRA_SEG6_LOCAL_ACTION_END_X:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DX6:
+ json_object_string_addf(json, "nh6", "%pI6", &ctx->nh6);
+ return;
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DX4:
+ json_object_string_addf(json, "nh4", "%pI4", &ctx->nh4);
+ return;
+ case ZEBRA_SEG6_LOCAL_ACTION_END_T:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DT6:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DT4:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DT46:
+ json_object_int_add(json, "table", ctx->table);
+ return;
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DX2:
+ json_object_boolean_add(json, "none", true);
+ return;
+ case ZEBRA_SEG6_LOCAL_ACTION_END_B6:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_B6_ENCAP:
+ json_object_string_addf(json, "nh6", "%pI6", &ctx->nh6);
+ return;
+ case ZEBRA_SEG6_LOCAL_ACTION_END_BM:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_S:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_AS:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_AM:
+ case ZEBRA_SEG6_LOCAL_ACTION_UNSPEC:
+ default:
+ json_object_boolean_add(json, "unknown", true);
+ return;
+ }
+}
+
const char *seg6local_context2str(char *str, size_t size,
const struct seg6local_context *ctx,
uint32_t action)
@@ -139,6 +180,21 @@ struct srv6_locator_chunk *srv6_locator_chunk_alloc(void)
return chunk;
}
+void srv6_locator_copy(struct srv6_locator *copy,
+ const struct srv6_locator *locator)
+{
+ strlcpy(copy->name, locator->name, sizeof(locator->name));
+ copy->prefix = locator->prefix;
+ copy->block_bits_length = locator->block_bits_length;
+ copy->node_bits_length = locator->node_bits_length;
+ copy->function_bits_length = locator->function_bits_length;
+ copy->argument_bits_length = locator->argument_bits_length;
+ copy->algonum = locator->algonum;
+ copy->current = locator->current;
+ copy->status_up = locator->status_up;
+ copy->flags = locator->flags;
+}
+
void srv6_locator_free(struct srv6_locator *locator)
{
if (locator) {
@@ -154,6 +210,59 @@ void srv6_locator_chunk_free(struct srv6_locator_chunk **chunk)
XFREE(MTYPE_SRV6_LOCATOR_CHUNK, *chunk);
}
+struct srv6_sid_format *srv6_sid_format_alloc(const char *name)
+{
+ struct srv6_sid_format *format = NULL;
+
+ format = XCALLOC(MTYPE_SRV6_SID_FORMAT, sizeof(struct srv6_sid_format));
+ strlcpy(format->name, name, sizeof(format->name));
+
+ QOBJ_REG(format, srv6_sid_format);
+ return format;
+}
+
+void srv6_sid_format_free(struct srv6_sid_format *format)
+{
+ if (!format)
+ return;
+
+ QOBJ_UNREG(format);
+ XFREE(MTYPE_SRV6_SID_FORMAT, format);
+}
+
+/**
+ * Free an SRv6 SID format.
+ *
+ * @param val SRv6 SID format to be freed
+ */
+void delete_srv6_sid_format(void *val)
+{
+ srv6_sid_format_free((struct srv6_sid_format *)val);
+}
+
+struct srv6_sid_ctx *srv6_sid_ctx_alloc(enum seg6local_action_t behavior,
+ struct in_addr *nh4,
+ struct in6_addr *nh6, vrf_id_t vrf_id)
+{
+ struct srv6_sid_ctx *ctx = NULL;
+
+ ctx = XCALLOC(MTYPE_SRV6_SID_CTX, sizeof(struct srv6_sid_ctx));
+ ctx->behavior = behavior;
+ if (nh4)
+ ctx->nh4 = *nh4;
+ if (nh6)
+ ctx->nh6 = *nh6;
+ if (vrf_id)
+ ctx->vrf_id = vrf_id;
+
+ return ctx;
+}
+
+void srv6_sid_ctx_free(struct srv6_sid_ctx *ctx)
+{
+ XFREE(MTYPE_SRV6_SID_CTX, ctx);
+}
+
json_object *srv6_locator_chunk_json(const struct srv6_locator_chunk *chunk)
{
json_object *jo_root = NULL;
@@ -223,23 +332,47 @@ json_object *srv6_locator_json(const struct srv6_locator *loc)
/* set prefix */
json_object_string_addf(jo_root, "prefix", "%pFX", &loc->prefix);
- /* set block_bits_length */
- json_object_int_add(jo_root, "blockBitsLength", loc->block_bits_length);
-
- /* set node_bits_length */
- json_object_int_add(jo_root, "nodeBitsLength", loc->node_bits_length);
-
- /* set function_bits_length */
- json_object_int_add(jo_root, "functionBitsLength",
- loc->function_bits_length);
-
- /* set argument_bits_length */
- json_object_int_add(jo_root, "argumentBitsLength",
- loc->argument_bits_length);
-
- /* set true if the locator is a Micro-segment (uSID) locator */
- if (CHECK_FLAG(loc->flags, SRV6_LOCATOR_USID))
- json_object_string_add(jo_root, "behavior", "usid");
+ if (loc->sid_format) {
+ /* set block_bits_length */
+ json_object_int_add(jo_root, "blockBitsLength",
+ loc->sid_format->block_len);
+
+ /* set node_bits_length */
+ json_object_int_add(jo_root, "nodeBitsLength",
+ loc->sid_format->node_len);
+
+ /* set function_bits_length */
+ json_object_int_add(jo_root, "functionBitsLength",
+ loc->sid_format->function_len);
+
+ /* set argument_bits_length */
+ json_object_int_add(jo_root, "argumentBitsLength",
+ loc->sid_format->argument_len);
+
+ /* set true if the locator is a Micro-segment (uSID) locator */
+ if (loc->sid_format->type == SRV6_SID_FORMAT_TYPE_USID)
+ json_object_string_add(jo_root, "behavior", "usid");
+ } else {
+ /* set block_bits_length */
+ json_object_int_add(jo_root, "blockBitsLength",
+ loc->block_bits_length);
+
+ /* set node_bits_length */
+ json_object_int_add(jo_root, "nodeBitsLength",
+ loc->node_bits_length);
+
+ /* set function_bits_length */
+ json_object_int_add(jo_root, "functionBitsLength",
+ loc->function_bits_length);
+
+ /* set argument_bits_length */
+ json_object_int_add(jo_root, "argumentBitsLength",
+ loc->argument_bits_length);
+
+ /* set true if the locator is a Micro-segment (uSID) locator */
+ if (CHECK_FLAG(loc->flags, SRV6_LOCATOR_USID))
+ json_object_string_add(jo_root, "behavior", "usid");
+ }
/* set status_up */
json_object_boolean_add(jo_root, "statusUp",
@@ -272,23 +405,47 @@ json_object *srv6_locator_detailed_json(const struct srv6_locator *loc)
/* set prefix */
json_object_string_addf(jo_root, "prefix", "%pFX", &loc->prefix);
- /* set block_bits_length */
- json_object_int_add(jo_root, "blockBitsLength", loc->block_bits_length);
-
- /* set node_bits_length */
- json_object_int_add(jo_root, "nodeBitsLength", loc->node_bits_length);
-
- /* set function_bits_length */
- json_object_int_add(jo_root, "functionBitsLength",
- loc->function_bits_length);
-
- /* set argument_bits_length */
- json_object_int_add(jo_root, "argumentBitsLength",
- loc->argument_bits_length);
-
- /* set true if the locator is a Micro-segment (uSID) locator */
- if (CHECK_FLAG(loc->flags, SRV6_LOCATOR_USID))
- json_object_string_add(jo_root, "behavior", "usid");
+ if (loc->sid_format) {
+ /* set block_bits_length */
+ json_object_int_add(jo_root, "blockBitsLength",
+ loc->sid_format->block_len);
+
+ /* set node_bits_length */
+ json_object_int_add(jo_root, "nodeBitsLength",
+ loc->sid_format->node_len);
+
+ /* set function_bits_length */
+ json_object_int_add(jo_root, "functionBitsLength",
+ loc->sid_format->function_len);
+
+ /* set argument_bits_length */
+ json_object_int_add(jo_root, "argumentBitsLength",
+ loc->sid_format->argument_len);
+
+ /* set true if the locator is a Micro-segment (uSID) locator */
+ if (loc->sid_format->type == SRV6_SID_FORMAT_TYPE_USID)
+ json_object_string_add(jo_root, "behavior", "usid");
+ } else {
+ /* set block_bits_length */
+ json_object_int_add(jo_root, "blockBitsLength",
+ loc->block_bits_length);
+
+ /* set node_bits_length */
+ json_object_int_add(jo_root, "nodeBitsLength",
+ loc->node_bits_length);
+
+ /* set function_bits_length */
+ json_object_int_add(jo_root, "functionBitsLength",
+ loc->function_bits_length);
+
+ /* set argument_bits_length */
+ json_object_int_add(jo_root, "argumentBitsLength",
+ loc->argument_bits_length);
+
+ /* set true if the locator is a Micro-segment (uSID) locator */
+ if (CHECK_FLAG(loc->flags, SRV6_LOCATOR_USID))
+ json_object_string_add(jo_root, "behavior", "usid");
+ }
/* set algonum */
json_object_int_add(jo_root, "algoNum", loc->algonum);
diff --git a/lib/srv6.h b/lib/srv6.h
index 433c5c14fd..f25c5cfcaa 100644
--- a/lib/srv6.h
+++ b/lib/srv6.h
@@ -20,6 +20,8 @@
#define SRH_BASE_HEADER_LENGTH 8
#define SRH_SEGMENT_LENGTH 16
+#define SRV6_SID_FORMAT_NAME_SIZE 512
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -127,6 +129,12 @@ struct srv6_locator {
uint8_t flags;
#define SRV6_LOCATOR_USID (1 << 0) /* The SRv6 Locator is a uSID Locator */
+ /* Pointer to the SID format. */
+ struct srv6_sid_format *sid_format;
+
+ /* Pointer to the parent SID block of the locator. */
+ void *sid_block;
+
QOBJ_FIELDS;
};
DECLARE_QOBJ_TYPE(srv6_locator);
@@ -183,6 +191,72 @@ struct nexthop_srv6 {
struct seg6_seg_stack *seg6_segs;
};
+/* SID format type */
+enum srv6_sid_format_type {
+ SRV6_SID_FORMAT_TYPE_UNSPEC = 0,
+ /* SRv6 SID uncompressed format */
+ SRV6_SID_FORMAT_TYPE_UNCOMPRESSED = 1,
+ /* SRv6 SID compressed uSID format */
+ SRV6_SID_FORMAT_TYPE_USID = 2,
+};
+
+/* SRv6 SID format */
+struct srv6_sid_format {
+ /* Name of the format */
+ char name[SRV6_SID_FORMAT_NAME_SIZE];
+
+ /* Format type: uncompressed vs compressed */
+ enum srv6_sid_format_type type;
+
+ /*
+ * Lengths of block/node/function/argument parts of the SIDs allocated
+ * using this format
+ */
+ uint8_t block_len;
+ uint8_t node_len;
+ uint8_t function_len;
+ uint8_t argument_len;
+
+ union {
+ /* Configuration settings for compressed uSID format type */
+ struct {
+ /* Start of the Local ID Block (LIB) range */
+ uint32_t lib_start;
+
+ /* Start/End of the Explicit LIB range */
+ uint32_t elib_start;
+ uint32_t elib_end;
+
+ /* Start/End of the Wide LIB range */
+ uint32_t wlib_start;
+ uint32_t wlib_end;
+
+ /* Start/End of the Explicit Wide LIB range */
+ uint32_t ewlib_start;
+ } usid;
+
+ /* Configuration settings for uncompressed format type */
+ struct {
+ /* Start of the Explicit range */
+ uint32_t explicit_start;
+ } uncompressed;
+ } config;
+
+ QOBJ_FIELDS;
+};
+DECLARE_QOBJ_TYPE(srv6_sid_format);
+
+/* Context for an SRv6 SID */
+struct srv6_sid_ctx {
+ /* Behavior associated with the SID */
+ enum seg6local_action_t behavior;
+
+ /* Behavior-specific attributes */
+ struct in_addr nh4;
+ struct in6_addr nh6;
+ vrf_id_t vrf_id;
+};
+
static inline const char *seg6_mode2str(enum seg6_mode_t mode)
{
switch (mode) {
@@ -245,6 +319,55 @@ seg6local_action2str(uint32_t action);
const char *seg6local_context2str(char *str, size_t size,
const struct seg6local_context *ctx,
uint32_t action);
+void seg6local_context2json(const struct seg6local_context *ctx,
+ uint32_t action, json_object *json);
+
+static inline const char *srv6_sid_ctx2str(char *str, size_t size,
+ const struct srv6_sid_ctx *ctx)
+{
+ int len = 0;
+
+ len += snprintf(str + len, size - len, "%s",
+ seg6local_action2str(ctx->behavior));
+
+ switch (ctx->behavior) {
+ case ZEBRA_SEG6_LOCAL_ACTION_UNSPEC:
+ break;
+
+ case ZEBRA_SEG6_LOCAL_ACTION_END:
+ snprintf(str + len, size - len, " USP");
+ break;
+
+ case ZEBRA_SEG6_LOCAL_ACTION_END_X:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DX6:
+ snprintfrr(str + len, size - len, " nh6 %pI6", &ctx->nh6);
+ break;
+
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DX4:
+ snprintfrr(str + len, size - len, " nh4 %pI4", &ctx->nh4);
+ break;
+
+ case ZEBRA_SEG6_LOCAL_ACTION_END_T:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DT6:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DT4:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DT46:
+ snprintf(str + len, size - len, " vrf_id %u", ctx->vrf_id);
+ break;
+
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DX2:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_B6:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_B6_ENCAP:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_BM:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_S:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_AS:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_AM:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_BPF:
+ default:
+ snprintf(str + len, size - len, " unknown(%s)", __func__);
+ }
+
+ return str;
+}
int snprintf_seg6_segs(char *str,
size_t size, const struct seg6_segs *segs);
@@ -254,12 +377,24 @@ extern struct srv6_locator_chunk *srv6_locator_chunk_alloc(void);
extern void srv6_locator_free(struct srv6_locator *locator);
extern void srv6_locator_chunk_list_free(void *data);
extern void srv6_locator_chunk_free(struct srv6_locator_chunk **chunk);
+extern void srv6_locator_copy(struct srv6_locator *copy,
+ const struct srv6_locator *locator);
json_object *srv6_locator_chunk_json(const struct srv6_locator_chunk *chunk);
json_object *srv6_locator_json(const struct srv6_locator *loc);
json_object *srv6_locator_detailed_json(const struct srv6_locator *loc);
json_object *
srv6_locator_chunk_detailed_json(const struct srv6_locator_chunk *chunk);
+extern struct srv6_sid_format *srv6_sid_format_alloc(const char *name);
+extern void srv6_sid_format_free(struct srv6_sid_format *format);
+extern void delete_srv6_sid_format(void *format);
+
+extern struct srv6_sid_ctx *srv6_sid_ctx_alloc(enum seg6local_action_t behavior,
+ struct in_addr *nh4,
+ struct in6_addr *nh6,
+ vrf_id_t vrf_id);
+extern void srv6_sid_ctx_free(struct srv6_sid_ctx *ctx);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/stream.c b/lib/stream.c
index fa20ebdbe7..bb90f3b944 100644
--- a/lib/stream.c
+++ b/lib/stream.c
@@ -921,7 +921,7 @@ int stream_put_in_addr(struct stream *s, const struct in_addr *addr)
return sizeof(uint32_t);
}
-bool stream_put_ipaddr(struct stream *s, struct ipaddr *ip)
+bool stream_put_ipaddr(struct stream *s, const struct ipaddr *ip)
{
stream_putw(s, ip->ipa_type);
diff --git a/lib/stream.h b/lib/stream.h
index 61eaa46c95..e48cedc613 100644
--- a/lib/stream.h
+++ b/lib/stream.h
@@ -175,7 +175,7 @@ extern int stream_putq(struct stream *, uint64_t);
extern int stream_putq_at(struct stream *, size_t, uint64_t);
extern int stream_put_ipv4(struct stream *, uint32_t);
extern int stream_put_in_addr(struct stream *s, const struct in_addr *addr);
-extern bool stream_put_ipaddr(struct stream *s, struct ipaddr *ip);
+extern bool stream_put_ipaddr(struct stream *s, const struct ipaddr *ip);
extern int stream_put_in_addr_at(struct stream *s, size_t putp,
const struct in_addr *addr);
extern int stream_put_in6_addr_at(struct stream *s, size_t putp,
diff --git a/lib/subdir.am b/lib/subdir.am
index 3264f31af7..4bcce9a2b0 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -504,20 +504,41 @@ SUFFIXES += .xref
%.xref: % $(CLIPPY)
$(AM_V_XRELFO) $(CLIPPY) $(top_srcdir)/python/xrelfo.py $(WERROR) $(XRELFO_FLAGS) -o $@ $<
+# these run up to a total of 350k lines at the time of writing. That's a lot
+# for the compiler to chug down - enough to warrant splitting it up so it can
+# benefit from parallel build.
+vtysh_cmd_split = \
+ vtysh/vtysh_cmd.1.c \
+ vtysh/vtysh_cmd.2.c \
+ vtysh/vtysh_cmd.3.c \
+ vtysh/vtysh_cmd.4.c \
+ vtysh/vtysh_cmd.5.c \
+ vtysh/vtysh_cmd.6.c \
+ vtysh/vtysh_cmd.7.c \
+ vtysh/vtysh_cmd.8.c \
+ # end
+
# dependencies added in python/makefile.py
frr.xref:
- $(AM_V_XRELFO) $(CLIPPY) $(top_srcdir)/python/xrelfo.py -o $@ -c vtysh/vtysh_cmd.c $^
+ $(AM_V_XRELFO) $(CLIPPY) $(top_srcdir)/python/xrelfo.py -o $@ $^ \
+ -c vtysh/vtysh_cmd.c $(vtysh_cmd_split)
all-am: frr.xref
clean-xref:
-rm -rf $(xrefs) frr.xref
clean-local: clean-xref
-CLEANFILES += vtysh/vtysh_cmd.c
vtysh/vtysh_cmd.c: frr.xref
@test -f $@ || rm -f frr.xref || true
@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) frr.xref
+vtysh/vtysh_cmd.%.c: vtysh/vtysh_cmd.c
+ @test -f $@ || rm -f vtysh/vtysh_cmd.c || true
+ @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) vtysh/vtysh_cmd.c
+
+nodist_vtysh_vtysh_SOURCES = vtysh/vtysh_cmd.c $(vtysh_cmd_split)
+CLEANFILES += vtysh/vtysh_cmd.c $(vtysh_cmd_split)
+
## automake's "ylwrap" is a great piece of GNU software... not.
.l.c:
$(AM_V_LEX)$(am__skiplex) $(LEXCOMPILE) $<
diff --git a/lib/termtable.c b/lib/termtable.c
index 9b36d5ebfa..ce19701389 100644
--- a/lib/termtable.c
+++ b/lib/termtable.c
@@ -363,7 +363,7 @@ char *ttable_dump(struct ttable *tt, const char *newline)
memcpy(&right[0], newline, nl_len);
/* allocate print buffer */
- buf = XCALLOC(MTYPE_TMP, width * (nlines + 1) + 1);
+ buf = XCALLOC(MTYPE_TMP_TTABLE, width * (nlines + 1) + 1);
pos = 0;
if (tt->style.border.top_on) {
@@ -496,7 +496,9 @@ char *ttable_dump(struct ttable *tt, const char *newline)
* l int64
* s string (default)
*/
-json_object *ttable_json(struct ttable *tt, const char *const formats)
+static json_object *ttable_json_internal(struct ttable *tt,
+ const char *const formats,
+ const char *row_text[])
{
struct ttable_cell *row; /* iteration pointers */
json_object *json = NULL;
@@ -522,9 +524,55 @@ json_object *ttable_json(struct ttable *tt, const char *const formats)
default:
val = json_object_new_string(row[j].text);
}
- json_object_object_add(jobj, tt->table[0][j].text, val);
+ if (row_text)
+ json_object_object_add(jobj, row_text[j], val);
+ else
+ json_object_object_add(jobj,
+ tt->table[0][j].text,
+ val);
}
}
return json;
}
+
+json_object *ttable_json(struct ttable *tt, const char *const formats)
+{
+ return ttable_json_internal(tt, formats, NULL);
+}
+
+json_object *ttable_json_with_json_text(struct ttable *tt,
+ const char *const formats,
+ const char *json_override_text)
+{
+ char **row_name; /* iteration pointers */
+ char *res, *section, *orig;
+ int col = 0;
+ int ncols = 0, j;
+ json_object *json = NULL;
+
+ if (json_override_text) {
+ /* count how many columns we have */
+ for (j = 0; json_override_text[j]; j++)
+ ncols += !!(json_override_text[j] == '|');
+ ncols++;
+ }
+ if (json_override_text == NULL || ncols != tt->ncols)
+ return ttable_json_internal(tt, formats, NULL);
+
+ /* CALLOC a block of cells */
+ row_name = XCALLOC(MTYPE_TTABLE, ncols * sizeof(char *));
+ orig = XSTRDUP(MTYPE_TTABLE, json_override_text);
+ res = orig;
+ while (res && col < ncols) {
+ section = strsep(&res, "|");
+ row_name[col] = XSTRDUP(MTYPE_TTABLE, section);
+ col++;
+ }
+ json = ttable_json_internal(tt, formats, (const char **)row_name);
+ for (j = 0; j < col; j++)
+ XFREE(MTYPE_TTABLE, row_name[j]);
+ XFREE(MTYPE_TTABLE, row_name);
+ XFREE(MTYPE_TTABLE, orig);
+ return json;
+}
diff --git a/lib/termtable.h b/lib/termtable.h
index 7258682bd8..d284c4f376 100644
--- a/lib/termtable.h
+++ b/lib/termtable.h
@@ -270,7 +270,7 @@ void ttable_rowseps(struct ttable *tt, unsigned int row,
*
* Caller must free this string after use with
*
- * XFREE (MTYPE_TMP, str);
+ * XFREE (MTYPE_TMP_TTABLE, str);
*
* @param tt the table to dump
* @param newline the desired newline sequence to use, null terminated.
@@ -289,6 +289,21 @@ char *ttable_dump(struct ttable *tt, const char *newline);
*/
json_object *ttable_json(struct ttable *tt, const char *const formats);
+/**
+ * Convert a table to a JSON array of objects.
+ *
+ * Caller must free the returned json_object structure.
+ *
+ * @param tt the table to convert
+ * @param formats an array of characters indicating what JSON type should be
+ * used.
+ * @param formats an optinal string of row headers that overrids the first row of the table.
+ * This is useful to get naming convention that align with caml Format.
+ */
+json_object *ttable_json_with_json_text(struct ttable *tt,
+ const char *const formats,
+ const char *json_override_text);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/vector.c b/lib/vector.c
index 60d383101a..0636154960 100644
--- a/lib/vector.c
+++ b/lib/vector.c
@@ -24,45 +24,44 @@ vector vector_init(unsigned int size)
v->alloced = size;
v->active = 0;
v->count = 0;
+ v->dynamic = true;
v->index = XCALLOC(MTYPE_VECTOR_INDEX, sizeof(void *) * size);
return v;
}
void vector_free(vector v)
{
- XFREE(MTYPE_VECTOR_INDEX, v->index);
- XFREE(MTYPE_VECTOR, v);
+ if (v->alloced)
+ XFREE(MTYPE_VECTOR_INDEX, v->index);
+ if (v->dynamic)
+ XFREE(MTYPE_VECTOR, v);
}
-vector vector_copy(vector v)
-{
- unsigned int size;
- vector new = XCALLOC(MTYPE_VECTOR, sizeof(struct _vector));
-
- new->active = v->active;
- new->alloced = v->alloced;
- new->count = v->count;
-
- size = sizeof(void *) * (v->alloced);
- new->index = XCALLOC(MTYPE_VECTOR_INDEX, size);
- memcpy(new->index, v->index, size);
-
- return new;
-}
-
-/* Check assigned index, and if it runs short double index pointer */
+/* resize vector to a minimum of num
+ * may resize larger to avoid excessive realloc overhead
+ */
void vector_ensure(vector v, unsigned int num)
{
+ unsigned int newsz;
+
if (v->alloced > num)
return;
- v->index = XREALLOC(MTYPE_VECTOR_INDEX, v->index,
- sizeof(void *) * (v->alloced * 2));
- memset(&v->index[v->alloced], 0, sizeof(void *) * v->alloced);
- v->alloced *= 2;
+ newsz = MAX(v->active * 2, num + 1);
+
+ if (!v->alloced && v->index) {
+ /* currently using global variable, not malloc'd memory */
+ void **orig_index = v->index;
- if (v->alloced <= num)
- vector_ensure(v, num);
+ v->index = XMALLOC(MTYPE_VECTOR_INDEX, sizeof(void *) * newsz);
+ memcpy(v->index, orig_index, v->active * sizeof(void *));
+ v->alloced = v->active;
+ } else
+ v->index = XREALLOC(MTYPE_VECTOR_INDEX, v->index,
+ sizeof(void *) * newsz);
+
+ memset(&v->index[v->alloced], 0, sizeof(void *) * (newsz - v->alloced));
+ v->alloced = newsz;
}
/* This function only returns next empty slot index. It dose not mean
@@ -140,7 +139,7 @@ void *vector_lookup_ensure(vector v, unsigned int i)
/* Unset value at specified index slot. */
void vector_unset(vector v, unsigned int i)
{
- if (i >= v->alloced)
+ if (i >= v->active)
return;
if (v->index[i])
diff --git a/lib/vector.h b/lib/vector.h
index fcbc13257a..ae05d4d3e0 100644
--- a/lib/vector.h
+++ b/lib/vector.h
@@ -15,9 +15,26 @@ extern "C" {
/* struct for vector */
struct _vector {
- unsigned int active; /* number of active slots */
- unsigned int alloced; /* number of allocated slot */
+ /* active: index of last non-NULL item (+1)
+ * count: number of non-NULL items (+1)
+ *
+ * the two will be different if a slot is set to NULL (without pulling
+ * up later items in the array). Whether this happens depends on
+ * which vector functions are used. If no empty slots are used, the
+ * two fields will be identical.
+ *
+ * alloced: size of array pointed to by index. If this is 0, index
+ * points at a global variable rather than a malloc'd bit of memory.
+ * The vector code will convert to malloc'd memory if necessary to
+ * perform updates.
+ */
+ unsigned int active;
+ unsigned int alloced;
unsigned int count;
+
+ /* whether struct _vector itself is dynamically allocated */
+ bool dynamic;
+
void **index; /* index to data */
};
typedef struct _vector *vector;
@@ -51,7 +68,6 @@ static inline unsigned int vector_count(vector v)
}
extern void vector_free(vector v);
-extern vector vector_copy(vector v);
extern void *vector_lookup(vector, unsigned int);
extern void *vector_lookup_ensure(vector, unsigned int);
diff --git a/lib/vty.c b/lib/vty.c
index 628c694e95..256a3bb3f5 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -345,8 +345,17 @@ int vty_out(struct vty *vty, const char *format, ...)
case VTY_SHELL_SERV:
case VTY_FILE:
default:
+ vty->vty_buf_size_accumulated += strlen(filtered);
/* print without crlf replacement */
buffer_put(vty->obuf, (uint8_t *)filtered, strlen(filtered));
+ /* For every chunk of memory, we invoke vtysh_flush where we
+ * put the data of collective vty->obuf Linked List items on the
+ * socket and free the vty->obuf data.
+ */
+ if (vty->vty_buf_size_accumulated >= VTY_MAX_INTERMEDIATE_FLUSH) {
+ vty->vty_buf_size_accumulated = 0;
+ vtysh_flush(vty);
+ }
break;
}
@@ -390,6 +399,21 @@ int vty_json_no_pretty(struct vty *vty, struct json_object *json)
return vty_json_helper(vty, json, JSON_C_TO_STRING_NOSLASHESCAPE);
}
+
+void vty_json_key(struct vty *vty, const char *key, bool *first_key)
+{
+ vty_out(vty, "%s\"%s\":", *first_key ? "{" : ",", key);
+ *first_key = false;
+}
+
+void vty_json_close(struct vty *vty, bool first_key)
+{
+ if (first_key)
+ /* JSON was not opened */
+ vty_out(vty, "{");
+ vty_out(vty, "}\n");
+}
+
void vty_json_empty(struct vty *vty, struct json_object *json)
{
json_object *jsonobj = json;
@@ -2103,6 +2127,8 @@ static void vtysh_accept(struct event *thread)
int client_len;
struct sockaddr_un client;
struct vty *vty;
+ int ret = 0;
+ uint32_t sndbufsize = VTY_SEND_BUF_MAX;
vty_event_serv(VTYSH_SERV, vtyserv);
@@ -2126,6 +2152,20 @@ static void vtysh_accept(struct event *thread)
close(sock);
return;
}
+
+ /*
+ * Increasing the SEND socket buffer size so that the socket can hold
+ * before sending it to VTY shell.
+ */
+ ret = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&sndbufsize,
+ sizeof(sndbufsize));
+ if (ret < 0) {
+ flog_err(EC_LIB_SOCKET,
+ "Cannot set socket %d send buffer size, %s", sock,
+ safe_strerror(errno));
+ close(sock);
+ return;
+ }
set_cloexec(sock);
#ifdef VTYSH_DEBUG
@@ -2212,6 +2252,7 @@ static int vtysh_flush(struct vty *vty)
vty_close(vty);
return -1;
case BUFFER_EMPTY:
+ vty->vty_buf_size_accumulated = 0;
break;
}
return 0;
@@ -3487,7 +3528,7 @@ static void vty_mgmt_server_connected(struct mgmt_fe_client *client,
/* Start or stop listening for vty connections */
if (connected)
- frr_vty_serv_start();
+ frr_vty_serv_start(true);
else
frr_vty_serv_stop();
}
@@ -3576,8 +3617,9 @@ static void vty_mgmt_set_config_result_notified(
vty_out(vty, "%s\n", errmsg_if_any);
} else {
debug_fe_client("SET_CONFIG request for client 0x%" PRIx64
- " req-id %" PRIu64 " was successfull",
- client_id, req_id);
+ " req-id %" PRIu64 " was successfull%s%s",
+ client_id, req_id, errmsg_if_any ? ": " : "",
+ errmsg_if_any ?: "");
}
if (implicit_commit) {
@@ -3609,8 +3651,9 @@ static void vty_mgmt_commit_config_result_notified(
vty_out(vty, "%s\n", errmsg_if_any);
} else {
debug_fe_client("COMMIT_CONFIG request for client 0x%" PRIx64
- " req-id %" PRIu64 " was successfull",
- client_id, req_id);
+ " req-id %" PRIu64 " was successfull%s%s",
+ client_id, req_id, errmsg_if_any ? ": " : "",
+ errmsg_if_any ?: "");
if (errmsg_if_any)
vty_out(vty, "MGMTD: %s\n", errmsg_if_any);
}
@@ -3641,8 +3684,9 @@ static int vty_mgmt_get_data_result_notified(
}
debug_fe_client("GET_DATA request succeeded, client 0x%" PRIx64
- " req-id %" PRIu64,
- client_id, req_id);
+ " req-id %" PRIu64 "%s%s",
+ client_id, req_id, errmsg_if_any ? ": " : "",
+ errmsg_if_any ?: "");
if (req_id != mgmt_last_req_id) {
mgmt_last_req_id = req_id;
@@ -3885,7 +3929,7 @@ static int vty_mgmt_error_notified(struct mgmt_fe_client *client,
const char *cname = mgmt_fe_client_name(client);
if (!vty->mgmt_req_pending_cmd) {
- debug_fe_client("Erorr with no pending command: %d returned for client %s 0x%" PRIx64
+ debug_fe_client("Error with no pending command: %d returned for client %s 0x%" PRIx64
" session-id %" PRIu64 " req-id %" PRIu64
"error-str %s",
error, cname, client_id, session_id, req_id,
@@ -3896,7 +3940,7 @@ static int vty_mgmt_error_notified(struct mgmt_fe_client *client,
return CMD_WARNING;
}
- debug_fe_client("Erorr %d returned for client %s 0x%" PRIx64
+ debug_fe_client("Error %d returned for client %s 0x%" PRIx64
" session-id %" PRIu64 " req-id %" PRIu64 "error-str %s",
error, cname, client_id, session_id, req_id, errstr);
diff --git a/lib/vty.h b/lib/vty.h
index a9570ef048..e511e8e79a 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -236,6 +236,7 @@ struct vty {
uintptr_t mgmt_req_pending_data;
bool mgmt_locked_candidate_ds;
bool mgmt_locked_running_ds;
+ uint64_t vty_buf_size_accumulated;
};
static inline void vty_push_context(struct vty *vty, int node, uint64_t id)
@@ -338,6 +339,12 @@ struct vty_arg {
/* Vty read buffer size. */
#define VTY_READ_BUFSIZ 512
+/* Vty max send buffer size */
+#define VTY_SEND_BUF_MAX 16777216
+
+/* Vty flush intermediate size */
+#define VTY_MAX_INTERMEDIATE_FLUSH 131072
+
/* Directory separator. */
#ifndef DIRECTORY_SEP
#define DIRECTORY_SEP '/'
@@ -378,6 +385,8 @@ extern bool vty_set_include(struct vty *vty, const char *regexp);
*/
extern int vty_json(struct vty *vty, struct json_object *json);
extern int vty_json_no_pretty(struct vty *vty, struct json_object *json);
+void vty_json_key(struct vty *vty, const char *key, bool *first_key);
+void vty_json_close(struct vty *vty, bool first_key);
extern void vty_json_empty(struct vty *vty, struct json_object *json);
/* post fd to be passed to the vtysh client
* fd is owned by the VTY code after this and will be closed when done
diff --git a/lib/yang.c b/lib/yang.c
index 702fcf436d..6c1aed00cc 100644
--- a/lib/yang.c
+++ b/lib/yang.c
@@ -25,6 +25,11 @@ DEFINE_MTYPE_STATIC(LIB, YANG_DATA, "YANG data structure");
#define yang_lyd_find_xpath3(ctx_node, tree, xpath, format, prefix_data, vars, \
set) \
lyd_find_xpath3(ctx_node, tree, xpath, vars, set)
+
+#ifndef LYD_NEW_VAL_OUTPUT
+#define LYD_NEW_VAL_OUTPUT LYD_NEW_PATH_OUTPUT
+#endif
+
#else
#define yang_lyd_find_xpath3(ctx_node, tree, xpath, format, prefix_data, vars, \
set) \
@@ -671,7 +676,7 @@ void yang_dnode_rpc_output_add(struct lyd_node *output, const char *xpath,
LY_ERR err;
err = lyd_new_path(output, ly_native_ctx, xpath, value,
- LYD_NEW_PATH_OUTPUT | LYD_NEW_PATH_UPDATE, NULL);
+ LYD_NEW_VAL_OUTPUT | LYD_NEW_PATH_UPDATE, NULL);
assert(err == LY_SUCCESS);
}
@@ -897,7 +902,7 @@ char *yang_convert_lyd_format(const char *data, size_t data_len,
assert(out_format != LYD_LYB);
- if (in_format != LYD_LYB && !MGMT_MSG_VALIDATE_NUL_TERM(data, data_len)) {
+ if (in_format != LYD_LYB && (!data_len || data[data_len - 1] != 0)) {
zlog_err("Corrupt input data, no NUL terminating byte");
return NULL;
}
@@ -1393,8 +1398,10 @@ LY_ERR yang_lyd_trim_xpath(struct lyd_node **root, const char *xpath)
}
}
darr_foreach_i (remove, i) {
- if (remove[i] == *root)
+ if (remove[i] == *root) {
+ assert(*root);
*root = (*root)->next;
+ }
lyd_free_tree(remove[i]);
}
darr_free(remove);
diff --git a/lib/zclient.c b/lib/zclient.c
index c5b1e72380..0e832f0d8f 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -1125,6 +1125,10 @@ int zapi_srv6_locator_encode(struct stream *s, const struct srv6_locator *l)
stream_put(s, l->name, strlen(l->name));
stream_putw(s, l->prefix.prefixlen);
stream_put(s, &l->prefix.prefix, sizeof(l->prefix.prefix));
+ stream_putc(s, l->block_bits_length);
+ stream_putc(s, l->node_bits_length);
+ stream_putc(s, l->function_bits_length);
+ stream_putc(s, l->argument_bits_length);
stream_putc(s, l->flags);
return 0;
}
@@ -1141,6 +1145,10 @@ int zapi_srv6_locator_decode(struct stream *s, struct srv6_locator *l)
STREAM_GETW(s, l->prefix.prefixlen);
STREAM_GET(&l->prefix.prefix, s, sizeof(l->prefix.prefix));
l->prefix.family = AF_INET6;
+ STREAM_GETC(s, l->block_bits_length);
+ STREAM_GETC(s, l->node_bits_length);
+ STREAM_GETC(s, l->function_bits_length);
+ STREAM_GETC(s, l->argument_bits_length);
STREAM_GETC(s, l->flags);
return 0;
@@ -2125,6 +2133,46 @@ stream_failure:
return false;
}
+bool zapi_srv6_sid_notify_decode(struct stream *s, struct srv6_sid_ctx *ctx,
+ struct in6_addr *sid_value, uint32_t *func,
+ uint32_t *wide_func,
+ enum zapi_srv6_sid_notify *note,
+ char **p_locator_name)
+{
+ uint32_t f, wf;
+ uint16_t len;
+ static char locator_name[SRV6_LOCNAME_SIZE] = {};
+
+ STREAM_GET(note, s, sizeof(*note));
+ STREAM_GET(ctx, s, sizeof(struct srv6_sid_ctx));
+ STREAM_GET(sid_value, s, sizeof(struct in6_addr));
+ STREAM_GETL(s, f);
+ STREAM_GETL(s, wf);
+
+ if (func)
+ *func = f;
+ if (wide_func)
+ *wide_func = wf;
+
+ STREAM_GETW(s, len);
+ if (len > SRV6_LOCNAME_SIZE) {
+ *p_locator_name = NULL;
+ return false;
+ }
+ if (p_locator_name) {
+ if (len == 0)
+ *p_locator_name = NULL;
+ else {
+ STREAM_GET(locator_name, s, len);
+ *p_locator_name = locator_name;
+ }
+ }
+ return true;
+
+stream_failure:
+ return false;
+}
+
struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh)
{
struct nexthop *n = nexthop_new();
@@ -2134,6 +2182,7 @@ struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh)
n->ifindex = znh->ifindex;
n->gate = znh->gate;
n->srte_color = znh->srte_color;
+ n->weight = znh->weight;
/*
* This function currently handles labels
@@ -2174,6 +2223,7 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh,
znh->weight = nh->weight;
znh->ifindex = nh->ifindex;
znh->gate = nh->gate;
+ znh->srte_color = nh->srte_color;
if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK))
SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
@@ -3267,10 +3317,154 @@ int srv6_manager_release_locator_chunk(struct zclient *zclient,
return zclient_send_message(zclient);
}
+/**
+ * Function to request a SRv6 locator in an asynchronous way
+ *
+ * @param zclient The zclient used to connect to SRv6 Manager (zebra)
+ * @param locator_name Name of SRv6 locator
+ * @return 0 on success, -1 otherwise
+ */
+int srv6_manager_get_locator(struct zclient *zclient, const char *locator_name)
+{
+ struct stream *s;
+ size_t len;
+
+ if (!locator_name)
+ return -1;
+
+ if (zclient->sock < 0) {
+ flog_err(EC_LIB_ZAPI_SOCKET, "%s: invalid zclient socket",
+ __func__);
+ return -1;
+ }
+
+ if (zclient_debug)
+ zlog_debug("Getting SRv6 Locator %s", locator_name);
+
+ len = strlen(locator_name);
+
+ /* Send request */
+ s = zclient->obuf;
+ stream_reset(s);
+ zclient_create_header(s, ZEBRA_SRV6_MANAGER_GET_LOCATOR, VRF_DEFAULT);
+
+ /* Locator name */
+ stream_putw(s, len);
+ stream_put(s, locator_name, len);
+
+ /* Put length at the first point of the stream. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zclient_send_message(zclient);
+}
+
+/**
+ * Function to request an SRv6 SID in an asynchronous way
+ *
+ * @param zclient The zclient used to connect to SRv6 manager (zebra)
+ * @param ctx Context associated with the SRv6 SID
+ * @param sid_value SRv6 SID value for explicit SID allocation
+ * @param locator_name Name of the parent locator for dynamic SID allocation
+ * @param sid_func SID function assigned by the SRv6 Manager
+ * @result 0 on success, -1 otherwise
+ */
+int srv6_manager_get_sid(struct zclient *zclient, const struct srv6_sid_ctx *ctx,
+ struct in6_addr *sid_value, const char *locator_name,
+ uint32_t *sid_func)
+{
+ struct stream *s;
+ uint8_t flags = 0;
+ size_t len;
+ char buf[256];
+
+ if (zclient->sock < 0) {
+ flog_err(EC_LIB_ZAPI_SOCKET, "%s: invalid zclient socket",
+ __func__);
+ return ZCLIENT_SEND_FAILURE;
+ }
+
+ if (zclient_debug)
+ zlog_debug("Getting SRv6 SID: %s",
+ srv6_sid_ctx2str(buf, sizeof(buf), ctx));
+
+ /* send request */
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_SRV6_MANAGER_GET_SRV6_SID, VRF_DEFAULT);
+
+ /* Context associated with the SRv6 SID */
+ stream_put(s, ctx, sizeof(struct srv6_sid_ctx));
+
+ /* Flags */
+ if (!sid_zero_ipv6(sid_value))
+ SET_FLAG(flags, ZAPI_SRV6_MANAGER_SID_FLAG_HAS_SID_VALUE);
+ if (locator_name)
+ SET_FLAG(flags, ZAPI_SRV6_MANAGER_SID_FLAG_HAS_LOCATOR);
+ stream_putc(s, flags);
+
+ /* SRv6 SID value */
+ if (CHECK_FLAG(flags, ZAPI_SRV6_MANAGER_SID_FLAG_HAS_SID_VALUE))
+ stream_put(s, sid_value, sizeof(struct in6_addr));
+
+ /* SRv6 locator */
+ if (CHECK_FLAG(flags, ZAPI_SRV6_MANAGER_SID_FLAG_HAS_LOCATOR)) {
+ len = strlen(locator_name);
+ stream_putw(s, len);
+ stream_put(s, locator_name, len);
+ }
+
+ /* Put length at the first point of the stream. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ /* Send the request to SRv6 Manager */
+ return zclient_send_message(zclient);
+}
+
+/**
+ * Function to release an SRv6 SID
+ *
+ * @param zclient Zclient used to connect to SRv6 manager (zebra)
+ * @param ctx Context associated with the SRv6 SID to be removed
+ * @result 0 on success, -1 otherwise
+ */
+int srv6_manager_release_sid(struct zclient *zclient,
+ const struct srv6_sid_ctx *ctx)
+{
+ struct stream *s;
+ char buf[256];
+
+ if (zclient->sock < 0) {
+ flog_err(EC_LIB_ZAPI_SOCKET, "%s: invalid zclient socket",
+ __func__);
+ return -1;
+ }
+
+ if (zclient_debug)
+ zlog_debug("Releasing SRv6 SID: %s",
+ srv6_sid_ctx2str(buf, sizeof(buf), ctx));
+
+ /* send request */
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_SRV6_MANAGER_RELEASE_SRV6_SID,
+ VRF_DEFAULT);
+
+ /* Context associated with the SRv6 SID */
+ stream_put(s, ctx, sizeof(struct srv6_sid_ctx));
+
+ /* Put length at the first point of the stream. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ /* Send the SID release message */
+ return zclient_send_message(zclient);
+}
+
/*
* Asynchronous label chunk request
*
- * @param zclient Zclient used to connect to label manager (zebra)
+ * @param zclient The zclient used to connect to label manager (zebra)
* @param keep Avoid garbage collection
* @param chunk_size Amount of labels requested
* @param base Base for the label chunk. if MPLS_LABEL_BASE_ANY we do not care
diff --git a/lib/zclient.h b/lib/zclient.h
index 3759f94542..2877b347d8 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -209,6 +209,9 @@ typedef enum {
ZEBRA_SRV6_LOCATOR_DELETE,
ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK,
ZEBRA_SRV6_MANAGER_RELEASE_LOCATOR_CHUNK,
+ ZEBRA_SRV6_MANAGER_GET_LOCATOR,
+ ZEBRA_SRV6_MANAGER_GET_SRV6_SID,
+ ZEBRA_SRV6_MANAGER_RELEASE_SRV6_SID,
ZEBRA_ERROR,
ZEBRA_CLIENT_CAPABILITIES,
ZEBRA_OPAQUE_MESSAGE,
@@ -235,6 +238,7 @@ typedef enum {
ZEBRA_TC_FILTER_ADD,
ZEBRA_TC_FILTER_DELETE,
ZEBRA_OPAQUE_NOTIFY,
+ ZEBRA_SRV6_SID_NOTIFY,
} zebra_message_types_t;
/* Zebra message types. Please update the corresponding
* command_types array with any changes!
@@ -761,6 +765,13 @@ enum zapi_iptable_notify_owner {
ZAPI_IPTABLE_FAIL_REMOVE,
};
+enum zapi_srv6_sid_notify {
+ ZAPI_SRV6_SID_FAIL_ALLOC = 0,
+ ZAPI_SRV6_SID_ALLOCATED,
+ ZAPI_SRV6_SID_RELEASED,
+ ZAPI_SRV6_SID_FAIL_RELEASE,
+};
+
enum zclient_send_status {
ZCLIENT_SEND_FAILURE = -1,
ZCLIENT_SEND_SUCCESS = 0,
@@ -813,6 +824,28 @@ zapi_rule_notify_owner2str(enum zapi_rule_notify_owner note)
return ret;
}
+static inline const char *zapi_srv6_sid_notify2str(enum zapi_srv6_sid_notify note)
+{
+ const char *ret = "UNKNOWN";
+
+ switch (note) {
+ case ZAPI_SRV6_SID_FAIL_ALLOC:
+ ret = "ZAPI_SRV6_SID_FAIL_ALLOC";
+ break;
+ case ZAPI_SRV6_SID_ALLOCATED:
+ ret = "ZAPI_SRV6_SID_ALLOCATED";
+ break;
+ case ZAPI_SRV6_SID_FAIL_RELEASE:
+ ret = "ZAPI_SRV6_SID_FAIL_RELEASE";
+ break;
+ case ZAPI_SRV6_SID_RELEASED:
+ ret = "ZAPI_SRV6_SID_RELEASED";
+ break;
+ }
+
+ return ret;
+}
+
/* Zebra MAC types */
#define ZEBRA_MACIP_TYPE_STICKY 0x01 /* Sticky MAC*/
#define ZEBRA_MACIP_TYPE_GW 0x02 /* gateway (SVI) mac*/
@@ -1070,10 +1103,23 @@ extern int tm_get_table_chunk(struct zclient *zclient, uint32_t chunk_size,
uint32_t *start, uint32_t *end);
extern int tm_release_table_chunk(struct zclient *zclient, uint32_t start,
uint32_t end);
+
+/* Zebra SRv6 Manager flags */
+#define ZAPI_SRV6_MANAGER_SID_FLAG_HAS_SID_VALUE 0x01
+#define ZAPI_SRV6_MANAGER_SID_FLAG_HAS_LOCATOR 0x02
+
extern int srv6_manager_get_locator_chunk(struct zclient *zclient,
const char *locator_name);
extern int srv6_manager_release_locator_chunk(struct zclient *zclient,
const char *locator_name);
+extern int srv6_manager_get_locator(struct zclient *zclient,
+ const char *locator_name);
+extern int srv6_manager_get_sid(struct zclient *zclient,
+ const struct srv6_sid_ctx *ctx,
+ struct in6_addr *sid_value,
+ const char *locator_name, uint32_t *sid_func);
+extern int srv6_manager_release_sid(struct zclient *zclient,
+ const struct srv6_sid_ctx *ctx);
extern enum zclient_send_status zebra_send_sr_policy(struct zclient *zclient,
int cmd,
@@ -1128,6 +1174,11 @@ bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno,
bool zapi_ipset_notify_decode(struct stream *s,
uint32_t *unique,
enum zapi_ipset_notify_owner *note);
+bool zapi_srv6_sid_notify_decode(struct stream *s, struct srv6_sid_ctx *ctx,
+ struct in6_addr *sid_value, uint32_t *func,
+ uint32_t *wide_func,
+ enum zapi_srv6_sid_notify *note,
+ char **locator_name);
/* Nexthop-group message apis */
extern enum zclient_send_status