diff options
Diffstat (limited to 'lib')
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 */ @@ -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; +} @@ -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 */ @@ -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); @@ -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); @@ -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 |
