diff options
Diffstat (limited to 'lib')
92 files changed, 2590 insertions, 1963 deletions
diff --git a/lib/atomlist.c b/lib/atomlist.c index 8169ba9eb4..b7c9516a00 100644 --- a/lib/atomlist.c +++ b/lib/atomlist.c @@ -18,6 +18,8 @@ #include "config.h" #endif +#include <assert.h> + #include "atomlist.h" void atomlist_add_head(struct atomlist_head *h, struct atomlist_item *item) @@ -121,7 +123,7 @@ static void atomlist_del_core(struct atomlist_head *h, memory_order_consume); /* track the beginning of a chain of deleted items - * this is neccessary to make this lock-free; we can + * this is necessary to make this lock-free; we can * complete deletions started by other threads. */ if (!atomptr_l(prevval)) { diff --git a/lib/atomlist.h b/lib/atomlist.h index c795128a34..b0c4da4baa 100644 --- a/lib/atomlist.h +++ b/lib/atomlist.h @@ -100,7 +100,7 @@ static inline bool atomptr_u(atomptr_t val) /* single-linked list, unsorted/arbitrary. * can be used as queue with add_tail / pop * - * all operations are lock-free, but not neccessarily wait-free. this means + * all operations are lock-free, but not necessarily wait-free. this means * that there is no state where the system as a whole stops making process, * but it *is* possible that a *particular* thread is delayed by some time. * @@ -143,8 +143,8 @@ static struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, if (ifp == NULL) { if (bsglobal.debugging) zlog_debug( - "zebra_interface_bfd_read: Can't find interface by ifindex: %d ", - ifindex); + "%s: Can't find interface by ifindex: %d ", + __func__, ifindex); return NULL; } } @@ -203,7 +203,7 @@ static void bfd_last_update(time_t last_update, char *buf, size_t len) struct tm tm; struct timeval tv; - /* If no BFD satatus update has ever been received, print `never'. */ + /* If no BFD status update has ever been received, print `never'. */ if (last_update == 0) { snprintf(buf, len, "never"); return; @@ -251,8 +251,8 @@ void bfd_client_sendmsg(struct zclient *zclient, int command, if (ret == ZCLIENT_SEND_FAILURE) { if (bsglobal.debugging) zlog_debug( - "bfd_client_sendmsg %ld: zclient_send_message() failed", - (long)getpid()); + "%s: %ld: zclient_send_message() failed", + __func__, (long)getpid()); return; } @@ -552,7 +552,8 @@ static bool bfd_sess_address_changed(const struct bfd_session_params *bsp, } void bfd_sess_set_ipv4_addrs(struct bfd_session_params *bsp, - struct in_addr *src, struct in_addr *dst) + const struct in_addr *src, + const struct in_addr *dst) { if (!bfd_sess_address_changed(bsp, AF_INET, (struct in6_addr *)src, (struct in6_addr *)dst)) @@ -576,10 +577,10 @@ void bfd_sess_set_ipv4_addrs(struct bfd_session_params *bsp, } void bfd_sess_set_ipv6_addrs(struct bfd_session_params *bsp, - struct in6_addr *src, struct in6_addr *dst) + const struct in6_addr *src, + const struct in6_addr *dst) { - if (!bfd_sess_address_changed(bsp, AF_INET, (struct in6_addr *)src, - (struct in6_addr *)dst)) + if (!bfd_sess_address_changed(bsp, AF_INET6, src, dst)) return; /* If already installed, remove the old setting. */ @@ -645,32 +646,16 @@ void bfd_sess_set_vrf(struct bfd_session_params *bsp, vrf_id_t vrf_id) bsp->args.vrf_id = vrf_id; } -void bfd_sess_set_mininum_ttl(struct bfd_session_params *bsp, uint8_t min_ttl) -{ - assert(min_ttl != 0); - - if (bsp->args.ttl == ((BFD_SINGLE_HOP_TTL + 1) - min_ttl)) - return; - - /* If already installed, remove the old setting. */ - _bfd_sess_remove(bsp); - - /* Invert TTL value: protocol expects number of hops. */ - min_ttl = (BFD_SINGLE_HOP_TTL + 1) - min_ttl; - bsp->args.ttl = min_ttl; - bsp->args.mhop = (min_ttl > 1); -} - -void bfd_sess_set_hop_count(struct bfd_session_params *bsp, uint8_t min_ttl) +void bfd_sess_set_hop_count(struct bfd_session_params *bsp, uint8_t hops) { - if (bsp->args.ttl == min_ttl) + if (bsp->args.ttl == hops) return; /* If already installed, remove the old setting. */ _bfd_sess_remove(bsp); - bsp->args.ttl = min_ttl; - bsp->args.mhop = (min_ttl > 1); + bsp->args.ttl = hops; + bsp->args.mhop = (hops > 1); } @@ -705,11 +690,6 @@ enum bfd_session_state bfd_sess_status(const struct bfd_session_params *bsp) return bsp->bss.state; } -uint8_t bfd_sess_minimum_ttl(const struct bfd_session_params *bsp) -{ - return ((BFD_SINGLE_HOP_TTL + 1) - bsp->args.ttl); -} - uint8_t bfd_sess_hop_count(const struct bfd_session_params *bsp) { return bsp->args.ttl; @@ -821,10 +801,13 @@ void bfd_sess_show(struct vty *vty, struct json_object *json, * * Use this as `zclient` `bfd_dest_replay` callback. */ -static int zclient_bfd_session_reply(ZAPI_CALLBACK_ARGS) +int zclient_bfd_session_replay(ZAPI_CALLBACK_ARGS) { struct bfd_session_params *bsp; + if (!zclient->bfd_integration) + return 0; + /* Do nothing when shutting down. */ if (bsglobal.shutting_down) return 0; @@ -855,7 +838,7 @@ static int zclient_bfd_session_reply(ZAPI_CALLBACK_ARGS) return 0; } -static int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS) +int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS) { struct bfd_session_params *bsp, *bspn; size_t sessions_updated = 0; @@ -868,6 +851,9 @@ static int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS) struct prefix sp; char ifstr[128], cbitstr[32]; + if (!zclient->bfd_integration) + return 0; + /* Do nothing when shutting down. */ if (bsglobal.shutting_down) return 0; @@ -969,9 +955,8 @@ void bfd_protocol_integration_init(struct zclient *zc, struct thread_master *tm) bsglobal.zc = zc; bsglobal.tm = tm; - /* Install our callbacks. */ - zc->interface_bfd_dest_update = zclient_bfd_session_update; - zc->bfd_dest_replay = zclient_bfd_session_reply; + /* Enable BFD callbacks. */ + zc->bfd_integration = true; /* Send the client registration */ bfd_client_sendmsg(zc, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); @@ -124,7 +124,8 @@ void bfd_sess_free(struct bfd_session_params **bsp); * \param dst remote address (mandatory). */ void bfd_sess_set_ipv4_addrs(struct bfd_session_params *bsp, - struct in_addr *src, struct in_addr *dst); + const struct in_addr *src, + const struct in_addr *dst); /** * Set the local and peer address of the BFD session. @@ -138,7 +139,8 @@ void bfd_sess_set_ipv4_addrs(struct bfd_session_params *bsp, * \param dst remote address (mandatory). */ void bfd_sess_set_ipv6_addrs(struct bfd_session_params *bsp, - struct in6_addr *src, struct in6_addr *dst); + const struct in6_addr *src, + const struct in6_addr *dst); /** * Configure the BFD session interface. @@ -179,39 +181,14 @@ void bfd_sess_set_vrf(struct bfd_session_params *bsp, vrf_id_t vrf_id); * Configure the BFD session single/multi hop setting. * * NOTE: - * If the TTL changed the session is removed and must be installed again - * with `bfd_sess_install`. + * If the number of hops is changed the session is removed and must be + * installed again with `bfd_sess_install`. * * \param bsp BFD session parameters. - * \param min_ttl minimum TTL value expected (255 for single hop, 254 for - * multi hop with single hop, 253 for multi hop with two hops - * and so on). See `BFD_SINGLE_HOP_TTL` and - * `BFD_MULTI_HOP_MIN_TTL` for defaults. - * - * To simplify things if your protocol only knows the amount of hops it is - * better to use `bfd_sess_set_hops` instead. + * \param hops maximum amount of hops expected (1 for single hop, 2 or + * more for multi hop). */ -void bfd_sess_set_mininum_ttl(struct bfd_session_params *bsp, uint8_t min_ttl); - -/** To use single hop the minimum TTL must be set to this. */ -#define BFD_SINGLE_HOP_TTL 255 -/** To use multi hop the minimum TTL must be set to this or less. */ -#define BFD_MULTI_HOP_MIN_TTL 254 - -/** - * This function is the inverted version of `bfd_sess_set_minimum_ttl`. - * Instead of receiving the minimum expected TTL, it receives the amount of - * hops the protocol will jump. - * - * NOTE: - * If the TTL changed the session is removed and must be installed again - * with `bfd_sess_install`. - * - * \param bsp BFD session parameters. - * \param min_ttl minimum amount of hops expected (1 for single hop, 2 or - * more for multi hop). - */ -void bfd_sess_set_hop_count(struct bfd_session_params *bsp, uint8_t min_ttl); +void bfd_sess_set_hop_count(struct bfd_session_params *bsp, uint8_t hops); /** * Configure the BFD session to set the Control Plane Independent bit. @@ -276,17 +253,7 @@ void bfd_sess_uninstall(struct bfd_session_params *bsp); enum bfd_session_state bfd_sess_status(const struct bfd_session_params *bsp); /** - * Get BFD session minimum TTL configured value. - * - * \param bsp session parameters. - * - * \returns configured minimum TTL. - */ -uint8_t bfd_sess_minimum_ttl(const struct bfd_session_params *bsp); - -/** - * Inverted version of `bfd_sess_minimum_ttl`. Gets the amount of hops in the - * way to the peer. + * Get BFD session amount of hops configured value. * * \param bsp session parameters. * diff --git a/lib/buffer.c b/lib/buffer.c index 41b1adc9fc..e976fecc1f 100644 --- a/lib/buffer.c +++ b/lib/buffer.c @@ -59,7 +59,7 @@ struct buffer_data { /* It should always be true that: 0 <= sp <= cp <= size */ /* Default buffer size (used if none specified). It is rounded up to the - next page boundery. */ + next page boundary. */ #define BUFFER_SIZE_DEFAULT 4096 #define BUFFER_DATA_FREE(D) XFREE(MTYPE_BUFFER_DATA, (D)) diff --git a/lib/command.c b/lib/command.c index 53aa064705..9cf93ea192 100644 --- a/lib/command.c +++ b/lib/command.c @@ -86,6 +86,9 @@ vector cmdvec = NULL; /* Host information structure. */ struct host host; +/* for vtysh, put together CLI trees only when switching into node */ +static bool defer_cli_tree; + /* * Returns host.name if any, otherwise * it returns the system hostname. @@ -142,31 +145,6 @@ static struct cmd_node config_node = { .node_exit = vty_config_node_exit, }; -static bool vty_check_node_for_xpath_decrement(enum node_type target_node, - enum node_type node) -{ - /* bgp afi-safi (`address-family <afi> <safi>`) node - * does not increment xpath_index. - * In order to use (`router bgp`) BGP_NODE's xpath as a base, - * retain xpath_index as 1 upon exiting from - * afi-safi node. - */ - - if (target_node == BGP_NODE - && (node == BGP_IPV4_NODE || node == BGP_IPV6_NODE - || node == BGP_IPV4M_NODE || node == BGP_IPV6M_NODE - || node == BGP_VPNV4_NODE || node == BGP_VPNV6_NODE - || node == BGP_EVPN_NODE || node == BGP_IPV4L_NODE - || node == BGP_IPV6L_NODE || node == BGP_FLOWSPECV4_NODE - || node == BGP_FLOWSPECV6_NODE)) - return false; - - if (target_node == INTERFACE_NODE && node == LINK_PARAMS_NODE) - return false; - - return true; -} - /* This is called from main when a daemon is invoked with -v or --version. */ void print_version(const char *progname) { @@ -285,6 +263,11 @@ const char *cmd_prompt(enum node_type node) return cnode->prompt; } +void cmd_defer_tree(bool val) +{ + defer_cli_tree = val; +} + /* Install a command into a node. */ void _install_element(enum node_type ntype, const struct cmd_element *cmd) { @@ -319,20 +302,50 @@ void _install_element(enum node_type ntype, const struct cmd_element *cmd) assert(hash_get(cnode->cmd_hash, (void *)cmd, hash_alloc_intern)); + if (cnode->graph_built || !defer_cli_tree) { + struct graph *graph = graph_new(); + struct cmd_token *token = + cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL); + graph_new_node(graph, token, + (void (*)(void *)) & cmd_token_del); + + cmd_graph_parse(graph, cmd); + cmd_graph_names(graph); + cmd_graph_merge(cnode->cmdgraph, graph, +1); + graph_delete_graph(graph); + + cnode->graph_built = true; + } + + vector_set(cnode->cmd_vector, (void *)cmd); + + if (ntype == VIEW_NODE) + _install_element(ENABLE_NODE, cmd); +} + +static void cmd_finalize_iter(struct hash_bucket *hb, void *arg) +{ + struct cmd_node *cnode = arg; + const struct cmd_element *cmd = hb->data; struct graph *graph = graph_new(); struct cmd_token *token = cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL); + graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del); cmd_graph_parse(graph, cmd); cmd_graph_names(graph); cmd_graph_merge(cnode->cmdgraph, graph, +1); graph_delete_graph(graph); +} - vector_set(cnode->cmd_vector, (void *)cmd); +void cmd_finalize_node(struct cmd_node *cnode) +{ + if (cnode->graph_built) + return; - if (ntype == VIEW_NODE) - _install_element(ENABLE_NODE, cmd); + hash_iterate(cnode->cmd_hash, cmd_finalize_iter, cnode); + cnode->graph_built = true; } void uninstall_element(enum node_type ntype, const struct cmd_element *cmd) @@ -368,15 +381,18 @@ void uninstall_element(enum node_type ntype, const struct cmd_element *cmd) vector_unset_value(cnode->cmd_vector, (void *)cmd); - struct graph *graph = graph_new(); - struct cmd_token *token = - cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL); - graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del); + if (cnode->graph_built) { + struct graph *graph = graph_new(); + struct cmd_token *token = + cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL); + graph_new_node(graph, token, + (void (*)(void *)) & cmd_token_del); - cmd_graph_parse(graph, cmd); - cmd_graph_names(graph); - cmd_graph_merge(cnode->cmdgraph, graph, -1); - graph_delete_graph(graph); + cmd_graph_parse(graph, cmd); + cmd_graph_names(graph); + cmd_graph_merge(cnode->cmdgraph, graph, -1); + graph_delete_graph(graph); + } if (ntype == VIEW_NODE) uninstall_element(ENABLE_NODE, cmd); @@ -503,6 +519,8 @@ static int config_write_host(struct vty *vty) static struct graph *cmd_node_graph(vector v, enum node_type ntype) { struct cmd_node *cnode = vector_slot(v, ntype); + + cmd_finalize_node(cnode); return cnode->cmdgraph; } @@ -879,13 +897,15 @@ static int cmd_execute_command_real(vector vline, enum cmd_filter_type filter, * a match before calling node_exit handlers below */ for (i = 0; i < up_level; i++) { + struct cmd_node *cnode; + if (node <= CONFIG_NODE) return CMD_NO_LEVEL_UP; + cnode = vector_slot(cmdvec, node); node = node_parent(node); - if (xpath_index > 0 - && vty_check_node_for_xpath_decrement(node, vty->node)) + if (xpath_index > 0 && !cnode->no_xpath) xpath_index--; } @@ -1019,12 +1039,13 @@ int cmd_execute_command(vector vline, struct vty *vty, /* This assumes all nodes above CONFIG_NODE are childs of * CONFIG_NODE */ while (vty->node > CONFIG_NODE) { + struct cmd_node *cnode = vector_slot(cmdvec, try_node); + try_node = node_parent(try_node); vty->node = try_node; - if (vty->xpath_index > 0 - && vty_check_node_for_xpath_decrement(try_node, - onode)) + if (vty->xpath_index > 0 && !cnode->no_xpath) vty->xpath_index--; + ret = cmd_execute_command_real(vline, FILTER_RELAXED, vty, cmd, 0); if (ret == CMD_SUCCESS || ret == CMD_WARNING @@ -1343,8 +1364,7 @@ void cmd_exit(struct vty *vty) } if (cnode->parent_node) vty->node = cnode->parent_node; - if (vty->xpath_index > 0 - && vty_check_node_for_xpath_decrement(vty->node, cnode->node)) + if (vty->xpath_index > 0 && !cnode->no_xpath) vty->xpath_index--; } @@ -1506,9 +1526,10 @@ int cmd_list_cmds(struct vty *vty, int do_permute) { struct cmd_node *node = vector_slot(cmdvec, vty->node); - if (do_permute) + if (do_permute) { + cmd_finalize_node(node); permute(vector_slot(node->cmdgraph->nodes, 0), vty); - else { + } else { /* loop over all commands at this node */ const struct cmd_element *element = NULL; for (unsigned int i = 0; i < vector_active(node->cmd_vector); @@ -1551,7 +1572,10 @@ DEFUN_HIDDEN(show_cli_graph, "Dump current command space as DOT graph\n") { struct cmd_node *cn = vector_slot(cmdvec, vty->node); - char *dot = cmd_graph_dump_dot(cn->cmdgraph); + char *dot; + + cmd_finalize_node(cn); + dot = cmd_graph_dump_dot(cn->cmdgraph); vty_out(vty, "%s\n", dot); XFREE(MTYPE_TMP, dot); diff --git a/lib/command.h b/lib/command.h index 8a7c9a2048..c888356d61 100644 --- a/lib/command.h +++ b/lib/command.h @@ -210,6 +210,12 @@ struct cmd_node { /* Hashed index of command node list, for de-dupping primarily */ struct hash *cmd_hash; + + /* set as soon as any command is in cmdgraph */ + bool graph_built; + + /* don't decrement vty->xpath_index on leaving this node */ + bool no_xpath; }; /* Return value of the commands. */ @@ -234,7 +240,7 @@ struct cmd_node { /* Argc max counts. */ #define CMD_ARGC_MAX 256 -/* Turn off these macros when uisng cpp with extract.pl */ +/* Turn off these macros when using cpp with extract.pl */ #ifndef VTYSH_EXTRACT_PL /* helper defines for end-user DEFUN* macros */ @@ -526,6 +532,12 @@ extern void _install_element(enum node_type, const struct cmd_element *); * deprecated/hidden) are not reversed. */ extern void uninstall_element(enum node_type, const struct cmd_element *); +/* construct CLI tree only when entering nodes */ +extern void cmd_defer_tree(bool val); + +/* finish CLI tree for node when above is true (noop otherwise) */ +extern void cmd_finalize_node(struct cmd_node *node); + /* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated string with a space between each element (allocated using XMALLOC(MTYPE_TMP)). Returns NULL if shift >= argc. */ diff --git a/lib/command_graph.c b/lib/command_graph.c index 15c8302e63..09d802e796 100644 --- a/lib/command_graph.c +++ b/lib/command_graph.c @@ -77,16 +77,17 @@ struct cmd_token *cmd_token_dup(struct cmd_token *token) return copy; } -void cmd_token_varname_set(struct cmd_token *token, const char *varname) +static void cmd_token_varname_do(struct cmd_token *token, const char *varname, + uint8_t varname_src) { - XFREE(MTYPE_CMD_VAR, token->varname); - if (!varname) { - token->varname = NULL; + if (token->varname_src >= varname_src) return; - } + + XFREE(MTYPE_CMD_VAR, token->varname); size_t len = strlen(varname), i; token->varname = XMALLOC(MTYPE_CMD_VAR, len + 1); + token->varname_src = varname_src; for (i = 0; i < len; i++) switch (varname[i]) { @@ -102,6 +103,80 @@ void cmd_token_varname_set(struct cmd_token *token, const char *varname) token->varname[len] = '\0'; } +void cmd_token_varname_set(struct cmd_token *token, const char *varname) +{ + if (varname) { + cmd_token_varname_do(token, varname, VARNAME_EXPLICIT); + return; + } + if (token->type == VARIABLE_TKN) { + if (strcmp(token->text, "WORD") && strcmp(token->text, "NAME")) + cmd_token_varname_do(token, token->text, VARNAME_TEXT); + } +} + +static void cmd_token_varname_fork(struct graph_node *node, + struct cmd_token *prevtoken) +{ + for (size_t i = 0; i < vector_active(node->to); i++) { + struct graph_node *next = vector_slot(node->to, i); + struct cmd_token *nexttoken = next->data; + + if (nexttoken->type == FORK_TKN) { + cmd_token_varname_fork(next, prevtoken); + continue; + } + if (nexttoken->varname) + continue; + if (!IS_VARYING_TOKEN(nexttoken->type)) + continue; + + cmd_token_varname_do(nexttoken, prevtoken->text, VARNAME_TEXT); + } +} + +void cmd_token_varname_join(struct graph_node *join, const char *varname) +{ + if (!varname) + return; + + for (size_t i = 0; i < vector_active(join->from); i++) { + struct graph_node *prev = vector_slot(join->from, i); + struct cmd_token *token = prev->data; + + if (token->type == JOIN_TKN) + cmd_token_varname_join(prev, varname); + else if (token->type < SPECIAL_TKN) + cmd_token_varname_do(token, varname, VARNAME_EXPLICIT); + } +} + +void cmd_token_varname_seqappend(struct graph_node *node) +{ + struct graph_node *prevnode = node; + struct cmd_token *token = node->data; + struct cmd_token *prevtoken; + + if (token->type == WORD_TKN) + return; + + do { + if (vector_active(prevnode->from) != 1) + return; + + prevnode = vector_slot(prevnode->from, 0); + prevtoken = prevnode->data; + } while (prevtoken->type == FORK_TKN); + + if (prevtoken->type != WORD_TKN) + return; + + if (token->type == FORK_TKN) + cmd_token_varname_fork(node, prevtoken); + else + cmd_token_varname_do(token, prevtoken->text, VARNAME_TEXT); +} + static bool cmd_nodes_link(struct graph_node *from, struct graph_node *to) { for (size_t i = 0; i < vector_active(from->to); i++) @@ -357,72 +432,6 @@ void cmd_graph_merge(struct graph *old, struct graph *new, int direction) vector_slot(new->nodes, 0), direction); } -static void cmd_node_names(struct graph_node *gn, struct graph_node *join, - const char *prevname) -{ - size_t i; - struct cmd_token *tok = gn->data, *jointok; - struct graph_node *stop = cmd_loopstop(gn); - - switch (tok->type) { - case WORD_TKN: - prevname = tok->text; - break; - - case VARIABLE_TKN: - if (!tok->varname && strcmp(tok->text, "WORD") - && strcmp(tok->text, "NAME")) - cmd_token_varname_set(tok, tok->text); - /* fallthrough */ - case RANGE_TKN: - case IPV4_TKN: - case IPV4_PREFIX_TKN: - case IPV6_TKN: - case IPV6_PREFIX_TKN: - case MAC_TKN: - case MAC_PREFIX_TKN: - if (!tok->varname && prevname) - cmd_token_varname_set(tok, prevname); - prevname = NULL; - break; - - case START_TKN: - case JOIN_TKN: - case NEG_ONLY_TKN: - /* "<foo|bar> WORD" -> word is not "bar" or "foo" */ - prevname = NULL; - break; - - case FORK_TKN: - /* apply "<A.B.C.D|X:X::X:X>$name" */ - jointok = tok->forkjoin->data; - if (!jointok->varname) - break; - for (i = 0; i < vector_active(tok->forkjoin->from); i++) { - struct graph_node *tail = - vector_slot(tok->forkjoin->from, i); - struct cmd_token *tailtok = tail->data; - if (tail == gn || tailtok->varname) - continue; - cmd_token_varname_set(tailtok, jointok->varname); - } - break; - - case END_TKN: - return; - } - - for (i = 0; i < vector_active(gn->to); i++) { - struct graph_node *next = vector_slot(gn->to, i); - if (next == stop || next == join) - continue; - cmd_node_names(next, join, prevname); - } - - if (tok->type == FORK_TKN && tok->forkjoin != join) - cmd_node_names(tok->forkjoin, join, NULL); -} - void cmd_graph_names(struct graph *graph) { struct graph_node *start; @@ -451,12 +460,10 @@ void cmd_graph_names(struct graph *graph) struct cmd_token *tok1 = next1->data; /* the other one needs to be "no" (only one will match here) */ if ((tok0->type == WORD_TKN && !strcmp(tok0->text, "no"))) - cmd_token_varname_set(tok0, "no"); + cmd_token_varname_do(tok0, "no", VARNAME_AUTO); if ((tok1->type == WORD_TKN && !strcmp(tok1->text, "no"))) - cmd_token_varname_set(tok1, "no"); + cmd_token_varname_do(tok1, "no", VARNAME_AUTO); } while (0); - - cmd_node_names(start, NULL, NULL); } #ifndef BUILDING_CLIPPY diff --git a/lib/command_graph.h b/lib/command_graph.h index 86157f872e..ed4da6aa4c 100644 --- a/lib/command_graph.h +++ b/lib/command_graph.h @@ -79,11 +79,20 @@ enum { CMD_ATTR_NORMAL, CMD_ATTR_YANG, }; +enum varname_src { + VARNAME_NONE = 0, + VARNAME_AUTO, + VARNAME_VAR, + VARNAME_TEXT, + VARNAME_EXPLICIT, +}; + /* Command token struct. */ struct cmd_token { enum cmd_token_type type; // token type uint8_t attr; // token attributes bool allowrepeat; // matcher allowed to match token repetitively? + uint8_t varname_src; uint32_t refcnt; char *text; // token text @@ -119,6 +128,8 @@ extern struct cmd_token *cmd_token_new(enum cmd_token_type, uint8_t attr, extern struct cmd_token *cmd_token_dup(struct cmd_token *); extern void cmd_token_del(struct cmd_token *); extern void cmd_token_varname_set(struct cmd_token *token, const char *varname); +extern void cmd_token_varname_seqappend(struct graph_node *n); +extern void cmd_token_varname_join(struct graph_node *n, const char *varname); extern void cmd_graph_parse(struct graph *graph, const struct cmd_element *cmd); extern void cmd_graph_names(struct graph *graph); diff --git a/lib/command_match.h b/lib/command_match.h index fcb333120f..0488cc1a1f 100644 --- a/lib/command_match.h +++ b/lib/command_match.h @@ -68,7 +68,7 @@ enum match_type { * @param[in] vline vectorized input string * @param[out] argv pointer to argument list if successful match, NULL * otherwise. The elements of this list are pointers to struct cmd_token - * and represent the sequence of tokens matched by the inpu. The ->arg + * and represent the sequence of tokens matched by the input. The ->arg * field of each token points to a copy of the input matched on it. These * may be safely deleted or modified. * @param[out] element pointer to matched cmd_element if successful match, diff --git a/lib/command_parse.y b/lib/command_parse.y index 3e2cdc79af..35c119691b 100644 --- a/lib/command_parse.y +++ b/lib/command_parse.y @@ -217,10 +217,12 @@ cmd_token: { if ((ctx->currnode = graph_add_edge (ctx->currnode, $1)) != $1) graph_delete_node (ctx->graph, $1); + cmd_token_varname_seqappend($1); } | selector { graph_add_edge (ctx->currnode, $1.start); + cmd_token_varname_seqappend($1.start); ctx->currnode = $1.end; } ; @@ -295,9 +297,8 @@ placeholder_token_real: placeholder_token: placeholder_token_real varname_token { - struct cmd_token *token = $$->data; $$ = $1; - cmd_token_varname_set (token, $2); + cmd_token_varname_set ($$->data, $2); XFREE (MTYPE_LEX, $2); }; @@ -306,7 +307,7 @@ placeholder_token: selector: '<' selector_seq_seq '>' varname_token { $$ = $2; - cmd_token_varname_set ($2.end->data, $4); + cmd_token_varname_join ($2.end, $4); XFREE (MTYPE_LEX, $4); }; @@ -338,11 +339,11 @@ selector: '{' selector_seq_seq '}' varname_token * 1) this allows "at least 1 of" semantics, which are otherwise impossible * 2) this would add a start->end->start loop in the graph that the current * loop-avoidal fails to handle - * just use [{a|b}] if neccessary, that will work perfectly fine, and reason + * just use [{a|b}] if necessary, that will work perfectly fine, and reason * #1 is good enough to keep it this way. */ loopcheck(ctx, &$$); - cmd_token_varname_set ($2.end->data, $4); + cmd_token_varname_join ($2.end, $4); XFREE (MTYPE_LEX, $4); }; @@ -359,6 +360,7 @@ selector_token_seq: selector_token_seq selector_token { graph_add_edge ($1.end, $2.start); + cmd_token_varname_seqappend($2.start); $$.start = $1.start; $$.end = $2.end; } @@ -370,7 +372,7 @@ selector: '[' selector_seq_seq ']' varname_token { $$ = $2; graph_add_edge ($$.start, $$.end); - cmd_token_varname_set ($2.end->data, $4); + cmd_token_varname_join ($2.end, $4); XFREE (MTYPE_LEX, $4); } ; @@ -383,7 +385,7 @@ selector: EXCL_BRACKET selector_seq_seq ']' varname_token $$ = $2; graph_add_edge ($$.start, neg_only); graph_add_edge (neg_only, $$.end); - cmd_token_varname_set ($2.end->data, $4); + cmd_token_varname_join ($2.end, $4); XFREE (MTYPE_LEX, $4); } ; @@ -60,7 +60,7 @@ int db_init(const char *path_fmt, ...) (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE), NULL) != SQLITE_OK) { if (dbp == NULL) { - zlog_warn("%s: failed to open dabatase '%s'", __func__, + zlog_warn("%s: failed to open database '%s'", __func__, path); return -1; } diff --git a/lib/distribute.h b/lib/distribute.h index 83682dea6a..6b3226e8b4 100644 --- a/lib/distribute.h +++ b/lib/distribute.h @@ -29,7 +29,7 @@ extern "C" { #endif -/* Disctirubte list types. */ +/* Distribute list types. */ enum distribute_type { DISTRIBUTE_V4_IN, DISTRIBUTE_V6_IN, diff --git a/lib/elf_py.c b/lib/elf_py.c index 1c306893ad..f230add695 100644 --- a/lib/elf_py.c +++ b/lib/elf_py.c @@ -636,6 +636,9 @@ static Elf_Scn *elf_find_addr(struct elffile *ef, uint64_t addr, size_t *idx) Elf_Scn *scn = elf_getscn(ef->elf, i); GElf_Shdr _shdr, *shdr = gelf_getshdr(scn, &_shdr); + /* virtual address is kinda meaningless for TLS sections */ + if (shdr->sh_flags & SHF_TLS) + continue; if (addr < shdr->sh_addr || addr >= shdr->sh_addr + shdr->sh_size) continue; diff --git a/lib/ferr.c b/lib/ferr.c index 513ef5ebec..e5b6d7552d 100644 --- a/lib/ferr.c +++ b/lib/ferr.c @@ -157,13 +157,7 @@ void log_ref_display(struct vty *vty, uint32_t code, bool json) } } - if (json) { - const char *str = json_object_to_json_string_ext( - top, JSON_C_TO_STRING_PRETTY); - vty_out(vty, "%s\n", str); - json_object_free(top); - } - + vty_json(vty, top); list_delete(&errlist); } diff --git a/lib/ferr.h b/lib/ferr.h index 4e95431cea..c27601f66c 100644 --- a/lib/ferr.h +++ b/lib/ferr.h @@ -34,7 +34,7 @@ extern "C" { /* return type when this error indication stuff is used. * * guaranteed to have boolean evaluation to "false" when OK, "true" when error - * (i.e. can be changed to pointer in the future if neccessary) + * (i.e. can be changed to pointer in the future if necessary) * * For checking, always use "if (value)", nothing else. * Do _NOT_ use any integer constant (!= 0), or sign check (< 0). diff --git a/lib/filter.c b/lib/filter.c index 744ea9c480..fc4b578e77 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -192,7 +192,7 @@ void access_list_delete(struct access_list *access) access_list_free(access); } -/* Insert new access list to list of access_list. Each acceess_list +/* Insert new access list to list of access_list. Each access_list is sorted by the name. */ static struct access_list *access_list_insert(afi_t afi, const char *name) { @@ -387,7 +387,7 @@ void access_list_filter_add(struct access_list *access, struct filter *replace; struct filter *point; - /* Automatic asignment of seq no. */ + /* Automatic assignment of seq no. */ if (filter->seq == -1) filter->seq = filter_new_seq_get(access); @@ -558,18 +558,12 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi, json_rule); else { if (json) { - char buf[BUFSIZ]; - - json_object_string_add( - json_rule, "address", - inet_ntop(AF_INET, - &filter->addr, buf, - sizeof(buf))); - json_object_string_add( - json_rule, "mask", - inet_ntop(AF_INET, - &filter->addr_mask, - buf, sizeof(buf))); + json_object_string_addf( + json_rule, "address", "%pI4", + &filter->addr); + json_object_string_addf( + json_rule, "mask", "%pI4", + &filter->addr_mask); } else { if (filter->addr_mask.s_addr == 0xffffffff) @@ -589,14 +583,7 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi, } } - if (json) { - vty_out(vty, "%s\n", - json_object_to_json_string_ext( - json, JSON_C_TO_STRING_PRETTY)); - json_object_free(json); - } - - return CMD_SUCCESS; + return vty_json(vty, json); } /* show MAC access list - this only has MAC filters for now*/ @@ -612,7 +599,7 @@ DEFUN (show_mac_access_list, DEFUN (show_mac_access_list_name, show_mac_access_list_name_cmd, - "show mac access-list WORD", + "show mac access-list ACCESSLIST_MAC_NAME", SHOW_STR "mac access lists\n" "List mac access lists\n" @@ -635,15 +622,11 @@ DEFUN (show_ip_access_list, DEFUN (show_ip_access_list_name, show_ip_access_list_name_cmd, - "show ip access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD> [json]", + "show ip access-list ACCESSLIST4_NAME [json]", SHOW_STR IP_STR "List IP access lists\n" - "IP standard access list\n" - "IP extended access list\n" - "IP standard access list (expanded range)\n" - "IP extended access list (expanded range)\n" - "IP zebra access-list\n" + "IP access-list name\n" JSON_STR) { bool uj = use_json(argc, argv); @@ -665,11 +648,11 @@ DEFUN (show_ipv6_access_list, DEFUN (show_ipv6_access_list_name, show_ipv6_access_list_name_cmd, - "show ipv6 access-list WORD [json]", + "show ipv6 access-list ACCESSLIST6_NAME [json]", SHOW_STR IPV6_STR "List IPv6 access lists\n" - "IPv6 zebra access-list\n" + "IPv6 access-list name\n" JSON_STR) { bool uj = use_json(argc, argv); @@ -685,21 +668,15 @@ static void config_write_access_cisco(struct vty *vty, struct filter *mfilter, filter = &mfilter->u.cfilter; if (json) { - char buf[BUFSIZ]; - json_object_boolean_add(json, "extended", !!filter->extended); - json_object_string_add( - json, "sourceAddress", - inet_ntop(AF_INET, &filter->addr, buf, sizeof(buf))); - json_object_string_add(json, "sourceMask", - inet_ntop(AF_INET, &filter->addr_mask, - buf, sizeof(buf))); - json_object_string_add( - json, "destinationAddress", - inet_ntop(AF_INET, &filter->mask, buf, sizeof(buf))); - json_object_string_add(json, "destinationMask", - inet_ntop(AF_INET, &filter->mask_mask, - buf, sizeof(buf))); + json_object_string_addf(json, "sourceAddress", "%pI4", + &filter->addr); + json_object_string_addf(json, "sourceMask", "%pI4", + &filter->addr_mask); + json_object_string_addf(json, "destinationAddress", "%pI4", + &filter->mask); + json_object_string_addf(json, "destinationMask", "%pI4", + &filter->mask_mask); } else { vty_out(vty, " ip"); if (filter->addr_mask.s_addr == 0xffffffff) @@ -734,16 +711,13 @@ static void config_write_access_zebra(struct vty *vty, struct filter *mfilter, p = &filter->prefix; if (json) { - json_object_string_add(json, "prefix", - prefix2str(p, buf, sizeof(buf))); + json_object_string_addf(json, "prefix", "%pFX", p); json_object_boolean_add(json, "exact-match", !!filter->exact); } else { if (p->prefixlen == 0 && !filter->exact) vty_out(vty, " any"); else if (p->family == AF_INET6 || p->family == AF_INET) - vty_out(vty, " %s/%d%s", - inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ), - p->prefixlen, + vty_out(vty, " %pFX%s", p, filter->exact ? " exact-match" : ""); else if (p->family == AF_ETHERNET) { if (p->prefixlen == 0) @@ -843,12 +817,62 @@ static void access_list_init_ipv4(void) install_element(ENABLE_NODE, &show_ip_access_list_name_cmd); } +static void access_list_autocomplete_afi(afi_t afi, vector comps, + struct cmd_token *token) +{ + struct access_list *access; + struct access_list *next; + struct access_master *master; + + master = access_master_get(afi); + if (master == NULL) + return; + + for (access = master->str.head; access; access = next) { + next = access->next; + vector_set(comps, XSTRDUP(MTYPE_COMPLETION, access->name)); + } +} + static struct cmd_node access_ipv6_node = { .name = "ipv6 access list", .node = ACCESS_IPV6_NODE, .prompt = "", }; +static void access_list_autocomplete(vector comps, struct cmd_token *token) +{ + access_list_autocomplete_afi(AFI_IP, comps, token); + access_list_autocomplete_afi(AFI_IP6, comps, token); + access_list_autocomplete_afi(AFI_L2VPN, comps, token); +} + +static void access_list4_autocomplete(vector comps, struct cmd_token *token) +{ + access_list_autocomplete_afi(AFI_IP, comps, token); +} + +static void access_list6_autocomplete(vector comps, struct cmd_token *token) +{ + access_list_autocomplete_afi(AFI_IP6, comps, token); +} + +static void access_list_mac_autocomplete(vector comps, struct cmd_token *token) +{ + access_list_autocomplete_afi(AFI_L2VPN, comps, token); +} + +static const struct cmd_variable_handler access_list_handlers[] = { + {.tokenname = "ACCESSLIST_NAME", + .completions = access_list_autocomplete}, + {.tokenname = "ACCESSLIST4_NAME", + .completions = access_list4_autocomplete}, + {.tokenname = "ACCESSLIST6_NAME", + .completions = access_list6_autocomplete}, + {.tokenname = "ACCESSLIST_MAC_NAME", + .completions = access_list_mac_autocomplete}, + {.completions = NULL}}; + static void access_list_reset_ipv6(void) { struct access_list *access; @@ -878,6 +902,8 @@ static void access_list_init_ipv6(void) void access_list_init(void) { + cmd_variable_handler_register(access_list_handlers); + access_list_init_ipv4(); access_list_init_ipv6(); access_list_init_mac(); diff --git a/lib/filter.h b/lib/filter.h index d1956ec019..b378288c58 100644 --- a/lib/filter.h +++ b/lib/filter.h @@ -234,15 +234,19 @@ bool plist_is_dup(const struct lyd_node *dnode, struct plist_dup_args *pda); struct lyd_node; struct vty; -extern int access_list_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2); -extern void access_list_show(struct vty *vty, struct lyd_node *dnode, +extern int access_list_cmp(const struct lyd_node *dnode1, + const struct lyd_node *dnode2); +extern void access_list_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); -extern void access_list_remark_show(struct vty *vty, struct lyd_node *dnode, +extern void access_list_remark_show(struct vty *vty, + const struct lyd_node *dnode, bool show_defaults); -extern int prefix_list_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2); -extern void prefix_list_show(struct vty *vty, struct lyd_node *dnode, +extern int prefix_list_cmp(const struct lyd_node *dnode1, + const struct lyd_node *dnode2); +extern void prefix_list_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); -extern void prefix_list_remark_show(struct vty *vty, struct lyd_node *dnode, +extern void prefix_list_remark_show(struct vty *vty, + const struct lyd_node *dnode, bool show_defaults); void filter_cli_init(void); diff --git a/lib/filter_cli.c b/lib/filter_cli.c index 45c7544a3b..fb40c527dd 100644 --- a/lib/filter_cli.c +++ b/lib/filter_cli.c @@ -36,10 +36,7 @@ #endif /* VTYSH_EXTRACT_PL */ #define ACCESS_LIST_STR "Access list entry\n" -#define ACCESS_LIST_LEG_STR "IP standard access list\n" -#define ACCESS_LIST_ELEG_STR "IP extended access list\n" -#define ACCESS_LIST_ELEG_EXT_STR "IP extended access list (expanded range)\n" -#define ACCESS_LIST_ZEBRA_STR "Access list entry\n" +#define ACCESS_LIST_ZEBRA_STR "Access list name\n" #define ACCESS_LIST_SEQ_STR \ "Sequence number of an entry\n" \ "Sequence number\n" @@ -137,7 +134,7 @@ DEFPY_YANG( access_list_std, access_list_std_cmd, "access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>", ACCESS_LIST_STR - ACCESS_LIST_LEG_STR + ACCESS_LIST_ZEBRA_STR ACCESS_LIST_SEQ_STR ACCESS_LIST_ACTION_STR "A single host address\n" @@ -214,7 +211,7 @@ DEFPY_YANG( "no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>", NO_STR ACCESS_LIST_STR - ACCESS_LIST_LEG_STR + ACCESS_LIST_ZEBRA_STR ACCESS_LIST_SEQ_STR ACCESS_LIST_ACTION_STR "A single host address\n" @@ -258,7 +255,7 @@ DEFPY_YANG( access_list_ext, access_list_ext_cmd, "access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>", ACCESS_LIST_STR - ACCESS_LIST_ELEG_STR + ACCESS_LIST_ZEBRA_STR ACCESS_LIST_SEQ_STR ACCESS_LIST_ACTION_STR "IPv4 address\n" @@ -377,7 +374,7 @@ DEFPY_YANG( "no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>", NO_STR ACCESS_LIST_STR - ACCESS_LIST_ELEG_STR + ACCESS_LIST_ZEBRA_STR ACCESS_LIST_SEQ_STR ACCESS_LIST_ACTION_STR "Any Internet Protocol\n" @@ -825,7 +822,7 @@ DEFPY_YANG( ALIAS( no_ipv6_access_list_remark, no_ipv6_access_list_remark_line_cmd, - "no ipv6 access-list WORD$name remark LINE...", + "no ipv6 access-list ACCESSLIST6_NAME$name remark LINE...", NO_STR IPV6_STR ACCESS_LIST_STR @@ -835,7 +832,7 @@ ALIAS( DEFPY_YANG( mac_access_list, mac_access_list_cmd, - "mac access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X:X:X:X:X$mac|any>", + "mac access-list ACCESSLIST_MAC_NAME$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X:X:X:X:X$mac|any>", MAC_STR ACCESS_LIST_STR ACCESS_LIST_ZEBRA_STR @@ -901,7 +898,7 @@ DEFPY_YANG( DEFPY_YANG( no_mac_access_list, no_mac_access_list_cmd, - "no mac access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X:X:X:X:X$mac|any>", + "no mac access-list ACCESSLIST_MAC_NAME$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X:X:X:X:X$mac|any>", NO_STR MAC_STR ACCESS_LIST_STR @@ -941,7 +938,7 @@ DEFPY_YANG( DEFPY_YANG( no_mac_access_list_all, no_mac_access_list_all_cmd, - "no mac access-list WORD$name", + "no mac access-list ACCESSLIST_MAC_NAME$name", NO_STR MAC_STR ACCESS_LIST_STR @@ -958,7 +955,7 @@ DEFPY_YANG( DEFPY_YANG( mac_access_list_remark, mac_access_list_remark_cmd, - "mac access-list WORD$name remark LINE...", + "mac access-list ACCESSLIST_MAC_NAME$name remark LINE...", MAC_STR ACCESS_LIST_STR ACCESS_LIST_ZEBRA_STR @@ -983,7 +980,7 @@ DEFPY_YANG( DEFPY_YANG( no_mac_access_list_remark, no_mac_access_list_remark_cmd, - "no mac access-list WORD$name remark", + "no mac access-list ACCESSLIST_MAC_NAME$name remark", NO_STR MAC_STR ACCESS_LIST_STR @@ -1007,7 +1004,7 @@ DEFPY_YANG( ALIAS( no_mac_access_list_remark, no_mac_access_list_remark_line_cmd, - "no mac access-list WORD$name remark LINE...", + "no mac access-list ACCESSLIST_MAC_NAME$name remark LINE...", NO_STR MAC_STR ACCESS_LIST_STR @@ -1015,7 +1012,8 @@ ALIAS( ACCESS_LIST_REMARK_STR ACCESS_LIST_REMARK_LINE_STR) -int access_list_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2) +int access_list_cmp(const struct lyd_node *dnode1, + const struct lyd_node *dnode2) { uint32_t seq1 = yang_dnode_get_uint32(dnode1, "./sequence"); uint32_t seq2 = yang_dnode_get_uint32(dnode2, "./sequence"); @@ -1023,7 +1021,7 @@ int access_list_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2) return seq1 - seq2; } -void access_list_show(struct vty *vty, struct lyd_node *dnode, +void access_list_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { int type = yang_dnode_get_enum(dnode, "../type"); @@ -1137,7 +1135,7 @@ void access_list_show(struct vty *vty, struct lyd_node *dnode, vty_out(vty, "\n"); } -void access_list_remark_show(struct vty *vty, struct lyd_node *dnode, +void access_list_remark_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { int type = yang_dnode_get_enum(dnode, "../type"); @@ -1658,7 +1656,8 @@ ALIAS( ACCESS_LIST_REMARK_STR ACCESS_LIST_REMARK_LINE_STR) -int prefix_list_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2) +int prefix_list_cmp(const struct lyd_node *dnode1, + const struct lyd_node *dnode2) { uint32_t seq1 = yang_dnode_get_uint32(dnode1, "./sequence"); uint32_t seq2 = yang_dnode_get_uint32(dnode2, "./sequence"); @@ -1666,7 +1665,7 @@ int prefix_list_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2) return seq1 - seq2; } -void prefix_list_show(struct vty *vty, struct lyd_node *dnode, +void prefix_list_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { int type = yang_dnode_get_enum(dnode, "../type"); @@ -1725,7 +1724,7 @@ void prefix_list_show(struct vty *vty, struct lyd_node *dnode, vty_out(vty, "\n"); } -void prefix_list_remark_show(struct vty *vty, struct lyd_node *dnode, +void prefix_list_remark_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { int type = yang_dnode_get_enum(dnode, "../type"); diff --git a/lib/frr_zmq.c b/lib/frr_zmq.c index 4e947a8a84..e572558de1 100644 --- a/lib/frr_zmq.c +++ b/lib/frr_zmq.c @@ -19,7 +19,7 @@ /* * IF YOU MODIFY THIS FILE PLEASE RUN `make check` and ensure that - * the test_zmq.c unit test is still working. There are dependancies + * the test_zmq.c unit test is still working. There are dependencies * between the two that are extremely fragile. My understanding * is that there is specialized ownership of the cb pointer based * upon what is happening. Those assumptions are supposed to be diff --git a/lib/frrcu.h b/lib/frrcu.h index 3808259040..ae840926b5 100644 --- a/lib/frrcu.h +++ b/lib/frrcu.h @@ -17,6 +17,8 @@ #ifndef _FRRCU_H #define _FRRCU_H +#include <assert.h> + #include "memory.h" #include "atomlist.h" diff --git a/lib/frrlua.c b/lib/frrlua.c index 00491568f6..535649eff2 100644 --- a/lib/frrlua.c +++ b/lib/frrlua.c @@ -223,6 +223,21 @@ void *lua_toin6addr(lua_State *L, int idx) return in6addr; } +void lua_pushipaddr(lua_State *L, const struct ipaddr *addr) +{ + if (IS_IPADDR_V4(addr)) + lua_pushinaddr(L, &addr->ipaddr_v4); + else + lua_pushin6addr(L, &addr->ipaddr_v6); +} + +void lua_pushethaddr(lua_State *L, const struct ethaddr *addr) +{ + lua_newtable(L); + lua_pushinteger(L, *(addr->octet)); + lua_setfield(L, -2, "octet"); +} + void lua_pushsockunion(lua_State *L, const union sockunion *su) { char buf[SU_ADDRSTRLEN]; @@ -297,6 +312,58 @@ void *lua_tointegerp(lua_State *L, int idx) return num; } +void lua_pushnexthop(lua_State *L, const struct nexthop *nexthop) +{ + lua_newtable(L); + lua_pushinteger(L, nexthop->vrf_id); + lua_setfield(L, -2, "vrf_id"); + lua_pushinteger(L, nexthop->ifindex); + lua_setfield(L, -2, "ifindex"); + lua_pushinteger(L, nexthop->type); + lua_setfield(L, -2, "type"); + lua_pushinteger(L, nexthop->flags); + lua_setfield(L, -2, "flags"); + if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) { + lua_pushinteger(L, nexthop->bh_type); + lua_setfield(L, -2, "bh_type"); + } else if (nexthop->type == NEXTHOP_TYPE_IPV4) { + lua_pushinaddr(L, &nexthop->gate.ipv4); + lua_setfield(L, -2, "gate"); + } else if (nexthop->type == NEXTHOP_TYPE_IPV6) { + lua_pushin6addr(L, &nexthop->gate.ipv6); + lua_setfield(L, -2, "gate"); + } + lua_pushinteger(L, nexthop->nh_label_type); + lua_setfield(L, -2, "nh_label_type"); + lua_pushinteger(L, nexthop->weight); + lua_setfield(L, -2, "weight"); + lua_pushinteger(L, nexthop->backup_num); + lua_setfield(L, -2, "backup_num"); + lua_pushinteger(L, *(nexthop->backup_idx)); + lua_setfield(L, -2, "backup_idx"); + if (nexthop->nh_encap_type == NET_VXLAN) { + lua_pushinteger(L, nexthop->nh_encap.vni); + lua_setfield(L, -2, "vni"); + } + lua_pushinteger(L, nexthop->nh_encap_type); + lua_setfield(L, -2, "nh_encap_type"); + lua_pushinteger(L, nexthop->srte_color); + lua_setfield(L, -2, "srte_color"); +} + +void lua_pushnexthop_group(lua_State *L, const struct nexthop_group *ng) +{ + lua_newtable(L); + struct nexthop *nexthop; + int i = 0; + + for (ALL_NEXTHOPS_PTR(ng, nexthop)) { + lua_pushnexthop(L, nexthop); + lua_seti(L, -2, i); + i++; + } +} + void lua_decode_stringp(lua_State *L, int idx, char *str) { strlcpy(str, lua_tostring(L, idx), strlen(str) + 1); diff --git a/lib/frrlua.h b/lib/frrlua.h index 3e16c82e22..a82009a779 100644 --- a/lib/frrlua.h +++ b/lib/frrlua.h @@ -100,6 +100,10 @@ void lua_pushin6addr(lua_State *L, const struct in6_addr *addr); void lua_decode_in6addr(lua_State *L, int idx, struct in6_addr *addr); +void lua_pushipaddr(lua_State *L, const struct ipaddr *addr); + +void lua_pushethaddr(lua_State *L, const struct ethaddr *addr); + /* * Converts the Lua value at idx to an in6_addr. * @@ -138,6 +142,10 @@ void lua_decode_sockunion(lua_State *L, int idx, union sockunion *su); */ void *lua_tosockunion(lua_State *L, int idx); +void lua_pushnexthop_group(lua_State *L, const struct nexthop_group *ng); + +void lua_pushnexthop(lua_State *L, const struct nexthop *nexthop); + /* * Converts an int to a Lua value and pushes it on the stack. */ diff --git a/lib/frrscript.c b/lib/frrscript.c index 0e0d3c030c..4fee79991a 100644 --- a/lib/frrscript.c +++ b/lib/frrscript.c @@ -32,6 +32,96 @@ DEFINE_MTYPE_STATIC(LIB, SCRIPT, "Scripting"); +/* + * Script name hash utilities + */ + +struct frrscript_names_head frrscript_names_hash; + +/* + * Wrapper for frrscript_names_add + * Use this to register hook calls when a daemon starts up + */ +int frrscript_names_add_function_name(const char *function_name) +{ + struct frrscript_names_entry *insert = + XCALLOC(MTYPE_SCRIPT, sizeof(*insert)); + strlcpy(insert->function_name, function_name, + sizeof(insert->function_name)); + + if (frrscript_names_add(&frrscript_names_hash, insert)) { + zlog_warn( + "Failed to add hook call function name to script_names"); + return 1; + } + return 0; +} + +void frrscript_names_destroy(void) +{ + struct frrscript_names_entry *ne; + + while ((ne = frrscript_names_pop(&frrscript_names_hash))) + XFREE(MTYPE_SCRIPT, ne); +} + +/* + * Given a function_name, set its script_name. function_names and script_names + * are one-to-one. Each set will wipe the previous script_name. + * Return 0 if set was successful, else 1. + * + * script_name is the base name of the file, without .lua. + */ +int frrscript_names_set_script_name(const char *function_name, + const char *script_name) +{ + struct frrscript_names_entry lookup; + + strlcpy(lookup.function_name, function_name, + sizeof(lookup.function_name)); + struct frrscript_names_entry *snhe = + frrscript_names_find(&frrscript_names_hash, &lookup); + if (!snhe) + return 1; + strlcpy(snhe->script_name, script_name, sizeof(snhe->script_name)); + return 0; +} + +/* + * Given a function_name, get its script_name. + * Return NULL if function_name not found. + * + * script_name is the base name of the file, without .lua. + */ +char *frrscript_names_get_script_name(const char *function_name) +{ + struct frrscript_names_entry lookup; + + strlcpy(lookup.function_name, function_name, + sizeof(lookup.function_name)); + struct frrscript_names_entry *snhe = + frrscript_names_find(&frrscript_names_hash, &lookup); + if (!snhe) + return NULL; + + if (snhe->script_name[0] == '\0') + return NULL; + + return snhe->script_name; +} + +uint32_t frrscript_names_hash_key(const struct frrscript_names_entry *snhe) +{ + return string_hash_make(snhe->function_name); +} + +int frrscript_names_hash_cmp(const struct frrscript_names_entry *snhe1, + const struct frrscript_names_entry *snhe2) +{ + return strncmp(snhe1->function_name, snhe2->function_name, + sizeof(snhe1->function_name)); +} + /* Codecs */ struct frrscript_codec frrscript_codecs_lib[] = { @@ -226,7 +316,7 @@ void *frrscript_get_result(struct frrscript *fs, const char *function_name, p = lua_to(lfs->L, 2); /* At the end, the Lua state should be same as it was at the start - * i.e. containing soley the returned table. + * i.e. containing solely the returned table. */ assert(lua_gettop(lfs->L) == 1); assert(lua_istable(lfs->L, -1) == 1); diff --git a/lib/frrscript.h b/lib/frrscript.h index c089df61fc..4db3e6f1b2 100644 --- a/lib/frrscript.h +++ b/lib/frrscript.h @@ -24,6 +24,8 @@ #ifdef HAVE_SCRIPTING #include <lua.h> +#include <nexthop.h> +#include <nexthop_group.h> #include "frrlua.h" #include "bgpd/bgp_script.h" // for peer and attr encoders/decoders @@ -31,6 +33,43 @@ extern "C" { #endif +/* Forward declarations */ +extern struct zebra_dplane_ctx ctx; +extern void lua_pushzebra_dplane_ctx(lua_State *L, + const struct zebra_dplane_ctx *ctx); +extern void lua_decode_zebra_dplane_ctx(lua_State *L, int idx, + struct zebra_dplane_ctx *ctx); + +/* + * Script name hash + */ +PREDECL_HASH(frrscript_names); + +struct frrscript_names_entry { + /* Name of a Lua hook call */ + char function_name[MAXPATHLEN]; + + /* Lua script in which to look for it */ + char script_name[MAXPATHLEN]; + + struct frrscript_names_item item; +}; + +extern struct frrscript_names_head frrscript_names_hash; + +int frrscript_names_hash_cmp(const struct frrscript_names_entry *snhe1, + const struct frrscript_names_entry *snhe2); +uint32_t frrscript_names_hash_key(const struct frrscript_names_entry *snhe); + +DECLARE_HASH(frrscript_names, struct frrscript_names_entry, item, + frrscript_names_hash_cmp, frrscript_names_hash_key); + +int frrscript_names_add_function_name(const char *function_name); +void frrscript_names_destroy(void); +int frrscript_names_set_script_name(const char *function_name, + const char *script_name); +char *frrscript_names_get_script_name(const char *function_name); + typedef void (*encoder_func)(lua_State *, const void *); typedef void *(*decoder_func)(lua_State *, int); @@ -171,7 +210,12 @@ time_t * : lua_pushtimet, \ char * : lua_pushstring_wrapper, \ struct attr * : lua_pushattr, \ struct peer * : lua_pushpeer, \ -const struct prefix * : lua_pushprefix \ +const struct prefix * : lua_pushprefix, \ +const struct ipaddr * : lua_pushipaddr, \ +const struct ethaddr * : lua_pushethaddr, \ +const struct nexthop_group * : lua_pushnexthop_group, \ +const struct nexthop * : lua_pushnexthop, \ +struct zebra_dplane_ctx * : lua_pushzebra_dplane_ctx \ )((L), (value)) #define DECODE_ARGS_WITH_STATE(L, value) \ @@ -187,7 +231,12 @@ time_t * : lua_decode_timet, \ char * : lua_decode_stringp, \ struct attr * : lua_decode_attr, \ struct peer * : lua_decode_noop, \ -const struct prefix * : lua_decode_noop \ +const struct prefix * : lua_decode_noop, \ +const struct ipaddr * : lua_decode_noop, \ +const struct ethaddr * : lua_decode_noop, \ +const struct nexthop_group * : lua_decode_noop, \ +const struct nexthop * : lua_decode_noop, \ +struct zebra_dplane_ctx * : lua_decode_noop \ )((L), -1, (value)) /* diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c index 209765bd6f..f9778c5d4c 100644 --- a/lib/grammar_sandbox.c +++ b/lib/grammar_sandbox.c @@ -395,6 +395,7 @@ DEFUN (grammar_findambig, vector_slot(cmdvec, scannode++); if (!cnode) continue; + cmd_finalize_node(cnode); nodegraph = cnode->cmdgraph; if (!nodegraph) continue; @@ -466,6 +467,7 @@ DEFUN (grammar_access, } vty_out(vty, "node %d\n", (int)cnode->node); + cmd_finalize_node(cnode); nodegraph = cnode->cmdgraph; return CMD_SUCCESS; } diff --git a/lib/graph.c b/lib/graph.c index 1cbe1b90f9..ba7314fb25 100644 --- a/lib/graph.c +++ b/lib/graph.c @@ -69,7 +69,7 @@ static void graph_vector_remove(vector v, unsigned int ix) * and v->active is > ix. */ v->active--; /* if ix == v->active--, we set the item to itself, then to NULL... - * still correct, no check neccessary. */ + * still correct, no check necessary. */ v->index[ix] = v->index[v->active]; v->index[v->active] = NULL; } diff --git a/lib/hash.h b/lib/hash.h index 47d951a34b..f3b24f051b 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -161,7 +161,7 @@ hash_create_size(unsigned int size, unsigned int (*hash_key)(const void *), * an element from its key, you must provide the data item itself, with the * portions used in the hash function set to the same values as the data item * to retrieve. To insert a data element, either provide the key as just - * described and provide alloc_func as descrbied below to allocate the full + * described and provide alloc_func as described below to allocate the full * data element, or provide the full data element and pass 'hash_alloc_intern' * to alloc_func. * diff --git a/lib/hook.h b/lib/hook.h index 3a0db6009b..d75e623edc 100644 --- a/lib/hook.h +++ b/lib/hook.h @@ -193,7 +193,7 @@ extern void _hook_unregister(struct hook *hook, void *funcptr, void *arg, * usage: DECLARE_HOOK(my_hook, (int arg1, struct foo *arg2), (arg1, arg2)); * as above, "passlist" must use the same order and same names as "arglist" * - * theoretically passlist is not neccessary, but let's keep things simple and + * theoretically passlist is not necessary, but let's keep things simple and * use exact same args on DECLARE and DEFINE. */ #define DECLARE_HOOK(hookname, arglist, passlist) \ @@ -45,8 +45,10 @@ DEFINE_MTYPE_STATIC(LIB, NBR_CONNECTED, "Neighbor Connected"); DEFINE_MTYPE(LIB, CONNECTED_LABEL, "Connected interface label"); DEFINE_MTYPE_STATIC(LIB, IF_LINK_PARAMS, "Informational Link Parameters"); +static void if_set_name(struct interface *ifp, const char *name); static struct interface *if_lookup_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id); +static struct interface *if_lookup_by_index_all_vrf(ifindex_t ifindex); static int if_cmp_func(const struct interface *, const struct interface *); static int if_cmp_index_func(const struct interface *ifp1, const struct interface *ifp2); @@ -153,16 +155,18 @@ static void ifp_connected_free(void *arg) } /* Create new interface structure. */ -static struct interface *if_new(vrf_id_t vrf_id) +static struct interface *if_new(struct vrf *vrf) { struct interface *ifp; + assert(vrf); + ifp = XCALLOC(MTYPE_IF, sizeof(struct interface)); ifp->ifindex = IFINDEX_INTERNAL; ifp->name[0] = '\0'; - ifp->vrf_id = vrf_id; + ifp->vrf = vrf; ifp->connected = list_new(); ifp->connected->del = ifp_connected_free; @@ -207,11 +211,11 @@ void if_down_via_zapi(struct interface *ifp) (*ifp_master.down_hook)(ifp); } -struct interface *if_create_name(const char *name, vrf_id_t vrf_id) +static struct interface *if_create_name(const char *name, struct vrf *vrf) { struct interface *ifp; - ifp = if_new(vrf_id); + ifp = if_new(vrf); if_set_name(ifp, name); @@ -219,71 +223,28 @@ struct interface *if_create_name(const char *name, vrf_id_t vrf_id) return ifp; } -struct interface *if_create_ifindex(ifindex_t ifindex, vrf_id_t vrf_id) -{ - struct interface *ifp; - - ifp = if_new(vrf_id); - - if_set_index(ifp, ifindex); - - hook_call(if_add, ifp); - return ifp; -} - /* Create new interface structure. */ void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id) { struct vrf *old_vrf, *vrf; /* remove interface from old master vrf list */ - old_vrf = vrf_lookup_by_id(ifp->vrf_id); - if (old_vrf) { - if (ifp->name[0] != '\0') - IFNAME_RB_REMOVE(old_vrf, ifp); + old_vrf = ifp->vrf; - if (ifp->ifindex != IFINDEX_INTERNAL) - IFINDEX_RB_REMOVE(old_vrf, ifp); - } + if (ifp->name[0] != '\0') + IFNAME_RB_REMOVE(old_vrf, ifp); - ifp->vrf_id = vrf_id; - vrf = vrf_get(ifp->vrf_id, NULL); + if (ifp->ifindex != IFINDEX_INTERNAL) + IFINDEX_RB_REMOVE(old_vrf, ifp); + + vrf = vrf_get(vrf_id, NULL); + ifp->vrf = vrf; if (ifp->name[0] != '\0') IFNAME_RB_INSERT(vrf, ifp); if (ifp->ifindex != IFINDEX_INTERNAL) IFINDEX_RB_INSERT(vrf, ifp); - - /* - * HACK: Change the interface VRF in the running configuration directly, - * bypassing the northbound layer. This is necessary to avoid deleting - * the interface and readding it in the new VRF, which would have - * several implications. - */ - if (yang_module_find("frr-interface")) { - struct lyd_node *if_dnode; - char oldpath[XPATH_MAXLEN]; - char newpath[XPATH_MAXLEN]; - - snprintf(oldpath, sizeof(oldpath), - "/frr-interface:lib/interface[name='%s'][vrf='%s']", - ifp->name, old_vrf->name); - snprintf(newpath, sizeof(newpath), - "/frr-interface:lib/interface[name='%s'][vrf='%s']", - ifp->name, vrf->name); - - if_dnode = yang_dnode_getf(running_config->dnode, "%s/vrf", - oldpath); - - if (if_dnode) { - yang_dnode_change_leaf(if_dnode, vrf->name); - nb_running_move_tree(oldpath, newpath); - running_config->version++; - } - - vty_update_xpath(oldpath, newpath); - } } @@ -304,10 +265,7 @@ void if_delete_retain(struct interface *ifp) void if_delete(struct interface **ifp) { struct interface *ptr = *ifp; - struct vrf *vrf; - - vrf = vrf_lookup_by_id(ptr->vrf_id); - assert(vrf); + struct vrf *vrf = ptr->vrf; IFNAME_RB_REMOVE(vrf, ptr); if (ptr->ifindex != IFINDEX_INTERNAL) @@ -341,7 +299,7 @@ static struct interface *if_lookup_by_ifindex(ifindex_t ifindex, return RB_FIND(if_index_head, &vrf->ifaces_by_index, &if_tmp); } -/* Interface existance check by index. */ +/* Interface existence check by index. */ struct interface *if_lookup_by_index(ifindex_t ifindex, vrf_id_t vrf_id) { switch (vrf_get_backend()) { @@ -354,7 +312,7 @@ struct interface *if_lookup_by_index(ifindex_t ifindex, vrf_id_t vrf_id) return NULL; } -/* Interface existance check by index. */ +/* Interface existence check by index. */ struct interface *if_vrf_lookup_by_index_next(ifindex_t ifindex, vrf_id_t vrf_id) { @@ -406,7 +364,7 @@ ifindex_t ifname2ifindex(const char *name, vrf_id_t vrf_id) : IFINDEX_INTERNAL; } -/* Interface existance check by interface name. */ +/* Interface existence check by interface name. */ struct interface *if_lookup_by_name(const char *name, vrf_id_t vrf_id) { struct vrf *vrf = vrf_lookup_by_id(vrf_id); @@ -431,7 +389,7 @@ struct interface *if_lookup_by_name_vrf(const char *name, struct vrf *vrf) return RB_FIND(if_name_head, &vrf->ifaces_by_name, &if_tmp); } -struct interface *if_lookup_by_name_all_vrf(const char *name) +static struct interface *if_lookup_by_name_all_vrf(const char *name) { struct vrf *vrf; struct interface *ifp; @@ -439,8 +397,8 @@ struct interface *if_lookup_by_name_all_vrf(const char *name) if (!name || strnlen(name, INTERFACE_NAMSIZ) == INTERFACE_NAMSIZ) return NULL; - RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { - ifp = if_lookup_by_name(name, vrf->vrf_id); + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + ifp = if_lookup_by_name_vrf(name, vrf); if (ifp) return ifp; } @@ -448,7 +406,7 @@ struct interface *if_lookup_by_name_all_vrf(const char *name) return NULL; } -struct interface *if_lookup_by_index_all_vrf(ifindex_t ifindex) +static struct interface *if_lookup_by_index_all_vrf(ifindex_t ifindex) { struct vrf *vrf; struct interface *ifp; @@ -465,36 +423,51 @@ struct interface *if_lookup_by_index_all_vrf(ifindex_t ifindex) return NULL; } -/* Lookup interface by IP address. */ -struct interface *if_lookup_exact_address(const void *src, int family, +/* Lookup interface by IP address. + * + * supersedes if_lookup_exact_address(), which didn't care about up/down + * state. but all users we have either only care if the address is local + * (=> use if_address_is_local() please), or care about UP interfaces before + * anything else + * + * to accept only UP interfaces, check if_is_up() on the returned ifp. + */ +struct interface *if_lookup_address_local(const void *src, int family, vrf_id_t vrf_id) { struct vrf *vrf = vrf_lookup_by_id(vrf_id); struct listnode *cnode; - struct interface *ifp; + struct interface *ifp, *best_down = NULL; struct prefix *p; struct connected *c; + if (family != AF_INET && family != AF_INET6) + return NULL; + FOR_ALL_INTERFACES (vrf, ifp) { for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) { p = c->address; - if (p && (p->family == family)) { - if (family == AF_INET) { - if (IPV4_ADDR_SAME( - &p->u.prefix4, + if (!p || p->family != family) + continue; + + if (family == AF_INET) { + if (!IPV4_ADDR_SAME(&p->u.prefix4, (struct in_addr *)src)) - return ifp; - } else if (family == AF_INET6) { - if (IPV6_ADDR_SAME( - &p->u.prefix6, + continue; + } else if (family == AF_INET6) { + if (!IPV6_ADDR_SAME(&p->u.prefix6, (struct in6_addr *)src)) - return ifp; - } + continue; } + + if (if_is_up(ifp)) + return ifp; + if (!best_down) + best_down = ifp; } } - return NULL; + return best_down; } /* Lookup interface by IP address. */ @@ -582,81 +555,67 @@ size_t if_lookup_by_hwaddr(const uint8_t *hw_addr, size_t addrsz, /* Get interface by name if given name interface doesn't exist create one. */ -struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id) +struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id, + const char *vrf_name) { - struct interface *ifp; + struct interface *ifp = NULL; + struct vrf *vrf; switch (vrf_get_backend()) { case VRF_BACKEND_UNKNOWN: case VRF_BACKEND_NETNS: - ifp = if_lookup_by_name(name, vrf_id); - if (ifp) - return ifp; - return if_create_name(name, vrf_id); - case VRF_BACKEND_VRF_LITE: - ifp = if_lookup_by_name_all_vrf(name); + vrf = vrf_get(vrf_id, vrf_name); + assert(vrf); + + ifp = if_lookup_by_name_vrf(name, vrf); if (ifp) { - if (ifp->vrf_id == vrf_id) - return ifp; /* If it came from the kernel or by way of zclient, * believe it and update the ifp accordingly. */ - if_update_to_new_vrf(ifp, vrf_id); + if (ifp->vrf->vrf_id != vrf_id && vrf_id != VRF_UNKNOWN) + if_update_to_new_vrf(ifp, vrf_id); + return ifp; } - return if_create_name(name, vrf_id); - } - return NULL; -} - -struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id) -{ - struct interface *ifp; - - switch (vrf_get_backend()) { - case VRF_BACKEND_UNKNOWN: - case VRF_BACKEND_NETNS: - ifp = if_lookup_by_ifindex(ifindex, vrf_id); - if (ifp) - return ifp; - return if_create_ifindex(ifindex, vrf_id); + break; case VRF_BACKEND_VRF_LITE: - ifp = if_lookup_by_index_all_vrf(ifindex); + ifp = if_lookup_by_name_all_vrf(name); if (ifp) { - if (ifp->vrf_id == vrf_id) - return ifp; /* If it came from the kernel or by way of zclient, * believe it and update the ifp accordingly. */ - if_update_to_new_vrf(ifp, vrf_id); + if (ifp->vrf->vrf_id != vrf_id && vrf_id != VRF_UNKNOWN) + if_update_to_new_vrf(ifp, vrf_id); + return ifp; } - return if_create_ifindex(ifindex, vrf_id); + + vrf = vrf_get(vrf_id, vrf_name); + assert(vrf); + + break; + default: + return NULL; } - return NULL; + return if_create_name(name, vrf); } int if_set_index(struct interface *ifp, ifindex_t ifindex) { - struct vrf *vrf; - if (ifp->ifindex == ifindex) return 0; - vrf = vrf_get(ifp->vrf_id, NULL); - assert(vrf); - /* * If there is already an interface with this ifindex, we will collide * on insertion, so don't even try. */ - if (if_lookup_by_ifindex(ifindex, ifp->vrf_id)) + if (if_lookup_by_ifindex(ifindex, ifp->vrf->vrf_id)) return -1; if (ifp->ifindex != IFINDEX_INTERNAL) - IFINDEX_RB_REMOVE(vrf, ifp); + IFINDEX_RB_REMOVE(ifp->vrf, ifp); ifp->ifindex = ifindex; @@ -666,30 +625,25 @@ int if_set_index(struct interface *ifp, ifindex_t ifindex) * already an interface with the desired ifindex at the top of * the function. Nevertheless. */ - if (IFINDEX_RB_INSERT(vrf, ifp)) + if (IFINDEX_RB_INSERT(ifp->vrf, ifp)) return -1; } return 0; } -void if_set_name(struct interface *ifp, const char *name) +static void if_set_name(struct interface *ifp, const char *name) { - struct vrf *vrf; - - vrf = vrf_get(ifp->vrf_id, NULL); - assert(vrf); - if (if_cmp_name_func(ifp->name, name) == 0) return; if (ifp->name[0] != '\0') - IFNAME_RB_REMOVE(vrf, ifp); + IFNAME_RB_REMOVE(ifp->vrf, ifp); strlcpy(ifp->name, name, sizeof(ifp->name)); if (ifp->name[0] != '\0') - IFNAME_RB_INSERT(vrf, ifp); + IFNAME_RB_INSERT(ifp->vrf, ifp); } /* Does interface up ? */ @@ -727,7 +681,7 @@ int if_is_no_ptm_operative(const struct interface *ifp) } /* Is this loopback interface ? */ -int if_is_loopback(const struct interface *ifp) +int if_is_loopback_exact(const struct interface *ifp) { /* XXX: Do this better, eg what if IFF_WHATEVER means X on platform M * but Y on platform N? @@ -741,9 +695,10 @@ int if_is_vrf(const struct interface *ifp) return CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK); } -bool if_is_loopback_or_vrf(const struct interface *ifp) +/* Should this interface be treated as a loopback? */ +bool if_is_loopback(const struct interface *ifp) { - if (if_is_loopback(ifp) || if_is_vrf(ifp)) + if (if_is_loopback_exact(ifp) || if_is_vrf(ifp)) return true; return false; @@ -817,15 +772,12 @@ static void if_dump(const struct interface *ifp) struct listnode *node; struct connected *c __attribute__((unused)); - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, c)) { - struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); - + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, c)) zlog_info( "Interface %s vrf %s(%u) index %d metric %d mtu %d mtu6 %d %s", - ifp->name, VRF_LOGNAME(vrf), ifp->vrf_id, ifp->ifindex, - ifp->metric, ifp->mtu, ifp->mtu6, + ifp->name, ifp->vrf->name, ifp->vrf->vrf_id, + ifp->ifindex, ifp->metric, ifp->mtu, ifp->mtu6, if_flag_dump(ifp->flags)); - } } /* Interface printing for all interface. */ @@ -894,16 +846,14 @@ connected_log(struct connected *connected, char *str) { struct prefix *p; struct interface *ifp; - struct vrf *vrf; char logbuf[BUFSIZ]; char buf[BUFSIZ]; ifp = connected->ifp; p = connected->address; - vrf = vrf_lookup_by_id(ifp->vrf_id); snprintf(logbuf, sizeof(logbuf), "%s interface %s vrf %s(%u) %s %pFX ", - str, ifp->name, VRF_LOGNAME(vrf), ifp->vrf_id, + str, ifp->name, ifp->vrf->name, ifp->vrf->vrf_id, prefix_family_str(p), p); p = connected->destination; @@ -1059,30 +1009,15 @@ struct connected *connected_get_linklocal(struct interface *ifp) void if_terminate(struct vrf *vrf) { struct interface *ifp; - bool delete; - - /* - * If the default VRF is being terminated or has - * already been terminated it means that - * the program is shutting down and we need to - * delete all the interfaces. Otherwise, we only - * need to move VRF's interfaces to the default VRF. - */ - delete = vrf_is_backend_netns() || vrf->vrf_id == VRF_DEFAULT - || !vrf_lookup_by_id(VRF_DEFAULT); while (!RB_EMPTY(if_name_head, &vrf->ifaces_by_name)) { ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name); - if (delete) { - if (ifp->node) { - ifp->node->info = NULL; - route_unlock_node(ifp->node); - } - if_delete(&ifp); - } else { - if_update_to_new_vrf(ifp, VRF_DEFAULT); + if (ifp->node) { + ifp->node->info = NULL; + route_unlock_node(ifp->node); } + if_delete(&ifp); } } @@ -1108,6 +1043,8 @@ const char *if_link_type_str(enum zebra_link_type llt) llts(ZEBRA_LLT_CSLIP, "Compressed SLIP"); llts(ZEBRA_LLT_SLIP6, "SLIPv6"); llts(ZEBRA_LLT_CSLIP6, "Compressed SLIPv6"); + llts(ZEBRA_LLT_RSRVD, "Reserved"); + llts(ZEBRA_LLT_ADAPT, "Adapt"); llts(ZEBRA_LLT_ROSE, "ROSE packet radio"); llts(ZEBRA_LLT_X25, "CCITT X.25"); llts(ZEBRA_LLT_PPP, "PPP"); @@ -1124,8 +1061,10 @@ const char *if_link_type_str(enum zebra_link_type llt) llts(ZEBRA_LLT_SIT, "IPv6-in-IPv4 SIT"); llts(ZEBRA_LLT_IPDDP, "IP-in-DDP tunnel"); llts(ZEBRA_LLT_IPGRE, "GRE over IP"); + llts(ZEBRA_LLT_IP6GRE, "GRE over IPv6"); llts(ZEBRA_LLT_PIMREG, "PIMSM registration"); llts(ZEBRA_LLT_HIPPI, "HiPPI"); + llts(ZEBRA_LLT_ECONET, "Acorn Econet"); llts(ZEBRA_LLT_IRDA, "IrDA"); llts(ZEBRA_LLT_FCPP, "Fibre-Channel PtP"); llts(ZEBRA_LLT_FCAL, "Fibre-Channel Arbitrated Loop"); @@ -1136,9 +1075,6 @@ const char *if_link_type_str(enum zebra_link_type llt) llts(ZEBRA_LLT_IEEE80211_RADIOTAP, "IEEE 802.11 Radiotap"); llts(ZEBRA_LLT_IEEE802154, "IEEE 802.15.4"); llts(ZEBRA_LLT_IEEE802154_PHY, "IEEE 802.15.4 Phy"); - default: - flog_err(EC_LIB_DEVELOPMENT, "Unknown value %d", llt); - return "Unknown type!"; #undef llts } return NULL; @@ -1185,6 +1121,25 @@ void if_link_params_free(struct interface *ifp) /* ----------- CLI commands ----------- */ +/* Guess the VRF of an interface. */ +static int vrfname_by_ifname(const char *ifname, const char **vrfname) +{ + struct vrf *vrf; + struct interface *ifp; + int count = 0; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + FOR_ALL_INTERFACES (vrf, ifp) { + if (strmatch(ifp->name, ifname)) { + *vrfname = vrf->name; + count++; + } + } + } + + return count; +} + /* * XPath: /frr-interface:lib/interface */ @@ -1196,50 +1151,30 @@ DEFPY_YANG_NOSH (interface, VRF_CMD_HELP_STR) { char xpath_list[XPATH_MAXLEN]; - vrf_id_t vrf_id; struct interface *ifp; - int ret; - - if (!vrf_name) - vrf_name = VRF_DEFAULT_NAME; - - /* - * This command requires special handling to maintain backward - * compatibility. If a VRF name is not specified, it means we're willing - * to accept any interface with the given name on any VRF. If no - * interface is found, then a new one should be created on the default - * VRF. - */ - VRF_GET_ID(vrf_id, vrf_name, false); - ifp = if_lookup_by_name_all_vrf(ifname); - if (ifp && ifp->vrf_id != vrf_id) { - struct vrf *vrf; + struct vrf *vrf; + int ret, count; + if (vrf_is_backend_netns()) { /* - * Special case 1: a VRF name was specified, but the found - * interface is associated to different VRF. Reject the command. + * For backward compatibility, if the VRF name is not specified + * and there is exactly one interface with this name in the + * system, use its VRF. Otherwise fallback to the default VRF. */ - if (vrf_id != VRF_DEFAULT) { - vty_out(vty, "%% interface %s not in %s vrf\n", ifname, - vrf_name); - return CMD_WARNING_CONFIG_FAILED; + if (!vrf_name) { + count = vrfname_by_ifname(ifname, &vrf_name); + if (count != 1) + vrf_name = VRF_DEFAULT_NAME; } - /* - * Special case 2: a VRF name was *not* specified, and the found - * interface is associated to a VRF other than the default one. - * Update vrf_id and vrf_name to account for that. - */ - vrf = vrf_lookup_by_id(ifp->vrf_id); - assert(vrf); - vrf_id = ifp->vrf_id; - vrf_name = vrf->name; + snprintf(xpath_list, XPATH_MAXLEN, + "/frr-interface:lib/interface[name='%s:%s']", vrf_name, + ifname); + } else { + snprintf(xpath_list, XPATH_MAXLEN, + "/frr-interface:lib/interface[name='%s']", ifname); } - snprintf(xpath_list, sizeof(xpath_list), - "/frr-interface:lib/interface[name='%s'][vrf='%s']", ifname, - vrf_name); - nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); ret = nb_cli_apply_changes_clear_pending(vty, xpath_list); if (ret == CMD_SUCCESS) { @@ -1251,7 +1186,15 @@ DEFPY_YANG_NOSH (interface, * all interface-level commands are converted to the new * northbound model. */ - ifp = if_lookup_by_name(ifname, vrf_id); + if (vrf_is_backend_netns()) { + vrf = vrf_lookup_by_name(vrf_name); + if (vrf) + ifp = if_lookup_by_name_vrf(ifname, vrf); + else + ifp = NULL; + } else { + ifp = if_lookup_by_name_all_vrf(ifname); + } if (ifp) VTY_PUSH_CONTEXT(INTERFACE_NODE, ifp); } @@ -1267,31 +1210,77 @@ DEFPY_YANG (no_interface, "Interface's name\n" VRF_CMD_HELP_STR) { - if (!vrf_name) - vrf_name = VRF_DEFAULT_NAME; + char xpath_list[XPATH_MAXLEN]; + int count; + + if (vrf_is_backend_netns()) { + /* + * For backward compatibility, if the VRF name is not specified + * and there is exactly one interface with this name in the + * system, use its VRF. Otherwise fallback to the default VRF. + */ + if (!vrf_name) { + count = vrfname_by_ifname(ifname, &vrf_name); + if (count != 1) + vrf_name = VRF_DEFAULT_NAME; + } + + snprintf(xpath_list, XPATH_MAXLEN, + "/frr-interface:lib/interface[name='%s:%s']", vrf_name, + ifname); + } else { + snprintf(xpath_list, XPATH_MAXLEN, + "/frr-interface:lib/interface[name='%s']", ifname); + } nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); - return nb_cli_apply_changes( - vty, "/frr-interface:lib/interface[name='%s'][vrf='%s']", - ifname, vrf_name); + return nb_cli_apply_changes(vty, xpath_list); } -static void cli_show_interface(struct vty *vty, struct lyd_node *dnode, - bool show_defaults) +static void netns_ifname_split(const char *xpath, char *ifname, char *vrfname) { - const char *vrf; + char *delim; + int len; - vrf = yang_dnode_get_string(dnode, "./vrf"); + assert(vrf_is_backend_netns()); + delim = strchr(xpath, ':'); + assert(delim); + + len = delim - xpath; + memcpy(vrfname, xpath, len); + vrfname[len] = 0; + + strlcpy(ifname, delim + 1, XPATH_MAXLEN); +} + +static void cli_show_interface(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ vty_out(vty, "!\n"); - vty_out(vty, "interface %s", yang_dnode_get_string(dnode, "./name")); - if (!strmatch(vrf, VRF_DEFAULT_NAME)) - vty_out(vty, " vrf %s", vrf); + + if (vrf_is_backend_netns()) { + char ifname[XPATH_MAXLEN]; + char vrfname[XPATH_MAXLEN]; + + netns_ifname_split(yang_dnode_get_string(dnode, "./name"), + ifname, vrfname); + + vty_out(vty, "interface %s", ifname); + if (!strmatch(vrfname, VRF_DEFAULT_NAME)) + vty_out(vty, " vrf %s", vrfname); + } else { + const char *ifname = yang_dnode_get_string(dnode, "./name"); + + vty_out(vty, "interface %s", ifname); + } + vty_out(vty, "\n"); } -static void cli_show_interface_end(struct vty *vty, struct lyd_node *dnode) +static void cli_show_interface_end(struct vty *vty, + const struct lyd_node *dnode) { vty_out(vty, "exit\n"); } @@ -1327,8 +1316,9 @@ DEFPY_YANG (no_interface_desc, return nb_cli_apply_changes(vty, NULL); } -static void cli_show_interface_desc(struct vty *vty, struct lyd_node *dnode, - bool show_defaults) +static void cli_show_interface_desc(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) { vty_out(vty, " description %s\n", yang_dnode_get_string(dnode, NULL)); } @@ -1361,6 +1351,20 @@ static struct cmd_node interface_node = { .prompt = "%s(config-if)# ", }; +static int if_config_write_single(const struct lyd_node *dnode, void *arg) +{ + nb_cli_show_dnode_cmds(arg, dnode, false); + + return YANG_ITER_CONTINUE; +} + +static int if_nb_config_write(struct vty *vty) +{ + yang_dnode_iterate(if_config_write_single, vty, running_config->dnode, + "/frr-interface:lib/interface"); + return 1; +} + void if_cmd_init(int (*config_write)(struct vty *)) { cmd_variable_handler_register(if_var_handlers); @@ -1376,6 +1380,11 @@ void if_cmd_init(int (*config_write)(struct vty *)) install_element(INTERFACE_NODE, &no_interface_desc_cmd); } +void if_cmd_init_default(void) +{ + if_cmd_init(if_nb_config_write); +} + void if_zapi_callbacks(int (*create)(struct interface *ifp), int (*up)(struct interface *ifp), int (*down)(struct interface *ifp), @@ -1395,37 +1404,35 @@ void if_zapi_callbacks(int (*create)(struct interface *ifp), static int lib_interface_create(struct nb_cb_create_args *args) { const char *ifname; - const char *vrfname; - struct vrf *vrf; struct interface *ifp; ifname = yang_dnode_get_string(args->dnode, "./name"); - vrfname = yang_dnode_get_string(args->dnode, "./vrf"); switch (args->event) { case NB_EV_VALIDATE: - vrf = vrf_lookup_by_name(vrfname); - if (!vrf) { - zlog_warn("%s: VRF %s doesn't exist", __func__, - vrfname); - return NB_ERR_VALIDATION; - } - if (vrf->vrf_id == VRF_UNKNOWN) { - zlog_warn("%s: VRF %s is not active", __func__, - vrf->name); - return NB_ERR_VALIDATION; - } + if (vrf_is_backend_netns()) { + char ifname_ns[XPATH_MAXLEN]; + char vrfname_ns[XPATH_MAXLEN]; - /* if VRF is netns or not yet known - init for instance - * then assumption is that passed config is exact - * then the user intent was not to use an other iface - */ - if (vrf_get_backend() == VRF_BACKEND_VRF_LITE) { - ifp = if_lookup_by_name_all_vrf(ifname); - if (ifp && ifp->vrf_id != vrf->vrf_id) { - zlog_warn( - "%s: interface %s already exists in another VRF", - __func__, ifp->name); + netns_ifname_split(ifname, ifname_ns, vrfname_ns); + + if (strlen(ifname_ns) > 16) { + snprintf( + args->errmsg, args->errmsg_len, + "Maximum interface name length is 16 characters"); + return NB_ERR_VALIDATION; + } + if (strlen(vrfname_ns) > 36) { + snprintf( + args->errmsg, args->errmsg_len, + "Maximum VRF name length is 36 characters"); + return NB_ERR_VALIDATION; + } + } else { + if (strlen(ifname) > 16) { + snprintf( + args->errmsg, args->errmsg_len, + "Maximum interface name length is 16 characters"); return NB_ERR_VALIDATION; } } @@ -1434,9 +1441,18 @@ static int lib_interface_create(struct nb_cb_create_args *args) case NB_EV_ABORT: break; case NB_EV_APPLY: - vrf = vrf_lookup_by_name(vrfname); - assert(vrf); - ifp = if_get_by_name(ifname, vrf->vrf_id); + if (vrf_is_backend_netns()) { + char ifname_ns[XPATH_MAXLEN]; + char vrfname_ns[XPATH_MAXLEN]; + + netns_ifname_split(ifname, ifname_ns, vrfname_ns); + + ifp = if_get_by_name(ifname_ns, VRF_UNKNOWN, + vrfname_ns); + } else { + ifp = if_get_by_name(ifname, VRF_UNKNOWN, + VRF_DEFAULT_NAME); + } ifp->configured = true; nb_running_set_entry(args->dnode, ifp); @@ -1449,7 +1465,7 @@ static int lib_interface_create(struct nb_cb_create_args *args) static int lib_interface_destroy(struct nb_cb_destroy_args *args) { struct interface *ifp; - + struct vrf *vrf; switch (args->event) { case NB_EV_VALIDATE: @@ -1465,9 +1481,13 @@ static int lib_interface_destroy(struct nb_cb_destroy_args *args) break; case NB_EV_APPLY: ifp = nb_running_unset_entry(args->dnode); + vrf = ifp->vrf; ifp->configured = false; if_delete(&ifp); + + if (!vrf_is_enabled(vrf)) + vrf_delete(vrf); break; } @@ -1487,7 +1507,7 @@ static const void *lib_interface_get_next(struct nb_cb_get_next_args *args) assert(vrf); pif = RB_MIN(if_name_head, &vrf->ifaces_by_name); } else { - vrf = vrf_lookup_by_id(pif->vrf_id); + vrf = pif->vrf; pif = RB_NEXT(if_name_head, pif); /* if no more interfaces, switch to next vrf */ while (pif == NULL) { @@ -1505,13 +1525,14 @@ static int lib_interface_get_keys(struct nb_cb_get_keys_args *args) { const struct interface *ifp = args->list_entry; - struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + args->keys->num = 1; - assert(vrf); - - args->keys->num = 2; - strlcpy(args->keys->key[0], ifp->name, sizeof(args->keys->key[0])); - strlcpy(args->keys->key[1], vrf->name, sizeof(args->keys->key[1])); + if (vrf_is_backend_netns()) + snprintf(args->keys->key[0], sizeof(args->keys->key[0]), + "%s:%s", ifp->vrf->name, ifp->name); + else + snprintf(args->keys->key[0], sizeof(args->keys->key[0]), "%s", + ifp->name); return NB_OK; } @@ -1519,11 +1540,19 @@ static int lib_interface_get_keys(struct nb_cb_get_keys_args *args) static const void * lib_interface_lookup_entry(struct nb_cb_lookup_entry_args *args) { - const char *ifname = args->keys->key[0]; - const char *vrfname = args->keys->key[1]; - struct vrf *vrf = vrf_lookup_by_name(vrfname); + if (vrf_is_backend_netns()) { + char ifname[XPATH_MAXLEN]; + char vrfname[XPATH_MAXLEN]; + struct vrf *vrf; + + netns_ifname_split(args->keys->key[0], ifname, vrfname); + + vrf = vrf_lookup_by_name(vrfname); - return vrf ? if_lookup_by_name(ifname, vrf->vrf_id) : NULL; + return vrf ? if_lookup_by_name(ifname, vrf->vrf_id) : NULL; + } else { + return if_lookup_by_name_all_vrf(args->keys->key[0]); + } } /* @@ -1559,6 +1588,17 @@ static int lib_interface_description_destroy(struct nb_cb_destroy_args *args) } /* + * XPath: /frr-interface:lib/interface/vrf + */ +static struct yang_data * +lib_interface_vrf_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + + return yang_data_new_string(args->xpath, ifp->vrf->name); +} + +/* * XPath: /frr-interface:lib/interface/state/if-index */ static struct yang_data * @@ -1672,6 +1712,12 @@ const struct frr_yang_module_info frr_interface_info = { }, }, { + .xpath = "/frr-interface:lib/interface/vrf", + .cbs = { + .get_elem = lib_interface_vrf_get_elem, + } + }, + { .xpath = "/frr-interface:lib/interface/state/if-index", .cbs = { .get_elem = lib_interface_state_if_index_get_elem, @@ -251,8 +251,8 @@ struct interface { /* Interface MTU. */ unsigned int mtu; /* IPv4 MTU */ unsigned int - mtu6; /* IPv6 MTU - probably, but not neccessarily same as mtu - */ + mtu6; /* IPv6 MTU - probably, but not necessarily same as mtu + */ /* Link-layer information and hardware address */ enum zebra_link_type ll_type; @@ -293,7 +293,8 @@ struct interface { #endif /* HAVE_NET_RT_IFLIST */ struct route_node *node; - vrf_id_t vrf_id; + + struct vrf *vrf; /* * Has the end users entered `interface XXXX` from the cli in some @@ -310,56 +311,56 @@ RB_HEAD(if_index_head, interface); RB_PROTOTYPE(if_index_head, interface, index_entry, if_cmp_index_func) DECLARE_QOBJ_TYPE(interface); -#define IFNAME_RB_INSERT(vrf, ifp) \ +#define IFNAME_RB_INSERT(v, ifp) \ ({ \ struct interface *_iz = \ - RB_INSERT(if_name_head, &vrf->ifaces_by_name, (ifp)); \ + RB_INSERT(if_name_head, &v->ifaces_by_name, (ifp)); \ if (_iz) \ flog_err( \ EC_LIB_INTERFACE, \ "%s(%s): corruption detected -- interface with this " \ - "name exists already in VRF %u!", \ - __func__, (ifp)->name, (ifp)->vrf_id); \ + "name exists already in VRF %s!", \ + __func__, (ifp)->name, (ifp)->vrf->name); \ _iz; \ }) -#define IFNAME_RB_REMOVE(vrf, ifp) \ +#define IFNAME_RB_REMOVE(v, ifp) \ ({ \ struct interface *_iz = \ - RB_REMOVE(if_name_head, &vrf->ifaces_by_name, (ifp)); \ + RB_REMOVE(if_name_head, &v->ifaces_by_name, (ifp)); \ if (_iz == NULL) \ flog_err( \ EC_LIB_INTERFACE, \ "%s(%s): corruption detected -- interface with this " \ - "name doesn't exist in VRF %u!", \ - __func__, (ifp)->name, (ifp)->vrf_id); \ + "name doesn't exist in VRF %s!", \ + __func__, (ifp)->name, (ifp)->vrf->name); \ _iz; \ }) -#define IFINDEX_RB_INSERT(vrf, ifp) \ +#define IFINDEX_RB_INSERT(v, ifp) \ ({ \ - struct interface *_iz = RB_INSERT( \ - if_index_head, &vrf->ifaces_by_index, (ifp)); \ + struct interface *_iz = \ + RB_INSERT(if_index_head, &v->ifaces_by_index, (ifp)); \ if (_iz) \ flog_err( \ EC_LIB_INTERFACE, \ "%s(%u): corruption detected -- interface with this " \ - "ifindex exists already in VRF %u!", \ - __func__, (ifp)->ifindex, (ifp)->vrf_id); \ + "ifindex exists already in VRF %s!", \ + __func__, (ifp)->ifindex, (ifp)->vrf->name); \ _iz; \ }) -#define IFINDEX_RB_REMOVE(vrf, ifp) \ +#define IFINDEX_RB_REMOVE(v, ifp) \ ({ \ - struct interface *_iz = RB_REMOVE( \ - if_index_head, &vrf->ifaces_by_index, (ifp)); \ + struct interface *_iz = \ + RB_REMOVE(if_index_head, &v->ifaces_by_index, (ifp)); \ if (_iz == NULL) \ flog_err( \ EC_LIB_INTERFACE, \ "%s(%u): corruption detected -- interface with this " \ - "ifindex doesn't exist in VRF %u!", \ - __func__, (ifp)->ifindex, (ifp)->vrf_id); \ + "ifindex doesn't exist in VRF %s!", \ + __func__, (ifp)->ifindex, (ifp)->vrf->name); \ _iz; \ }) @@ -397,16 +398,12 @@ struct connected { /* The ZEBRA_IFC_REAL flag should be set if and only if this address exists in the kernel and is actually usable. (A case where it exists - but - is not yet usable would be IPv6 with DAD) + but is not yet usable would be IPv6 with DAD) The ZEBRA_IFC_CONFIGURED flag should be set if and only if this - address - was configured by the user from inside quagga. + address was configured by the user from inside frr. The ZEBRA_IFC_QUEUED flag should be set if and only if the address - exists - in the kernel. It may and should be set although the address might - not be - usable yet. (compare with ZEBRA_IFC_REAL) + exists in the kernel. It may and should be set although the + address might not be usable yet. (compare with ZEBRA_IFC_REAL) The ZEBRA_IFC_DOWN flag is used to record that an address is present, but down/unavailable. */ @@ -510,16 +507,10 @@ extern int if_cmp_name_func(const char *p1, const char *p2); */ extern void if_update_to_new_vrf(struct interface *, vrf_id_t vrf_id); -/* Create new interface, adds to name list only */ -extern struct interface *if_create_name(const char *name, vrf_id_t vrf_id); - -/* Create new interface, adds to index list only */ -extern struct interface *if_create_ifindex(ifindex_t ifindex, vrf_id_t vrf_id); extern struct interface *if_lookup_by_index(ifindex_t, vrf_id_t vrf_id); extern struct interface *if_vrf_lookup_by_index_next(ifindex_t ifindex, vrf_id_t vrf_id); -extern struct interface *if_lookup_by_index_all_vrf(ifindex_t); -extern struct interface *if_lookup_exact_address(const void *matchaddr, +extern struct interface *if_lookup_address_local(const void *matchaddr, int family, vrf_id_t vrf_id); extern struct connected *if_lookup_address(const void *matchaddr, int family, vrf_id_t vrf_id); @@ -528,17 +519,20 @@ extern struct interface *if_lookup_prefix(const struct prefix *prefix, size_t if_lookup_by_hwaddr(const uint8_t *hw_addr, size_t addrsz, struct interface ***result, vrf_id_t vrf_id); +static inline bool if_address_is_local(const void *matchaddr, int family, + vrf_id_t vrf_id) +{ + return if_lookup_address_local(matchaddr, family, vrf_id) != NULL; +} + struct vrf; -extern struct interface *if_lookup_by_name_all_vrf(const char *ifname); extern struct interface *if_lookup_by_name_vrf(const char *name, struct vrf *vrf); extern struct interface *if_lookup_by_name(const char *ifname, vrf_id_t vrf_id); -extern struct interface *if_get_by_name(const char *ifname, vrf_id_t vrf_id); -extern struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id); +extern struct interface *if_get_by_name(const char *ifname, vrf_id_t vrf_id, + const char *vrf_name); /* Sets the index and adds to index list */ extern int if_set_index(struct interface *ifp, ifindex_t ifindex); -/* Sets the name and adds to name list */ -extern void if_set_name(struct interface *ifp, const char *name); /* Delete the interface, but do not free the structure, and leave it in the interface list. It is often advisable to leave the pseudo interface @@ -553,9 +547,9 @@ extern int if_is_up(const struct interface *ifp); extern int if_is_running(const struct interface *ifp); extern int if_is_operative(const struct interface *ifp); extern int if_is_no_ptm_operative(const struct interface *ifp); -extern int if_is_loopback(const struct interface *ifp); +extern int if_is_loopback_exact(const struct interface *ifp); extern int if_is_vrf(const struct interface *ifp); -extern bool if_is_loopback_or_vrf(const struct interface *ifp); +extern bool if_is_loopback(const struct interface *ifp); extern int if_is_broadcast(const struct interface *ifp); extern int if_is_pointopoint(const struct interface *ifp); extern int if_is_multicast(const struct interface *ifp); @@ -599,6 +593,7 @@ void if_link_params_free(struct interface *); /* Northbound. */ struct vty; extern void if_cmd_init(int (*config_write)(struct vty *)); +extern void if_cmd_init_default(void); extern void if_zapi_callbacks(int (*create)(struct interface *ifp), int (*up)(struct interface *ifp), int (*down)(struct interface *ifp), diff --git a/lib/json.c b/lib/json.c index cfba6ea3b6..854a3d59d1 100644 --- a/lib/json.c +++ b/lib/json.c @@ -39,17 +39,41 @@ bool use_json(const int argc, struct cmd_token *argv[]) return false; } +struct json_object *json_object_new_stringv(const char *fmt, va_list args) +{ + struct json_object *ret; + char *text, buf[256]; + + text = vasnprintfrr(MTYPE_TMP, buf, sizeof(buf), fmt, args); + ret = json_object_new_string(text); + + if (text != buf) + XFREE(MTYPE_TMP, text); + return ret; +} + void json_array_string_add(json_object *json, const char *str) { json_object_array_add(json, json_object_new_string(str)); } +void json_array_string_addv(json_object *json, const char *fmt, va_list args) +{ + json_object_array_add(json, json_object_new_stringv(fmt, args)); +} + void json_object_string_add(struct json_object *obj, const char *key, const char *s) { json_object_object_add(obj, key, json_object_new_string(s)); } +void json_object_string_addv(struct json_object *obj, const char *key, + const char *fmt, va_list args) +{ + json_object_object_add(obj, key, json_object_new_stringv(fmt, args)); +} + void json_object_int_add(struct json_object *obj, const char *key, int64_t i) { json_object_object_add(obj, key, json_object_new_int64(i)); diff --git a/lib/json.h b/lib/json.h index fe208f4fa9..9d33ac7ae3 100644 --- a/lib/json.h +++ b/lib/json.h @@ -26,6 +26,7 @@ extern "C" { #endif #include "command.h" +#include "printfrr.h" #include <json-c/json.h> /* @@ -59,6 +60,53 @@ extern struct json_object *json_object_lock(struct json_object *obj); extern void json_object_free(struct json_object *obj); extern void json_array_string_add(json_object *json, const char *str); +/* printfrr => json helpers */ + +PRINTFRR(3, 0) +extern void json_object_string_addv(struct json_object *obj, const char *key, + const char *fmt, va_list args); +PRINTFRR(3, 4) +static inline void json_object_string_addf(struct json_object *obj, + const char *key, const char *fmt, + ...) +{ + va_list args; + + va_start(args, fmt); + json_object_string_addv(obj, key, fmt, args); + va_end(args); +} + +PRINTFRR(2, 0) +extern void json_array_string_addv(json_object *json, const char *fmt, + va_list args); +PRINTFRR(2, 3) +static inline void json_array_string_addf(struct json_object *obj, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + json_array_string_addv(obj, fmt, args); + va_end(args); +} + +PRINTFRR(1, 0) +extern struct json_object *json_object_new_stringv(const char *fmt, + va_list args); +PRINTFRR(1, 2) +static inline struct json_object *json_object_new_stringf(const char *fmt, ...) +{ + struct json_object *ret; + va_list args; + + va_start(args, fmt); + ret = json_object_new_stringv(fmt, args); + va_end(args); + + return ret; +} + #define JSON_STR "JavaScript Object Notation\n" /* NOTE: json-c lib has following commit 316da85 which diff --git a/lib/lib_errors.c b/lib/lib_errors.c index 17695e6607..a139b9a14c 100644 --- a/lib/lib_errors.c +++ b/lib/lib_errors.c @@ -41,19 +41,19 @@ static struct log_ref ferr_lib_warn[] = { { .code = EC_LIB_LINUX_NS, .title = "The Linux namespace subsystem has encountered a parsing error", - .description = "During system startup an invalid parameter for the namesapce was give to FRR", + .description = "During system startup an invalid parameter for the namespace was give to FRR", .suggestion = "Gather log data and open an Issue. restart FRR", }, { .code = EC_LIB_SLOW_THREAD_CPU, .title = "The Event subsystem has detected a slow cpu time process", - .description = "The Event subsystem has detected a slow process, this typically indicates that FRR is having trouble completing work in a timely manner. This can be either a misconfiguration, bug, or some combination therof. In this case total CPU time was over 5 seconds. Which indicates that FRR is very busy doing some work and should be addressed", + .description = "The Event subsystem has detected a slow process, this typically indicates that FRR is having trouble completing work in a timely manner. This can be either a misconfiguration, bug, or some combination thereof. In this case total CPU time was over 5 seconds. Which indicates that FRR is very busy doing some work and should be addressed", .suggestion = "Gather log data and open an Issue", }, { .code = EC_LIB_SLOW_THREAD_WALL, .title = "The Event subsystem has detected a slow wall time process", - .description = "The Event subsystem has detected a slow process, this typically indicates that FRR is having trouble completing work in a timely manner. This can be either a misconfiguration, bug or some combination therof. In this case total WALL time was over 5 seconds. Which indicates that FRR might be having trouble being scheduled or some system call is delaying", + .description = "The Event subsystem has detected a slow process, this typically indicates that FRR is having trouble completing work in a timely manner. This can be either a misconfiguration, bug or some combination thereof. In this case total WALL time was over 5 seconds. Which indicates that FRR might be having trouble being scheduled or some system call is delaying", .suggestion = "Gather log data and open an Issue", }, { @@ -286,7 +286,7 @@ static struct log_ref ferr_lib_err[] = { }, { .code = EC_LIB_NB_CB_INVALID_PRIO, - .title = "Norhtbound callback has an invalid priority", + .title = "Northbound callback has an invalid priority", .description = "The northbound subsystem, during initialization, has detected a callback whose priority is invalid", .suggestion = "This is a bug; please report it" }, diff --git a/lib/libfrr.c b/lib/libfrr.c index 9b05bb4fbf..1610ba4e7d 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -121,7 +121,6 @@ static const struct option lo_always[] = { {"scriptdir", required_argument, NULL, OPTION_SCRIPTDIR}, {"log", required_argument, NULL, OPTION_LOG}, {"log-level", required_argument, NULL, OPTION_LOGLEVEL}, - {"tcli", no_argument, NULL, OPTION_TCLI}, {"command-log-always", no_argument, NULL, OPTION_LOGGING}, {"limit-fds", required_argument, NULL, OPTION_LIMIT_FDS}, {NULL}}; @@ -138,31 +137,43 @@ static const struct optspec os_always = { " --scriptdir Override scripts directory\n" " --log Set Logging to stdout, syslog, or file:<name>\n" " --log-level Set Logging Level to use, debug, info, warn, etc\n" - " --tcli Use transaction-based CLI\n" " --limit-fds Limit number of fds supported\n", lo_always}; -static const struct option lo_cfg_pid_dry[] = { - {"pid_file", required_argument, NULL, 'i'}, +static const struct option lo_cfg[] = { {"config_file", required_argument, NULL, 'f'}, -#ifdef HAVE_SQLITE3 - {"db_file", required_argument, NULL, OPTION_DB_FILE}, -#endif {"dryrun", no_argument, NULL, 'C'}, - {"terminal", no_argument, NULL, 't'}, {NULL}}; -static const struct optspec os_cfg_pid_dry = { - "f:i:Ct", +static const struct optspec os_cfg = { + "f:C", " -f, --config_file Set configuration file name\n" - " -i, --pid_file Set process identifier file name\n" + " -C, --dryrun Check configuration for validity and exit\n", + lo_cfg}; + + +static const struct option lo_fullcli[] = { + {"terminal", no_argument, NULL, 't'}, + {"tcli", no_argument, NULL, OPTION_TCLI}, #ifdef HAVE_SQLITE3 - " --db_file Set database file name\n" + {"db_file", required_argument, NULL, OPTION_DB_FILE}, #endif - " -C, --dryrun Check configuration for validity and exit\n" + {NULL}}; +static const struct optspec os_fullcli = { + "t", + " --tcli Use transaction-based CLI\n" " -t, --terminal Open terminal session on stdio\n" " -d -t Daemonize after terminal session ends\n", - lo_cfg_pid_dry}; + lo_fullcli}; + + +static const struct option lo_pid[] = { + {"pid_file", required_argument, NULL, 'i'}, + {NULL}}; +static const struct optspec os_pid = { + "i:", + " -i, --pid_file Set process identifier file name\n", + lo_pid}; static const struct option lo_zclient[] = { @@ -320,8 +331,12 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) umask(0027); opt_extend(&os_always); - if (!(di->flags & FRR_NO_CFG_PID_DRY)) - opt_extend(&os_cfg_pid_dry); + if (!(di->flags & FRR_NO_SPLIT_CONFIG)) + opt_extend(&os_cfg); + if (!(di->flags & FRR_LIMITED_CLI)) + opt_extend(&os_fullcli); + if (!(di->flags & FRR_NO_PID)) + opt_extend(&os_pid); if (!(di->flags & FRR_NO_PRIVSEP)) opt_extend(&os_user); if (!(di->flags & FRR_NO_ZCLIENT)) @@ -459,12 +474,12 @@ static int frr_opt(int opt) frr_defaults_profile_set(optarg); break; case 'i': - if (di->flags & FRR_NO_CFG_PID_DRY) + if (di->flags & FRR_NO_PID) return 1; di->pid_file = optarg; break; case 'f': - if (di->flags & FRR_NO_CFG_PID_DRY) + if (di->flags & FRR_NO_SPLIT_CONFIG) return 1; di->config_file = optarg; break; @@ -497,18 +512,18 @@ static int frr_opt(int opt) break; #ifdef HAVE_SQLITE3 case OPTION_DB_FILE: - if (di->flags & FRR_NO_CFG_PID_DRY) + if (di->flags & FRR_NO_PID) return 1; di->db_file = optarg; break; #endif case 'C': - if (di->flags & FRR_NO_CFG_PID_DRY) + if (di->flags & FRR_NO_SPLIT_CONFIG) return 1; di->dryrun = true; break; case 't': - if (di->flags & FRR_NO_CFG_PID_DRY) + if (di->flags & FRR_LIMITED_CLI) return 1; di->terminal = true; break; @@ -986,7 +1001,7 @@ void frr_config_fork(void) { hook_call(frr_late_init, master); - if (!(di->flags & FRR_NO_CFG_PID_DRY)) { + if (!(di->flags & FRR_NO_SPLIT_CONFIG)) { /* Don't start execution if we are in dry-run mode */ if (di->dryrun) { frr_config_read_in(NULL); diff --git a/lib/libfrr.h b/lib/libfrr.h index 3dc5d7af81..e0642ef847 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -40,15 +40,17 @@ extern "C" { #define FRR_NO_PRIVSEP (1 << 0) #define FRR_NO_TCPVTY (1 << 1) #define FRR_LIMITED_CLI (1 << 2) -#define FRR_NO_CFG_PID_DRY (1 << 3) -#define FRR_NO_ZCLIENT (1 << 4) +#define FRR_NO_SPLIT_CONFIG (1 << 3) +#define FRR_NO_PID (1 << 4) +#define FRR_NO_CFG_PID_DRY (FRR_NO_PID | FRR_NO_SPLIT_CONFIG) +#define FRR_NO_ZCLIENT (1 << 5) /* If FRR_DETACH_LATER is used, the daemon will keep its parent running * until frr_detach() is called. Normally "somedaemon -d" returns once the * main event loop is reached in the daemon; use this for extra startup bits. * * Does nothing if -d isn't used. */ -#define FRR_DETACH_LATER (1 << 5) +#define FRR_DETACH_LATER (1 << 6) enum frr_cli_mode { FRR_CLI_CLASSIC = 0, @@ -94,7 +96,7 @@ struct frr_daemon_info { const char *copyright; char startinfo[128]; - struct quagga_signal_t *signals; + struct frr_signal_t *signals; size_t n_signals; struct zebra_privs_t *privs; diff --git a/lib/libospf.h b/lib/libospf.h index d2bb29d80e..c8ada9d3c5 100644 --- a/lib/libospf.h +++ b/lib/libospf.h @@ -36,7 +36,7 @@ extern "C" { #define IPPROTO_OSPFIGP 89 #endif /* IPPROTO_OSPFIGP */ -/* Architectual Constants */ +/* Architectural Constants */ #ifdef DEBUG #define OSPF_LS_REFRESH_TIME 120 #else diff --git a/lib/link_state.c b/lib/link_state.c index 062384aac7..b0bc386b79 100644 --- a/lib/link_state.c +++ b/lib/link_state.c @@ -46,6 +46,28 @@ DEFINE_MTYPE_STATIC(LIB, LS_DB, "Link State Database"); /** * Link State Node management functions */ +int ls_node_id_same(struct ls_node_id i1, struct ls_node_id i2) +{ + if (i1.origin != i2.origin) + return 0; + + if (i1.origin == UNKNOWN) + return 1; + + if (i1.origin == ISIS_L1 || i1.origin == ISIS_L2) { + if (memcmp(i1.id.iso.sys_id, i2.id.iso.sys_id, ISO_SYS_ID_LEN) + != 0 + || (i1.id.iso.level != i2.id.iso.level)) + return 0; + } else { + if (!IPV4_ADDR_SAME(&i1.id.ip.addr, &i2.id.ip.addr) + || !IPV4_ADDR_SAME(&i1.id.ip.area_id, &i2.id.ip.area_id)) + return 1; + } + + return 1; +} + struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr rid, struct in6_addr rid6) { @@ -83,29 +105,56 @@ void ls_node_del(struct ls_node *node) int ls_node_same(struct ls_node *n1, struct ls_node *n2) { + /* First, check pointer */ if ((n1 && !n2) || (!n1 && n2)) return 0; if (n1 == n2) return 1; + /* Then, verify Flags and Origin */ if (n1->flags != n2->flags) return 0; - if (n1->adv.origin != n2->adv.origin) + if (!ls_node_id_same(n1->adv, n2->adv)) return 0; - if (!memcmp(&n1->adv.id, &n2->adv.id, sizeof(struct ls_node_id))) + /* Finally, check each individual parameters that are valid */ + if (CHECK_FLAG(n1->flags, LS_NODE_NAME) + && (strncmp(n1->name, n2->name, MAX_NAME_LENGTH) != 0)) return 0; - - /* Do we need to test individually each field, instead performing a - * global memcmp? There is a risk that an old value that is bit masked - * i.e. corresponding flag = 0, will result into a false negative - */ - if (!memcmp(n1, n2, sizeof(struct ls_node))) + if (CHECK_FLAG(n1->flags, LS_NODE_ROUTER_ID) + && !IPV4_ADDR_SAME(&n1->router_id, &n2->router_id)) return 0; - else - return 1; + if (CHECK_FLAG(n1->flags, LS_NODE_ROUTER_ID6) + && !IPV6_ADDR_SAME(&n1->router6_id, &n2->router6_id)) + return 0; + if (CHECK_FLAG(n1->flags, LS_NODE_FLAG) + && (n1->node_flag != n2->node_flag)) + return 0; + if (CHECK_FLAG(n1->flags, LS_NODE_TYPE) && (n1->type != n2->type)) + return 0; + if (CHECK_FLAG(n1->flags, LS_NODE_AS_NUMBER) + && (n1->as_number != n2->as_number)) + return 0; + if (CHECK_FLAG(n1->flags, LS_NODE_SR)) { + if (n1->srgb.flag != n2->srgb.flag + || n1->srgb.lower_bound != n2->srgb.lower_bound + || n1->srgb.range_size != n2->srgb.range_size) + return 0; + if ((n1->algo[0] != n2->algo[0]) + || (n1->algo[1] != n2->algo[1])) + return 0; + if (CHECK_FLAG(n1->flags, LS_NODE_SRLB) + && ((n1->srlb.lower_bound != n2->srlb.lower_bound + || n1->srlb.range_size != n2->srlb.range_size))) + return 0; + if (CHECK_FLAG(n1->flags, LS_NODE_MSD) && (n1->msd != n2->msd)) + return 0; + } + + /* OK, n1 & n2 are equal */ + return 1; } /** @@ -171,29 +220,133 @@ void ls_attributes_del(struct ls_attributes *attr) int ls_attributes_same(struct ls_attributes *l1, struct ls_attributes *l2) { + /* First, check pointer */ if ((l1 && !l2) || (!l1 && l2)) return 0; if (l1 == l2) return 1; + /* Then, verify Flags and Origin */ if (l1->flags != l2->flags) return 0; - if (l1->adv.origin != l2->adv.origin) + if (!ls_node_id_same(l1->adv, l2->adv)) return 0; - if (!memcmp(&l1->adv.id, &l2->adv.id, sizeof(struct ls_node_id))) + /* Finally, check each individual parameters that are valid */ + if (CHECK_FLAG(l1->flags, LS_ATTR_NAME) + && strncmp(l1->name, l2->name, MAX_NAME_LENGTH) != 0) return 0; - - /* Do we need to test individually each field, instead performing a - * global memcmp? There is a risk that an old value that is bit masked - * i.e. corresponding flag = 0, will result into a false negative - */ - if (!memcmp(l1, l2, sizeof(struct ls_attributes))) + if (CHECK_FLAG(l1->flags, LS_ATTR_METRIC) && (l1->metric != l2->metric)) return 0; - else - return 1; + if (CHECK_FLAG(l1->flags, LS_ATTR_TE_METRIC) + && (l1->standard.te_metric != l2->standard.te_metric)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_ADM_GRP) + && (l1->standard.admin_group != l2->standard.admin_group)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_LOCAL_ADDR) + && !IPV4_ADDR_SAME(&l1->standard.local, &l2->standard.local)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_NEIGH_ADDR) + && !IPV4_ADDR_SAME(&l1->standard.remote, &l2->standard.remote)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_LOCAL_ADDR6) + && !IPV6_ADDR_SAME(&l1->standard.local6, &l2->standard.local6)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_NEIGH_ADDR6) + && !IPV6_ADDR_SAME(&l1->standard.remote6, &l2->standard.remote6)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_LOCAL_ID) + && (l1->standard.local_id != l2->standard.local_id)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_NEIGH_ID) + && (l1->standard.remote_id != l2->standard.remote_id)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_MAX_BW) + && (l1->standard.max_bw != l2->standard.max_bw)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_MAX_RSV_BW) + && (l1->standard.max_rsv_bw != l2->standard.max_rsv_bw)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_UNRSV_BW) + && memcmp(&l1->standard.unrsv_bw, &l2->standard.unrsv_bw, 32) != 0) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_REMOTE_AS) + && (l1->standard.remote_as != l2->standard.remote_as)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_REMOTE_ADDR) + && !IPV4_ADDR_SAME(&l1->standard.remote_addr, + &l2->standard.remote_addr)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_REMOTE_ADDR6) + && !IPV6_ADDR_SAME(&l1->standard.remote_addr6, + &l2->standard.remote_addr6)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_DELAY) + && (l1->extended.delay != l2->extended.delay)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_MIN_MAX_DELAY) + && ((l1->extended.min_delay != l2->extended.min_delay) + || (l1->extended.max_delay != l2->extended.max_delay))) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_JITTER) + && (l1->extended.jitter != l2->extended.jitter)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_PACKET_LOSS) + && (l1->extended.pkt_loss != l2->extended.pkt_loss)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_AVA_BW) + && (l1->extended.ava_bw != l2->extended.ava_bw)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_RSV_BW) + && (l1->extended.rsv_bw != l2->extended.rsv_bw)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_USE_BW) + && (l1->extended.used_bw != l2->extended.used_bw)) + return 0; + if (CHECK_FLAG(l1->flags, LS_ATTR_ADJ_SID)) { + if ((l1->adj_sid[0].sid != l2->adj_sid[0].sid) + || (l1->adj_sid[0].flags != l2->adj_sid[0].flags) + || (l1->adj_sid[0].weight != l2->adj_sid[0].weight)) + return 0; + if (((l1->adv.origin == ISIS_L1) || (l1->adv.origin == ISIS_L2)) + && (memcmp(&l1->adj_sid[0].neighbor.sysid, + &l2->adj_sid[0].neighbor.sysid, ISO_SYS_ID_LEN) + != 0)) + return 0; + if (((l1->adv.origin == OSPFv2) || (l1->adv.origin == STATIC) + || (l1->adv.origin == DIRECT)) + && (!IPV4_ADDR_SAME(&l1->adj_sid[0].neighbor.addr, + &l2->adj_sid[0].neighbor.addr))) + return 0; + } + if (CHECK_FLAG(l1->flags, LS_ATTR_BCK_ADJ_SID)) { + if ((l1->adj_sid[1].sid != l2->adj_sid[1].sid) + || (l1->adj_sid[1].flags != l2->adj_sid[1].flags) + || (l1->adj_sid[1].weight != l2->adj_sid[1].weight)) + return 0; + if (((l1->adv.origin == ISIS_L1) || (l1->adv.origin == ISIS_L2)) + && (memcmp(&l1->adj_sid[1].neighbor.sysid, + &l2->adj_sid[1].neighbor.sysid, ISO_SYS_ID_LEN) + != 0)) + return 0; + if (((l1->adv.origin == OSPFv2) || (l1->adv.origin == STATIC) + || (l1->adv.origin == DIRECT)) + && (!IPV4_ADDR_SAME(&l1->adj_sid[1].neighbor.addr, + &l2->adj_sid[1].neighbor.addr))) + return 0; + } + if (CHECK_FLAG(l1->flags, LS_ATTR_SRLG) + && ((l1->srlg_len != l2->srlg_len) + || memcmp(l1->srlgs, l2->srlgs, + l1->srlg_len * sizeof(uint32_t)) + != 0)) + return 0; + + /* OK, l1 & l2 are equal */ + return 1; } /** @@ -223,29 +376,42 @@ void ls_prefix_del(struct ls_prefix *pref) int ls_prefix_same(struct ls_prefix *p1, struct ls_prefix *p2) { + /* First, check pointer */ if ((p1 && !p2) || (!p1 && p2)) return 0; if (p1 == p2) return 1; + /* Then, verify Flags and Origin */ if (p1->flags != p2->flags) return 0; - if (p1->adv.origin != p2->adv.origin) + if (!ls_node_id_same(p1->adv, p2->adv)) return 0; - if (!memcmp(&p1->adv.id, &p2->adv.id, sizeof(struct ls_node_id))) + /* Finally, check each individual parameters that are valid */ + if (prefix_same(&p1->pref, &p2->pref) == 0) return 0; - - /* Do we need to test individually each field, instead performing a - * global memcmp? There is a risk that an old value that is bit masked - * i.e. corresponding flag = 0, will result into a false negative - */ - if (!memcmp(p1, p2, sizeof(struct ls_prefix))) + if (CHECK_FLAG(p1->flags, LS_PREF_IGP_FLAG) + && (p1->igp_flag != p2->igp_flag)) return 0; - else - return 1; + if (CHECK_FLAG(p1->flags, LS_PREF_ROUTE_TAG) + && (p1->route_tag != p2->route_tag)) + return 0; + if (CHECK_FLAG(p1->flags, LS_PREF_EXTENDED_TAG) + && (p1->extended_tag != p2->extended_tag)) + return 0; + if (CHECK_FLAG(p1->flags, LS_PREF_METRIC) && (p1->metric != p2->metric)) + return 0; + if (CHECK_FLAG(p1->flags, LS_PREF_SR)) { + if ((p1->sr.algo != p2->sr.algo) || (p1->sr.sid != p2->sr.sid) + || (p1->sr.sid_flag != p2->sr.sid_flag)) + return 0; + } + + /* OK, p1 & p2 are equal */ + return 1; } /** @@ -844,11 +1010,11 @@ void ls_ted_del_all(struct ls_ted *ted) return; /* First remove Vertices, Edges and Subnets and associated Link State */ - frr_each (vertices, &ted->vertices, vertex) + frr_each_safe (vertices, &ted->vertices, vertex) ls_vertex_del_all(ted, vertex); - frr_each (edges, &ted->edges, edge) + frr_each_safe (edges, &ted->edges, edge) ls_edge_del_all(ted, edge); - frr_each (subnets, &ted->subnets, subnet) + frr_each_safe (subnets, &ted->subnets, subnet) ls_subnet_del_all(ted, subnet); /* then remove TED itself */ @@ -865,17 +1031,17 @@ void ls_ted_clean(struct ls_ted *ted) return; /* First, start with Vertices */ - frr_each (vertices, &ted->vertices, vertex) + frr_each_safe (vertices, &ted->vertices, vertex) if (vertex->status == ORPHAN) ls_vertex_del_all(ted, vertex); /* Then Edges */ - frr_each (edges, &ted->edges, edge) + frr_each_safe (edges, &ted->edges, edge) if (edge->status == ORPHAN) ls_edge_del_all(ted, edge); /* and Subnets */ - frr_each (subnets, &ted->subnets, subnet) + frr_each_safe (subnets, &ted->subnets, subnet) if (subnet->status == ORPHAN) ls_subnet_del_all(ted, subnet); diff --git a/lib/link_state.h b/lib/link_state.h index de116df89e..981e8b5285 100644 --- a/lib/link_state.h +++ b/lib/link_state.h @@ -94,6 +94,16 @@ struct ls_node_id { } id __attribute__((aligned(8))); }; +/** + * Check if two Link State Node IDs are equal. Note that this routine has the + * same return value sense as '==' (which is different from a comparison). + * + * @param i1 First Link State Node Identifier + * @param i2 Second Link State Node Identifier + * @return 1 if equal, 0 otherwise + */ +extern int ls_node_id_same(struct ls_node_id i1, struct ls_node_id i2); + /* Link State flags to indicate which Node parameters are valid */ #define LS_NODE_UNSET 0x0000 #define LS_NODE_NAME 0x0001 @@ -64,7 +64,7 @@ const char *lookup_msg(const struct message *mz, int kz, const char *nf) } /* For time string format. */ -size_t quagga_timestamp(int timestamp_precision, char *buf, size_t buflen) +size_t frr_timestamp(int timestamp_precision, char *buf, size_t buflen) { static struct { time_t last; @@ -355,9 +355,6 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_INTERFACE_NBR_ADDRESS_ADD), DESC_ENTRY(ZEBRA_INTERFACE_NBR_ADDRESS_DELETE), DESC_ENTRY(ZEBRA_INTERFACE_BFD_DEST_UPDATE), - DESC_ENTRY(ZEBRA_IMPORT_ROUTE_REGISTER), - DESC_ENTRY(ZEBRA_IMPORT_ROUTE_UNREGISTER), - DESC_ENTRY(ZEBRA_IMPORT_CHECK_UPDATE), DESC_ENTRY(ZEBRA_BFD_DEST_REGISTER), DESC_ENTRY(ZEBRA_BFD_DEST_DEREGISTER), DESC_ENTRY(ZEBRA_BFD_DEST_UPDATE), @@ -102,9 +102,9 @@ extern void zlog_backtrace_sigsafe(int priority, void *program_counter); It caches the most recent localtime result and can therefore avoid multiple calls within the same second. If buflen is too small, *buf will be set to '\0', and 0 will be returned. */ -#define QUAGGA_TIMESTAMP_LEN 40 -extern size_t quagga_timestamp(int timestamp_precision /* # subsecond digits */, - char *buf, size_t buflen); +#define FRR_TIMESTAMP_LEN 40 +extern size_t frr_timestamp(int timestamp_precision /* # subsecond digits */, + char *buf, size_t buflen); extern void zlog_hexdump(const void *mem, size_t len); extern const char *zlog_sanitize(char *buf, size_t bufsz, const void *in, @@ -135,8 +135,8 @@ struct timestamp_control { size_t len; /* length of rendered timestamp */ int precision; /* configuration parameter */ int already_rendered; /* should be initialized to 0 */ - char buf[QUAGGA_TIMESTAMP_LEN]; /* will contain the rendered timestamp - */ + char buf[FRR_TIMESTAMP_LEN]; /* will contain the rendered timestamp + */ }; /* Defines for use in command construction: */ diff --git a/lib/log_vty.c b/lib/log_vty.c index cbb8de8976..621949ab57 100644 --- a/lib/log_vty.c +++ b/lib/log_vty.c @@ -36,6 +36,8 @@ DEFINE_HOOK(zlog_rotate, (), ()); DEFINE_HOOK(zlog_cli_show, (struct vty * vty), (vty)); +static unsigned logmsgs_with_persist_bt; + static const int log_default_lvl = LOG_DEBUG; static int log_config_stdout_lvl = ZLOG_DISABLED; @@ -267,6 +269,43 @@ DEFUN_HIDDEN (no_config_log_monitor, return CMD_SUCCESS; } +DEFPY_NOSH (debug_uid_backtrace, + debug_uid_backtrace_cmd, + "[no] debug unique-id UID backtrace", + NO_STR + DEBUG_STR + "Options per individual log message, by unique ID\n" + "Log message unique ID (XXXXX-XXXXX)\n" + "Add backtrace to log when message is printed\n") +{ + struct xrefdata search, *xrd; + struct xrefdata_logmsg *xrdl; + uint8_t flag; + + strlcpy(search.uid, uid, sizeof(search.uid)); + xrd = xrefdata_uid_find(&xrefdata_uid, &search); + + if (!xrd) + return CMD_ERR_NOTHING_TODO; + + if (xrd->xref->type != XREFT_LOGMSG) { + vty_out(vty, "%% ID \"%s\" is not a log message\n", uid); + return CMD_WARNING; + } + xrdl = container_of(xrd, struct xrefdata_logmsg, xrefdata); + + flag = (vty->node == CONFIG_NODE) ? LOGMSG_FLAG_PERSISTENT + : LOGMSG_FLAG_EPHEMERAL; + + if ((xrdl->fl_print_bt & flag) == (no ? 0 : flag)) + return CMD_SUCCESS; + if (flag == LOGMSG_FLAG_PERSISTENT) + logmsgs_with_persist_bt += no ? -1 : 1; + + xrdl->fl_print_bt ^= flag; + return CMD_SUCCESS; +} + static int set_log_file(struct zlog_cfg_file *target, struct vty *vty, const char *fname, int loglevel) { @@ -751,6 +790,24 @@ void log_config_write(struct vty *vty) vty_out(vty, "no log error-category\n"); if (!zlog_get_prefix_xid()) vty_out(vty, "no log unique-id\n"); + + if (logmsgs_with_persist_bt) { + struct xrefdata *xrd; + struct xrefdata_logmsg *xrdl; + + vty_out(vty, "!\n"); + + frr_each (xrefdata_uid, &xrefdata_uid, xrd) { + if (xrd->xref->type != XREFT_LOGMSG) + continue; + + xrdl = container_of(xrd, struct xrefdata_logmsg, + xrefdata); + if (xrdl->fl_print_bt & LOGMSG_FLAG_PERSISTENT) + vty_out(vty, "debug unique-id %s backtrace\n", + xrd->uid); + } + } } static int log_vty_init(const char *progname, const char *protoname, @@ -801,4 +858,7 @@ void log_cmd_init(void) install_element(CONFIG_NODE, &config_log_filterfile_cmd); install_element(CONFIG_NODE, &no_config_log_filterfile_cmd); install_element(CONFIG_NODE, &log_immediate_mode_cmd); + + install_element(ENABLE_NODE, &debug_uid_backtrace_cmd); + install_element(CONFIG_NODE, &debug_uid_backtrace_cmd); } diff --git a/lib/netns_linux.c b/lib/netns_linux.c index cde842b88c..43c0d8c359 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -279,7 +279,7 @@ static void ns_disable_internal(struct ns *ns) } } -/* VRF list existance check by name. */ +/* VRF list existence check by name. */ static struct ns_map_nsid *ns_map_nsid_lookup_by_nsid(ns_id_t ns_id) { struct ns_map_nsid ns_map; diff --git a/lib/network.h b/lib/network.h index 4a9666984f..10ed917572 100644 --- a/lib/network.h +++ b/lib/network.h @@ -22,6 +22,13 @@ #ifndef _ZEBRA_NETWORK_H #define _ZEBRA_NETWORK_H +#ifdef HAVE_SYS_ENDIAN_H +#include <sys/endian.h> +#endif +#ifdef HAVE_ENDIAN_H +#include <endian.h> +#endif + #ifdef __cplusplus extern "C" { #endif @@ -45,6 +52,35 @@ extern int set_cloexec(int fd); extern float htonf(float); extern float ntohf(float); +/* force type for be64toh/htobe64 to be uint64_t, *without* a direct cast + * + * this is a workaround for false-positive printfrr warnings from FRR's + * frr-format GCC plugin that would be triggered from + * { printfrr("%"PRIu64, (uint64_t)be64toh(...)); } + * + * the key element here is that "(uint64_t)expr" causes the warning, while + * "({ uint64_t x = expr; x; })" does not. (The cast is the trigger, a + * variable of the same type works correctly.) + */ + +/* zap system definitions... */ +#ifdef be64toh +#undef be64toh +#endif +#ifdef htobe64 +#undef htobe64 +#endif + +#if BYTE_ORDER == LITTLE_ENDIAN +#define be64toh(x) ({ uint64_t r = __builtin_bswap64(x); r; }) +#define htobe64(x) ({ uint64_t r = __builtin_bswap64(x); r; }) +#elif BYTE_ORDER == BIG_ENDIAN +#define be64toh(x) ({ uint64_t r = (x); r; }) +#define htobe64(x) ({ uint64_t r = (x); r; }) +#else +#error nobody expects the endianish inquisition. check OS endian.h headers. +#endif + /** * Helper function that returns a random long value. The main purpose of * this function is to hide a `random()` call that gets flagged by coverity diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index 97d70189ff..e8c678ad71 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -953,12 +953,6 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, nhg_hooks.add_nexthop(nhgc, nh); } - if (intf) { - struct interface *ifp = if_lookup_by_name_all_vrf(intf); - - if (ifp) - ifp->configured = true; - } return CMD_SUCCESS; } @@ -1041,7 +1035,6 @@ void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh) void nexthop_group_json_nexthop(json_object *j, const struct nexthop *nh) { - char buf[100]; struct vrf *vrf; json_object *json_backups = NULL; int i; @@ -1052,26 +1045,18 @@ void nexthop_group_json_nexthop(json_object *j, const struct nexthop *nh) ifindex2ifname(nh->ifindex, nh->vrf_id)); break; case NEXTHOP_TYPE_IPV4: - json_object_string_add( - j, "nexthop", - inet_ntop(AF_INET, &nh->gate.ipv4, buf, sizeof(buf))); + json_object_string_addf(j, "nexthop", "%pI4", &nh->gate.ipv4); break; case NEXTHOP_TYPE_IPV4_IFINDEX: - json_object_string_add( - j, "nexthop", - inet_ntop(AF_INET, &nh->gate.ipv4, buf, sizeof(buf))); + json_object_string_addf(j, "nexthop", "%pI4", &nh->gate.ipv4); json_object_string_add(j, "vrfId", ifindex2ifname(nh->ifindex, nh->vrf_id)); break; case NEXTHOP_TYPE_IPV6: - json_object_string_add( - j, "nexthop", - inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf))); + json_object_string_addf(j, "nexthop", "%pI6", &nh->gate.ipv6); break; case NEXTHOP_TYPE_IPV6_IFINDEX: - json_object_string_add( - j, "nexthop", - inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf))); + json_object_string_addf(j, "nexthop", "%pI6", &nh->gate.ipv6); json_object_string_add(j, "vrfId", ifindex2ifname(nh->ifindex, nh->vrf_id)); break; @@ -1265,7 +1250,6 @@ void nexthop_group_interface_state_change(struct interface *ifp, if (ifp->ifindex != nhop.ifindex) continue; - ifp->configured = true; nh = nexthop_new(); memcpy(nh, &nhop, sizeof(nhop)); diff --git a/lib/northbound.c b/lib/northbound.c index 6edd5184ef..49adea6d53 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -830,8 +830,7 @@ int nb_candidate_validate(struct nb_context *context, struct nb_config_cbs changes; int ret; - if (nb_candidate_validate_yang(candidate, errmsg, sizeof(errmsg_len)) - != NB_OK) + if (nb_candidate_validate_yang(candidate, errmsg, errmsg_len) != NB_OK) return NB_ERR_VALIDATION; RB_INIT(nb_config_cbs, &changes); diff --git a/lib/northbound.h b/lib/northbound.h index bf04dbda17..a330bd1a30 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -488,7 +488,8 @@ struct nb_callbacks { * >0 when the CLI command for the dnode2 should be printed first * 0 when there is no difference */ - int (*cli_cmp)(struct lyd_node *dnode1, struct lyd_node *dnode2); + int (*cli_cmp)(const struct lyd_node *dnode1, + const struct lyd_node *dnode2); /* * Optional callback to show the CLI command associated to the given @@ -510,7 +511,7 @@ struct nb_callbacks { * nodes, in which case it might be desirable to hide one or more * parts of the command when this parameter is set to false. */ - void (*cli_show)(struct vty *vty, struct lyd_node *dnode, + void (*cli_show)(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); /* @@ -523,7 +524,7 @@ struct nb_callbacks { * libyang data node that should be shown in the form of a CLI * command. */ - void (*cli_show_end)(struct vty *vty, struct lyd_node *dnode); + void (*cli_show_end)(struct vty *vty, const struct lyd_node *dnode); }; struct nb_dependency_callbacks { diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index 6676c0b072..70c71b18c4 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -550,14 +550,16 @@ void nb_cli_show_config_prepare(struct nb_config *config, bool with_defaults) LYD_VALIDATE_NO_STATE, NULL); } -static int lyd_node_cmp(struct lyd_node **dnode1, struct lyd_node **dnode2) +static int lyd_node_cmp(const struct lyd_node **dnode1, + const struct lyd_node **dnode2) { struct nb_node *nb_node = (*dnode1)->schema->priv; return nb_node->cbs.cli_cmp(*dnode1, *dnode2); } -static void show_dnode_children_cmds(struct vty *vty, struct lyd_node *root, +static void show_dnode_children_cmds(struct vty *vty, + const struct lyd_node *root, bool with_defaults) { struct nb_node *nb_node, *sort_node = NULL; @@ -616,7 +618,7 @@ static void show_dnode_children_cmds(struct vty *vty, struct lyd_node *root, } } -void nb_cli_show_dnode_cmds(struct vty *vty, struct lyd_node *root, +void nb_cli_show_dnode_cmds(struct vty *vty, const struct lyd_node *root, bool with_defaults) { struct nb_node *nb_node; diff --git a/lib/northbound_cli.h b/lib/northbound_cli.h index 28f81f8b39..e472425447 100644 --- a/lib/northbound_cli.h +++ b/lib/northbound_cli.h @@ -127,7 +127,8 @@ extern int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input, * show_defaults * Specify whether to display default configuration values or not. */ -extern void nb_cli_show_dnode_cmds(struct vty *vty, struct lyd_node *dnode, +extern void nb_cli_show_dnode_cmds(struct vty *vty, + const struct lyd_node *dnode, bool show_defaults); /* diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c index e62a83cee2..e1c8983fca 100644 --- a/lib/northbound_confd.c +++ b/lib/northbound_confd.c @@ -491,6 +491,47 @@ static void *thread_cdb_trigger_subscriptions(void *data) return NULL; } +static int frr_confd_subscribe(const struct lysc_node *snode, void *arg) +{ + struct yang_module *module = arg; + struct nb_node *nb_node; + int *spoint; + int ret; + + switch (snode->nodetype) { + case LYS_CONTAINER: + case LYS_LEAF: + case LYS_LEAFLIST: + case LYS_LIST: + break; + default: + return YANG_ITER_CONTINUE; + } + + if (CHECK_FLAG(snode->flags, LYS_CONFIG_R)) + return YANG_ITER_CONTINUE; + + nb_node = snode->priv; + if (!nb_node) + return YANG_ITER_CONTINUE; + + DEBUGD(&nb_dbg_client_confd, "%s: subscribing to '%s'", __func__, + nb_node->xpath); + + spoint = XMALLOC(MTYPE_CONFD, sizeof(*spoint)); + ret = cdb_subscribe2(cdb_sub_sock, CDB_SUB_RUNNING_TWOPHASE, + CDB_SUB_WANT_ABORT_ON_ABORT, 3, spoint, + module->confd_hash, nb_node->xpath); + if (ret != CONFD_OK) { + flog_err_confd("cdb_subscribe2"); + XFREE(MTYPE_CONFD, spoint); + return YANG_ITER_CONTINUE; + } + + listnode_add(confd_spoints, spoint); + return YANG_ITER_CONTINUE; +} + static int frr_confd_init_cdb(void) { struct yang_module *module; @@ -514,8 +555,6 @@ static int frr_confd_init_cdb(void) /* Subscribe to all loaded YANG data modules. */ confd_spoints = list_new(); RB_FOREACH (module, yang_modules, &yang_modules) { - struct lysc_node *snode; - module->confd_hash = confd_str2hash(module->info->ns); if (module->confd_hash == 0) { flog_err( @@ -530,42 +569,8 @@ static int frr_confd_init_cdb(void) * entire YANG module. So we have to find the top level * nodes ourselves and subscribe to their paths. */ - LY_LIST_FOR (module->info->data, snode) { - struct nb_node *nb_node; - int *spoint; - int ret; - - switch (snode->nodetype) { - case LYS_CONTAINER: - case LYS_LEAF: - case LYS_LEAFLIST: - case LYS_LIST: - break; - default: - continue; - } - - if (CHECK_FLAG(snode->flags, LYS_CONFIG_R)) - continue; - - nb_node = snode->priv; - if (!nb_node) - continue; - - DEBUGD(&nb_dbg_client_confd, "%s: subscribing to '%s'", - __func__, nb_node->xpath); - - spoint = XMALLOC(MTYPE_CONFD, sizeof(*spoint)); - ret = cdb_subscribe2( - cdb_sub_sock, CDB_SUB_RUNNING_TWOPHASE, - CDB_SUB_WANT_ABORT_ON_ABORT, 3, spoint, - module->confd_hash, nb_node->xpath); - if (ret != CONFD_OK) { - flog_err_confd("cdb_subscribe2"); - XFREE(MTYPE_CONFD, spoint); - } - listnode_add(confd_spoints, spoint); - } + yang_snodes_iterate(module->info, frr_confd_subscribe, 0, + module); } if (cdb_subscribe_done(cdb_sub_sock) != CONFD_OK) { @@ -705,7 +710,7 @@ static int frr_confd_data_get_next(struct confd_trans_ctx *tctx, confd_data_reply_next_key(tctx, v, keys.num, (long)nb_next); } else { - char pointer_str[16]; + char pointer_str[32]; /* * ConfD 6.6 user guide, chapter 6.11 (Operational data @@ -843,7 +848,7 @@ static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx, const void *nb_next; #define CONFD_OBJECTS_PER_TIME 100 struct confd_next_object objects[CONFD_OBJECTS_PER_TIME + 1]; - char pseudo_keys[CONFD_OBJECTS_PER_TIME][16]; + char pseudo_keys[CONFD_OBJECTS_PER_TIME][32]; int nobjects = 0; frr_confd_get_xpath(kp, xpath, sizeof(xpath)); @@ -868,7 +873,7 @@ static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx, memset(objects, 0, sizeof(objects)); for (int j = 0; j < CONFD_OBJECTS_PER_TIME; j++) { struct confd_next_object *object; - struct lysc_node *child; + const struct lysc_node *child; struct yang_data *data; size_t nvalues = 0; @@ -1189,6 +1194,8 @@ static int frr_confd_dp_ctl_read(struct thread *thread) thread_add_read(master, frr_confd_dp_ctl_read, dctx, fd, &t_dp_ctl); frr_confd_dp_read(dctx, fd); + + return 0; } static int frr_confd_dp_worker_read(struct thread *thread) @@ -1199,6 +1206,8 @@ static int frr_confd_dp_worker_read(struct thread *thread) thread_add_read(master, frr_confd_dp_worker_read, dctx, fd, &t_dp_worker); frr_confd_dp_read(dctx, fd); + + return 0; } static int frr_confd_subscribe_state(const struct lysc_node *snode, void *arg) @@ -50,7 +50,7 @@ struct pbr_filter { #define PBR_FILTER_SRC_PORT_RANGE (1 << 6) #define PBR_FILTER_DST_PORT_RANGE (1 << 7) #define PBR_FILTER_DSFIELD (1 << 8) -#define PBR_FILTER_IP_PROTOCOL (1 << 9) +#define PBR_FILTER_IP_PROTOCOL (1 << 9) #define PBR_DSFIELD_DSCP (0xfc) /* Upper 6 bits of DS field: DSCP */ #define PBR_DSFIELD_ECN (0x03) /* Lower 2 bits of DS field: BCN */ @@ -83,6 +83,13 @@ struct pbr_filter { * the user criteria may directly point to a table too. */ struct pbr_action { + /* VLAN */ + uint8_t pcp; + uint16_t vlan_id; + uint16_t vlan_flags; + + uint32_t queue_id; + uint32_t table; }; diff --git a/lib/plist.c b/lib/plist.c index 2f9f06f43b..d6a63c1b0c 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -1010,7 +1010,6 @@ static void vty_show_prefix_entry(struct vty *vty, json_object *json, afi_t afi, if (json) { json_object *json_entry; - char buf[BUFSIZ]; json_entry = json_object_new_object(); json_object_array_add(json_entries, json_entry); @@ -1021,10 +1020,9 @@ static void vty_show_prefix_entry(struct vty *vty, json_object *json, afi_t afi, json_object_string_add( json_entry, "type", prefix_list_type_str(pentry)); - json_object_string_add( - json_entry, "prefix", - prefix2str(&pentry->prefix, buf, - sizeof(buf))); + json_object_string_addf(json_entry, "prefix", + "%pFX", + &pentry->prefix); if (pentry->ge) json_object_int_add( @@ -1127,14 +1125,7 @@ static int vty_show_prefix_list(struct vty *vty, afi_t afi, const char *name, master, dtype, seqnum); } - if (uj) { - vty_out(vty, "%s\n", - json_object_to_json_string_ext( - json, JSON_C_TO_STRING_PRETTY)); - json_object_free(json); - } - - return CMD_SUCCESS; + return vty_json(vty, json); } static int vty_show_prefix_list_prefix(struct vty *vty, afi_t afi, @@ -1489,9 +1480,9 @@ int prefix_bgp_orf_set(char *name, afi_t afi, struct orf_prefix *orfp, struct prefix_list_entry *pentry; /* ge and le value check */ - if (orfp->ge && orfp->ge <= orfp->p.prefixlen) + if (orfp->ge && orfp->ge < orfp->p.prefixlen) return CMD_WARNING_CONFIG_FAILED; - if (orfp->le && orfp->le <= orfp->p.prefixlen) + if (orfp->le && orfp->le < orfp->p.prefixlen) return CMD_WARNING_CONFIG_FAILED; if (orfp->le && orfp->ge > orfp->le) return CMD_WARNING_CONFIG_FAILED; @@ -1592,9 +1583,7 @@ int prefix_bgp_show_prefix_list(struct vty *vty, afi_t afi, char *name, json_object_object_add(json, "ipv6PrefixList", json_prefix); - vty_out(vty, "%s\n", json_object_to_json_string_ext( - json, JSON_C_TO_STRING_PRETTY)); - json_object_free(json); + vty_json(vty, json); } else { vty_out(vty, "ip%s prefix-list %s: %d entries\n", afi == AFI_IP ? "" : "v6", plist->name, plist->count); @@ -1668,6 +1657,8 @@ static const struct cmd_variable_handler plist_var_handlers[] = { {/* "prefix-list WORD" */ .varname = "prefix_list", .completions = plist_autocomplete}, + {.tokenname = "PREFIXLIST_NAME", + .completions = plist_autocomplete}, {.completions = NULL}}; diff --git a/lib/prefix.c b/lib/prefix.c index ef7d2e59da..df753fe10b 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -113,7 +113,7 @@ const char *family2str(int family) return "?"; } -/* Address Famiy Identifier to Address Family converter. */ +/* Address Family Identifier to Address Family converter. */ int afi2family(afi_t afi) { if (afi == AFI_IP) diff --git a/lib/resolver.c b/lib/resolver.c index 4aba909f25..29138bbc8d 100644 --- a/lib/resolver.c +++ b/lib/resolver.c @@ -14,12 +14,14 @@ #include <ares.h> #include <ares_version.h> -#include "vector.h" +#include "typesafe.h" +#include "jhash.h" #include "thread.h" #include "lib_errors.h" #include "resolver.h" #include "command.h" #include "xref.h" +#include "vrf.h" XREF_SETUP(); @@ -27,13 +29,78 @@ struct resolver_state { ares_channel channel; struct thread_master *master; struct thread *timeout; - vector read_threads, write_threads; }; static struct resolver_state state; static bool resolver_debug; -#define THREAD_RUNNING ((struct thread *)-1) +/* a FD doesn't necessarily map 1:1 to a request; we could be talking to + * multiple caches simultaneously, to see which responds fastest. + * Theoretically we could also be using the same fd for multiple lookups, + * but the c-ares API guarantees an n:1 mapping for fd => channel. + * + * Either way c-ares makes that decision and we just need to deal with + * whatever FDs it gives us. + */ + +DEFINE_MTYPE_STATIC(LIB, ARES_FD, "c-ares (DNS) file descriptor information"); +PREDECL_HASH(resolver_fds); + +struct resolver_fd { + struct resolver_fds_item itm; + + int fd; + struct resolver_state *state; + struct thread *t_read, *t_write; +}; + +static int resolver_fd_cmp(const struct resolver_fd *a, + const struct resolver_fd *b) +{ + return numcmp(a->fd, b->fd); +} + +static uint32_t resolver_fd_hash(const struct resolver_fd *item) +{ + return jhash_1word(item->fd, 0xacd04c9e); +} + +DECLARE_HASH(resolver_fds, struct resolver_fd, itm, resolver_fd_cmp, + resolver_fd_hash); + +static struct resolver_fds_head resfds[1] = {INIT_HASH(resfds[0])}; + +static struct resolver_fd *resolver_fd_get(int fd, + struct resolver_state *newstate) +{ + struct resolver_fd ref = {.fd = fd}, *res; + + res = resolver_fds_find(resfds, &ref); + if (!res && newstate) { + res = XCALLOC(MTYPE_ARES_FD, sizeof(*res)); + res->fd = fd; + res->state = newstate; + resolver_fds_add(resfds, res); + + if (resolver_debug) + zlog_debug("c-ares registered FD %d", fd); + } + return res; +} + +static void resolver_fd_drop_maybe(struct resolver_fd *resfd) +{ + if (resfd->t_read || resfd->t_write) + return; + + if (resolver_debug) + zlog_debug("c-ares unregistered FD %d", resfd->fd); + + resolver_fds_del(resfds, resfd); + XFREE(MTYPE_ARES_FD, resfd); +} + +/* end of FD housekeeping */ static void resolver_update_timeouts(struct resolver_state *r); @@ -41,9 +108,7 @@ static int resolver_cb_timeout(struct thread *t) { struct resolver_state *r = THREAD_ARG(t); - r->timeout = THREAD_RUNNING; ares_process(r->channel, NULL, NULL); - r->timeout = NULL; resolver_update_timeouts(r); return 0; @@ -51,17 +116,16 @@ static int resolver_cb_timeout(struct thread *t) static int resolver_cb_socket_readable(struct thread *t) { - struct resolver_state *r = THREAD_ARG(t); - int fd = THREAD_FD(t); - struct thread **t_ptr; - - vector_set_index(r->read_threads, fd, THREAD_RUNNING); - ares_process_fd(r->channel, fd, ARES_SOCKET_BAD); - if (vector_lookup(r->read_threads, fd) == THREAD_RUNNING) { - t_ptr = (struct thread **)vector_get_index(r->read_threads, fd); - thread_add_read(r->master, resolver_cb_socket_readable, r, fd, - t_ptr); - } + struct resolver_fd *resfd = THREAD_ARG(t); + struct resolver_state *r = resfd->state; + + thread_add_read(r->master, resolver_cb_socket_readable, resfd, + resfd->fd, &resfd->t_read); + /* ^ ordering important: + * ares_process_fd may transitively call THREAD_OFF(resfd->t_read) + * combined with resolver_fd_drop_maybe, so resfd may be free'd after! + */ + ares_process_fd(r->channel, resfd->fd, ARES_SOCKET_BAD); resolver_update_timeouts(r); return 0; @@ -69,17 +133,16 @@ static int resolver_cb_socket_readable(struct thread *t) static int resolver_cb_socket_writable(struct thread *t) { - struct resolver_state *r = THREAD_ARG(t); - int fd = THREAD_FD(t); - struct thread **t_ptr; - - vector_set_index(r->write_threads, fd, THREAD_RUNNING); - ares_process_fd(r->channel, ARES_SOCKET_BAD, fd); - if (vector_lookup(r->write_threads, fd) == THREAD_RUNNING) { - t_ptr = (struct thread **)vector_get_index(r->write_threads, fd); - thread_add_write(r->master, resolver_cb_socket_writable, r, fd, - t_ptr); - } + struct resolver_fd *resfd = THREAD_ARG(t); + struct resolver_state *r = resfd->state; + + thread_add_write(r->master, resolver_cb_socket_writable, resfd, + resfd->fd, &resfd->t_write); + /* ^ ordering important: + * ares_process_fd may transitively call THREAD_OFF(resfd->t_write) + * combined with resolver_fd_drop_maybe, so resfd may be free'd after! + */ + ares_process_fd(r->channel, ARES_SOCKET_BAD, resfd->fd); resolver_update_timeouts(r); return 0; @@ -89,13 +152,11 @@ static void resolver_update_timeouts(struct resolver_state *r) { struct timeval *tv, tvbuf; - if (r->timeout == THREAD_RUNNING) - return; - THREAD_OFF(r->timeout); tv = ares_timeout(r->channel, NULL, &tvbuf); if (tv) { unsigned int timeoutms = tv->tv_sec * 1000 + tv->tv_usec / 1000; + thread_add_timer_msec(r->master, resolver_cb_timeout, r, timeoutms, &r->timeout); } @@ -105,43 +166,27 @@ static void ares_socket_cb(void *data, ares_socket_t fd, int readable, int writable) { struct resolver_state *r = (struct resolver_state *)data; - struct thread *t, **t_ptr; - - if (readable) { - t = vector_lookup(r->read_threads, fd); - if (!t) { - t_ptr = (struct thread **)vector_get_index( - r->read_threads, fd); - thread_add_read(r->master, resolver_cb_socket_readable, - r, fd, t_ptr); - } - } else { - t = vector_lookup(r->read_threads, fd); - if (t) { - if (t != THREAD_RUNNING) { - THREAD_OFF(t); - } - vector_unset(r->read_threads, fd); - } - } + struct resolver_fd *resfd; - if (writable) { - t = vector_lookup(r->write_threads, fd); - if (!t) { - t_ptr = (struct thread **)vector_get_index( - r->write_threads, fd); - thread_add_read(r->master, resolver_cb_socket_writable, - r, fd, t_ptr); - } - } else { - t = vector_lookup(r->write_threads, fd); - if (t) { - if (t != THREAD_RUNNING) { - THREAD_OFF(t); - } - vector_unset(r->write_threads, fd); - } - } + resfd = resolver_fd_get(fd, (readable || writable) ? r : NULL); + if (!resfd) + return; + + assert(resfd->state == r); + + if (!readable) + THREAD_OFF(resfd->t_read); + else if (!resfd->t_read) + thread_add_read(r->master, resolver_cb_socket_readable, resfd, + fd, &resfd->t_read); + + if (!writable) + THREAD_OFF(resfd->t_write); + else if (!resfd->t_write) + thread_add_write(r->master, resolver_cb_socket_writable, resfd, + fd, &resfd->t_write); + + resolver_fd_drop_maybe(resfd); } @@ -200,7 +245,7 @@ static int resolver_cb_literal(struct thread *t) return 0; } -void resolver_resolve(struct resolver_query *query, int af, +void resolver_resolve(struct resolver_query *query, int af, vrf_id_t vrf_id, const char *hostname, void (*callback)(struct resolver_query *, const char *, int, union sockunion *)) @@ -235,7 +280,18 @@ void resolver_resolve(struct resolver_query *query, int af, if (resolver_debug) zlog_debug("[%p] Resolving '%s'", query, hostname); + ret = vrf_switch_to_netns(vrf_id); + if (ret < 0) { + flog_err_sys(EC_LIB_SOCKET, "%s: Can't switch to VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + return; + } ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query); + ret = vrf_switchback_to_initial(); + if (ret < 0) + flog_err_sys(EC_LIB_SOCKET, + "%s: Can't switchback from VRF %u (%s)", __func__, + vrf_id, safe_strerror(errno)); resolver_update_timeouts(&state); } @@ -271,8 +327,6 @@ void resolver_init(struct thread_master *tm) struct ares_options ares_opts; state.master = tm; - state.read_threads = vector_init(1); - state.write_threads = vector_init(1); ares_opts = (struct ares_options){ .sock_state_cb = &ares_socket_cb, diff --git a/lib/resolver.h b/lib/resolver.h index 5f922dcb57..988449693c 100644 --- a/lib/resolver.h +++ b/lib/resolver.h @@ -27,10 +27,10 @@ struct resolver_query { }; void resolver_init(struct thread_master *tm); -void resolver_resolve(struct resolver_query *query, int af, - const char *hostname, void (*cb)(struct resolver_query *, - const char *, int, - union sockunion *)); +void resolver_resolve(struct resolver_query *query, int af, vrf_id_t vrf_id, + const char *hostname, + void (*cb)(struct resolver_query *, const char *, int, + union sockunion *)); #ifdef __cplusplus } diff --git a/lib/route_opaque.h b/lib/route_opaque.h index 7c4e9a16e1..c5e7d6a327 100644 --- a/lib/route_opaque.h +++ b/lib/route_opaque.h @@ -36,6 +36,12 @@ struct bgp_zebra_opaque { /* Show at least 10 large-communities AA:BB:CC */ char lcommunity[LCOMMUNITY_SIZE * 30]; + + /* 32 bytes seems enough because of + * bgp_path_selection_confed_as_path which is + * `Confederation based AS Path`. + */ + char selection_reason[BGP_MAX_SELECTION_REASON_STR_BUF]; }; static_assert(sizeof(struct bgp_zebra_opaque) <= ZAPI_MESSAGE_OPAQUE_LENGTH, diff --git a/lib/routemap.c b/lib/routemap.c index 5c60b7d1c6..7f733c8114 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -34,6 +34,7 @@ #include "lib_errors.h" #include "table.h" #include "json.h" +#include "jhash.h" DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP, "Route map"); DEFINE_MTYPE(LIB, ROUTE_MAP_NAME, "Route map name"); @@ -47,6 +48,27 @@ DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_DEP_DATA, "Route map dependency data"); DEFINE_QOBJ_TYPE(route_map_index); DEFINE_QOBJ_TYPE(route_map); +static int rmap_cmd_name_cmp(const struct route_map_rule_cmd_proxy *a, + const struct route_map_rule_cmd_proxy *b) +{ + return strcmp(a->cmd->str, b->cmd->str); +} + +static uint32_t rmap_cmd_name_hash(const struct route_map_rule_cmd_proxy *item) +{ + return jhash(item->cmd->str, strlen(item->cmd->str), 0xbfd69320); +} + +DECLARE_HASH(rmap_cmd_name, struct route_map_rule_cmd_proxy, itm, + rmap_cmd_name_cmp, rmap_cmd_name_hash); + +static struct rmap_cmd_name_head rmap_match_cmds[1] = { + INIT_HASH(rmap_match_cmds[0]), +}; +static struct rmap_cmd_name_head rmap_set_cmds[1] = { + INIT_HASH(rmap_set_cmds[0]), +}; + #define IPv4_PREFIX_LIST "ip address prefix-list" #define IPv6_PREFIX_LIST "ipv6 address prefix-list" @@ -61,12 +83,6 @@ struct route_map_pentry_dep { route_map_event_t event; }; -/* Vector for route match rules. */ -static vector route_match_vec; - -/* Vector for route set rules. */ -static vector route_set_vec; - static void route_map_pfx_tbl_update(route_map_event_t event, struct route_map_index *index, afi_t afi, const char *plist_name); @@ -159,6 +175,22 @@ void route_map_no_match_ip_next_hop_hook(int (*func)( rmap_match_set_hook.no_match_ip_next_hop = func; } +/* match ipv6 next-hop */ +void route_map_match_ipv6_next_hop_hook(int (*func)( + struct route_map_index *index, const char *command, const char *arg, + route_map_event_t type, char *errmsg, size_t errmsg_len)) +{ + rmap_match_set_hook.match_ipv6_next_hop = func; +} + +/* no match ipv6 next-hop */ +void route_map_no_match_ipv6_next_hop_hook(int (*func)( + struct route_map_index *index, const char *command, const char *arg, + route_map_event_t type, char *errmsg, size_t errmsg_len)) +{ + rmap_match_set_hook.no_match_ipv6_next_hop = func; +} + /* match ip next hop prefix list */ void route_map_match_ip_next_hop_prefix_list_hook(int (*func)( struct route_map_index *index, const char *command, @@ -250,6 +282,22 @@ void route_map_no_match_ipv6_next_hop_type_hook(int (*func)( rmap_match_set_hook.no_match_ipv6_next_hop_type = func; } +/* match ipv6 next-hop prefix-list */ +void route_map_match_ipv6_next_hop_prefix_list_hook(int (*func)( + struct route_map_index *index, const char *command, const char *arg, + route_map_event_t type, char *errmsg, size_t errmsg_len)) +{ + rmap_match_set_hook.match_ipv6_next_hop_prefix_list = func; +} + +/* no match ipv6 next-hop prefix-list */ +void route_map_no_match_ipv6_next_hop_prefix_list_hook(int (*func)( + struct route_map_index *index, const char *command, const char *arg, + route_map_event_t type, char *errmsg, size_t errmsg_len)) +{ + rmap_match_set_hook.no_match_ipv6_next_hop_prefix_list = func; +} + /* match metric */ void route_map_match_metric_hook(int (*func)( struct route_map_index *index, const char *command, @@ -1026,14 +1074,7 @@ static int vty_show_route_map(struct vty *vty, const char *name, bool use_json) list_delete(&maplist); } - if (use_json) { - vty_out(vty, "%s\n", - json_object_to_json_string_ext( - json, JSON_C_TO_STRING_PRETTY)); - json_object_free(json); - } - - return CMD_SUCCESS; + return vty_json(vty, json); } /* Unused route map details */ @@ -1231,40 +1272,40 @@ static struct route_map_rule *route_map_rule_new(void) } /* Install rule command to the match list. */ -void route_map_install_match(const struct route_map_rule_cmd *cmd) +void _route_map_install_match(struct route_map_rule_cmd_proxy *proxy) { - vector_set(route_match_vec, (void *)cmd); + rmap_cmd_name_add(rmap_match_cmds, proxy); } /* Install rule command to the set list. */ -void route_map_install_set(const struct route_map_rule_cmd *cmd) +void _route_map_install_set(struct route_map_rule_cmd_proxy *proxy) { - vector_set(route_set_vec, (void *)cmd); + rmap_cmd_name_add(rmap_set_cmds, proxy); } /* Lookup rule command from match list. */ static const struct route_map_rule_cmd *route_map_lookup_match(const char *name) { - unsigned int i; - const struct route_map_rule_cmd *rule; + struct route_map_rule_cmd refcmd = {.str = name}; + struct route_map_rule_cmd_proxy ref = {.cmd = &refcmd}; + struct route_map_rule_cmd_proxy *res; - for (i = 0; i < vector_active(route_match_vec); i++) - if ((rule = vector_slot(route_match_vec, i)) != NULL) - if (strcmp(rule->str, name) == 0) - return rule; + res = rmap_cmd_name_find(rmap_match_cmds, &ref); + if (res) + return res->cmd; return NULL; } /* Lookup rule command from set list. */ static const struct route_map_rule_cmd *route_map_lookup_set(const char *name) { - unsigned int i; - const struct route_map_rule_cmd *rule; + struct route_map_rule_cmd refcmd = {.str = name}; + struct route_map_rule_cmd_proxy ref = {.cmd = &refcmd}; + struct route_map_rule_cmd_proxy *res; - for (i = 0; i < vector_active(route_set_vec); i++) - if ((rule = vector_slot(route_set_vec, i)) != NULL) - if (strcmp(rule->str, name) == 0) - return rule; + res = rmap_cmd_name_find(rmap_set_cmds, &ref); + if (res) + return res->cmd; return NULL; } @@ -2480,7 +2521,7 @@ void route_map_notify_pentry_dependencies(const char *affected_name, do whatever the exit policy (EXIT, NEXT or GOTO) tells. on-match next - If this clause is matched, then the set statements are executed and then we drop through to the next clause - on-match goto n - If this clause is matched, then the set statments + on-match goto n - If this clause is matched, then the set statements are executed and then we goto the nth clause, or the first clause greater than this. In order to ensure route-maps *always* exit, you cannot jump backwards. @@ -3161,11 +3202,21 @@ void route_map_rule_tag_free(void *rule) void route_map_finish(void) { int i; + struct route_map_rule_cmd_proxy *proxy; + + /* these 2 hash tables have INIT_HASH initializers, so the "default" + * state is "initialized & empty" => fini() followed by init() to + * return to that same state + */ + while ((proxy = rmap_cmd_name_pop(rmap_match_cmds))) + (void)proxy; + rmap_cmd_name_fini(rmap_match_cmds); + rmap_cmd_name_init(rmap_match_cmds); - vector_free(route_match_vec); - route_match_vec = NULL; - vector_free(route_set_vec); - route_set_vec = NULL; + while ((proxy = rmap_cmd_name_pop(rmap_set_cmds))) + (void)proxy; + rmap_cmd_name_fini(rmap_set_cmds); + rmap_cmd_name_init(rmap_set_cmds); /* * All protocols are setting these to NULL @@ -3309,9 +3360,6 @@ void route_map_init(void) { int i; - /* Make vector for match and set. */ - route_match_vec = vector_init(1); - route_set_vec = vector_init(1); route_map_master_hash = hash_create_size(8, route_map_hash_key_make, route_map_hash_cmp, "Route Map Master Hash"); diff --git a/lib/routemap.h b/lib/routemap.h index 2c8eb24537..6c4916898a 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -21,6 +21,7 @@ #ifndef _ZEBRA_ROUTEMAP_H #define _ZEBRA_ROUTEMAP_H +#include "typesafe.h" #include "prefix.h" #include "memory.h" #include "qobj.h" @@ -243,12 +244,16 @@ DECLARE_QOBJ_TYPE(route_map); (strmatch(C, "frr-route-map:ipv6-address-list")) #define IS_MATCH_IPv4_NEXTHOP_LIST(C) \ (strmatch(C, "frr-route-map:ipv4-next-hop-list")) +#define IS_MATCH_IPv6_NEXTHOP_LIST(C) \ + (strmatch(C, "frr-route-map:ipv6-next-hop-list")) #define IS_MATCH_IPv4_PREFIX_LIST(C) \ (strmatch(C, "frr-route-map:ipv4-prefix-list")) #define IS_MATCH_IPv6_PREFIX_LIST(C) \ (strmatch(C, "frr-route-map:ipv6-prefix-list")) #define IS_MATCH_IPv4_NEXTHOP_PREFIX_LIST(C) \ (strmatch(C, "frr-route-map:ipv4-next-hop-prefix-list")) +#define IS_MATCH_IPv6_NEXTHOP_PREFIX_LIST(C) \ + (strmatch(C, "frr-route-map:ipv6-next-hop-prefix-list")) #define IS_MATCH_IPv4_NEXTHOP_TYPE(C) \ (strmatch(C, "frr-route-map:ipv4-next-hop-type")) #define IS_MATCH_IPv6_NEXTHOP_TYPE(C) \ @@ -422,8 +427,37 @@ extern enum rmap_compile_rets route_map_delete_set(struct route_map_index *index, const char *set_name, const char *set_arg); +/* struct route_map_rule_cmd is kept const in order to not have writable + * function pointers (which is a security benefit.) Hence, below struct is + * used as proxy for hashing these for by-name lookup. + */ + +PREDECL_HASH(rmap_cmd_name); + +struct route_map_rule_cmd_proxy { + struct rmap_cmd_name_item itm; + const struct route_map_rule_cmd *cmd; +}; + +/* ... and just automatically create a proxy struct for each call location + * to route_map_install_{match,set} to avoid unnecessarily added boilerplate + * for each route-map user + */ + +#define route_map_install_match(c) \ + do { \ + static struct route_map_rule_cmd_proxy proxy = {.cmd = c}; \ + _route_map_install_match(&proxy); \ + } while (0) + +#define route_map_install_set(c) \ + do { \ + static struct route_map_rule_cmd_proxy proxy = {.cmd = c}; \ + _route_map_install_set(&proxy); \ + } while (0) + /* Install rule command to the match list. */ -extern void route_map_install_match(const struct route_map_rule_cmd *cmd); +extern void _route_map_install_match(struct route_map_rule_cmd_proxy *proxy); /* * Install rule command to the set list. @@ -434,7 +468,7 @@ extern void route_map_install_match(const struct route_map_rule_cmd *cmd); * in the apply command). See 'set metric' command * as it is handled in ripd/ripngd and ospfd. */ -extern void route_map_install_set(const struct route_map_rule_cmd *cmd); +extern void _route_map_install_set(struct route_map_rule_cmd_proxy *proxy); /* Lookup route map by name. */ extern struct route_map *route_map_lookup_by_name(const char *name); @@ -525,9 +559,16 @@ extern void route_map_match_ip_next_hop_hook(int (*func)( char *errmsg, size_t errmsg_len)); /* no match ip next hop */ extern void route_map_no_match_ip_next_hop_hook(int (*func)( - struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type, - char *errmsg, size_t errmsg_len)); + struct route_map_index *index, const char *command, const char *arg, + route_map_event_t type, char *errmsg, size_t errmsg_len)); +/* match ipv6 next hop */ +extern void route_map_match_ipv6_next_hop_hook(int (*func)( + struct route_map_index *index, const char *command, const char *arg, + route_map_event_t type, char *errmsg, size_t errmsg_len)); +/* no match ipv6 next hop */ +extern void route_map_no_match_ipv6_next_hop_hook(int (*func)( + struct route_map_index *index, const char *command, const char *arg, + route_map_event_t type, char *errmsg, size_t errmsg_len)); /* match ip next hop prefix list */ extern void route_map_match_ip_next_hop_prefix_list_hook(int (*func)( struct route_map_index *index, const char *command, @@ -578,6 +619,14 @@ extern void route_map_no_match_ipv6_next_hop_type_hook(int (*func)( struct route_map_index *index, const char *command, const char *arg, route_map_event_t type, char *errmsg, size_t errmsg_len)); +/* match ipv6 next-hop prefix-list */ +extern void route_map_match_ipv6_next_hop_prefix_list_hook(int (*func)( + struct route_map_index *index, const char *command, const char *arg, + route_map_event_t type, char *errmsg, size_t errmsg_len)); +/* no match ipv6 next-hop prefix-list */ +extern void route_map_no_match_ipv6_next_hop_prefix_list_hook(int (*func)( + struct route_map_index *index, const char *command, const char *arg, + route_map_event_t type, char *errmsg, size_t errmsg_len)); /* match metric */ extern void route_map_match_metric_hook(int (*func)( struct route_map_index *index, const char *command, @@ -713,6 +762,33 @@ struct route_map_match_set_hooks { route_map_event_t type, char *errmsg, size_t errmsg_len); + /* match ipv6 next hop */ + int (*match_ipv6_next_hop)(struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type, char *errmsg, + size_t errmsg_len); + + /* no match ipv6 next hop */ + int (*no_match_ipv6_next_hop)(struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type, char *errmsg, + size_t errmsg_len); + + /* match ipv6 next hop prefix-list */ + int (*match_ipv6_next_hop_prefix_list)(struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type, + char *errmsg, size_t errmsg_len); + + /* no match ipv6 next-hop prefix-list */ + int (*no_match_ipv6_next_hop_prefix_list)(struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type, + char *errmsg, + size_t errmsg_len); + /* match ip next hop prefix list */ int (*match_ip_next_hop_prefix_list)(struct route_map_index *index, const char *command, @@ -908,25 +984,28 @@ void routemap_hook_context_free(struct routemap_hook_context *rhc); extern const struct frr_yang_module_info frr_route_map_info; /* routemap_cli.c */ -extern int route_map_instance_cmp(struct lyd_node *dnode1, - struct lyd_node *dnode2); -extern void route_map_instance_show(struct vty *vty, struct lyd_node *dnode, +extern int route_map_instance_cmp(const struct lyd_node *dnode1, + const struct lyd_node *dnode2); +extern void route_map_instance_show(struct vty *vty, + const struct lyd_node *dnode, bool show_defaults); extern void route_map_instance_show_end(struct vty *vty, - struct lyd_node *dnode); -extern void route_map_condition_show(struct vty *vty, struct lyd_node *dnode, + const struct lyd_node *dnode); +extern void route_map_condition_show(struct vty *vty, + const struct lyd_node *dnode, bool show_defaults); -extern void route_map_action_show(struct vty *vty, struct lyd_node *dnode, +extern void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); -extern void route_map_exit_policy_show(struct vty *vty, struct lyd_node *dnode, +extern void route_map_exit_policy_show(struct vty *vty, + const struct lyd_node *dnode, bool show_defaults); -extern void route_map_call_show(struct vty *vty, struct lyd_node *dnode, +extern void route_map_call_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); extern void route_map_description_show(struct vty *vty, - struct lyd_node *dnode, + const struct lyd_node *dnode, bool show_defaults); extern void route_map_optimization_disabled_show(struct vty *vty, - struct lyd_node *dnode, + const struct lyd_node *dnode, bool show_defaults); extern void route_map_cli_init(void); diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index cadad15fa7..2685bd2d79 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -41,7 +41,7 @@ DEFPY_YANG_NOSH( route_map, route_map_cmd, - "route-map WORD$name <deny|permit>$action (1-65535)$sequence", + "route-map RMAP_NAME$name <deny|permit>$action (1-65535)$sequence", ROUTE_MAP_CMD_STR ROUTE_MAP_OP_CMD_STR ROUTE_MAP_SEQUENCE_CMD_STR) @@ -71,7 +71,7 @@ DEFPY_YANG_NOSH( DEFPY_YANG( no_route_map_all, no_route_map_all_cmd, - "no route-map WORD$name", + "no route-map RMAP_NAME$name", NO_STR ROUTE_MAP_CMD_STR) { @@ -86,7 +86,7 @@ DEFPY_YANG( DEFPY_YANG( no_route_map, no_route_map_cmd, - "no route-map WORD$name <deny|permit>$action (1-65535)$sequence", + "no route-map RMAP_NAME$name <deny|permit>$action (1-65535)$sequence", NO_STR ROUTE_MAP_CMD_STR ROUTE_MAP_OP_CMD_STR @@ -103,7 +103,8 @@ DEFPY_YANG( return nb_cli_apply_changes(vty, NULL); } -int route_map_instance_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2) +int route_map_instance_cmp(const struct lyd_node *dnode1, + const struct lyd_node *dnode2) { uint16_t seq1 = yang_dnode_get_uint16(dnode1, "./sequence"); uint16_t seq2 = yang_dnode_get_uint16(dnode2, "./sequence"); @@ -111,7 +112,7 @@ int route_map_instance_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2) return seq1 - seq2; } -void route_map_instance_show(struct vty *vty, struct lyd_node *dnode, +void route_map_instance_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { const char *name = yang_dnode_get_string(dnode, "../name"); @@ -122,7 +123,7 @@ void route_map_instance_show(struct vty *vty, struct lyd_node *dnode, } -void route_map_instance_show_end(struct vty *vty, struct lyd_node *dnode) +void route_map_instance_show_end(struct vty *vty, const struct lyd_node *dnode) { vty_out(vty, "exit\n"); vty_out(vty, "!\n"); @@ -165,12 +166,10 @@ DEFPY_YANG( DEFPY_YANG( match_ip_address, match_ip_address_cmd, - "match ip address <(1-199)|(1300-2699)|WORD>$name", + "match ip address ACCESSLIST4_NAME$name", MATCH_STR IP_STR "Match address of route\n" - "IP access-list number\n" - "IP access-list number (expanded range)\n" "IP Access-list name\n") { const char *xpath = @@ -187,13 +186,11 @@ DEFPY_YANG( DEFPY_YANG( no_match_ip_address, no_match_ip_address_cmd, - "no match ip address [<(1-199)|(1300-2699)|WORD>]", + "no match ip address [ACCESSLIST4_NAME]", NO_STR MATCH_STR IP_STR "Match address of route\n" - "IP access-list number\n" - "IP access-list number (expanded range)\n" "IP Access-list name\n") { const char *xpath = @@ -207,7 +204,7 @@ DEFPY_YANG( DEFPY_YANG( match_ip_address_prefix_list, match_ip_address_prefix_list_cmd, - "match ip address prefix-list WORD$name", + "match ip address prefix-list PREFIXLIST_NAME$name", MATCH_STR IP_STR "Match address of route\n" @@ -228,7 +225,7 @@ DEFPY_YANG( DEFPY_YANG( no_match_ip_address_prefix_list, no_match_ip_address_prefix_list_cmd, - "no match ip address prefix-list [WORD]", + "no match ip address prefix-list [PREFIXLIST_NAME]", NO_STR MATCH_STR IP_STR @@ -246,12 +243,10 @@ DEFPY_YANG( DEFPY_YANG( match_ip_next_hop, match_ip_next_hop_cmd, - "match ip next-hop <(1-199)|(1300-2699)|WORD>$name", + "match ip next-hop ACCESSLIST4_NAME$name", MATCH_STR IP_STR "Match next-hop address of route\n" - "IP access-list number\n" - "IP access-list number (expanded range)\n" "IP Access-list name\n") { const char *xpath = @@ -268,13 +263,11 @@ DEFPY_YANG( DEFPY_YANG( no_match_ip_next_hop, no_match_ip_next_hop_cmd, - "no match ip next-hop [<(1-199)|(1300-2699)|WORD>]", + "no match ip next-hop [ACCESSLIST4_NAME]", NO_STR MATCH_STR IP_STR "Match address of route\n" - "IP access-list number\n" - "IP access-list number (expanded range)\n" "IP Access-list name\n") { const char *xpath = @@ -288,7 +281,7 @@ DEFPY_YANG( DEFPY_YANG( match_ip_next_hop_prefix_list, match_ip_next_hop_prefix_list_cmd, - "match ip next-hop prefix-list WORD$name", + "match ip next-hop prefix-list PREFIXLIST_NAME$name", MATCH_STR IP_STR "Match next-hop address of route\n" @@ -310,7 +303,7 @@ DEFPY_YANG( DEFPY_YANG( no_match_ip_next_hop_prefix_list, no_match_ip_next_hop_prefix_list_cmd, - "no match ip next-hop prefix-list [WORD]", + "no match ip next-hop prefix-list [PREFIXLIST_NAME]", NO_STR MATCH_STR IP_STR @@ -365,7 +358,7 @@ DEFPY_YANG( DEFPY_YANG( match_ipv6_address, match_ipv6_address_cmd, - "match ipv6 address WORD$name", + "match ipv6 address ACCESSLIST6_NAME$name", MATCH_STR IPV6_STR "Match IPv6 address of route\n" @@ -385,7 +378,7 @@ DEFPY_YANG( DEFPY_YANG( no_match_ipv6_address, no_match_ipv6_address_cmd, - "no match ipv6 address [WORD]", + "no match ipv6 address [ACCESSLIST6_NAME]", NO_STR MATCH_STR IPV6_STR @@ -402,7 +395,7 @@ DEFPY_YANG( DEFPY_YANG( match_ipv6_address_prefix_list, match_ipv6_address_prefix_list_cmd, - "match ipv6 address prefix-list WORD$name", + "match ipv6 address prefix-list PREFIXLIST_NAME$name", MATCH_STR IPV6_STR "Match address of route\n" @@ -424,7 +417,7 @@ DEFPY_YANG( DEFPY_YANG( no_match_ipv6_address_prefix_list, no_match_ipv6_address_prefix_list_cmd, - "no match ipv6 address prefix-list [WORD]", + "no match ipv6 address prefix-list [PREFIXLIST_NAME]", NO_STR MATCH_STR IPV6_STR @@ -546,31 +539,29 @@ DEFPY_YANG( return nb_cli_apply_changes(vty, NULL); } -void route_map_condition_show(struct vty *vty, struct lyd_node *dnode, +void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { const char *condition = yang_dnode_get_string(dnode, "./condition"); - struct lyd_node *ln; + const struct lyd_node *ln; const char *acl; if (IS_MATCH_INTERFACE(condition)) { vty_out(vty, " match interface %s\n", yang_dnode_get_string( dnode, "./rmap-match-condition/interface")); - } else if (IS_MATCH_IPv4_ADDRESS_LIST(condition) - || IS_MATCH_IPv4_NEXTHOP_LIST(condition)) { - acl = NULL; - if ((ln = yang_dnode_get(dnode, - "./rmap-match-condition/list-name")) - != NULL) - acl = yang_dnode_get_string(ln, NULL); - - assert(acl); - - if (IS_MATCH_IPv4_ADDRESS_LIST(condition)) - vty_out(vty, " match ip address %s\n", acl); - else - vty_out(vty, " match ip next-hop %s\n", acl); + } else if (IS_MATCH_IPv4_ADDRESS_LIST(condition)) { + vty_out(vty, " match ip address %s\n", + yang_dnode_get_string( + dnode, "./rmap-match-condition/list-name")); + } else if (IS_MATCH_IPv4_NEXTHOP_LIST(condition)) { + vty_out(vty, " match ip next-hop %s\n", + yang_dnode_get_string( + dnode, "./rmap-match-condition/list-name")); + } else if (IS_MATCH_IPv6_NEXTHOP_LIST(condition)) { + vty_out(vty, " match ipv6 next-hop %s\n", + yang_dnode_get_string( + dnode, "./rmap-match-condition/list-name")); } else if (IS_MATCH_IPv4_PREFIX_LIST(condition)) { vty_out(vty, " match ip address prefix-list %s\n", yang_dnode_get_string( @@ -579,6 +570,10 @@ void route_map_condition_show(struct vty *vty, struct lyd_node *dnode, vty_out(vty, " match ip next-hop prefix-list %s\n", yang_dnode_get_string( dnode, "./rmap-match-condition/list-name")); + } else if (IS_MATCH_IPv6_NEXTHOP_PREFIX_LIST(condition)) { + vty_out(vty, " match ipv6 next-hop prefix-list %s\n", + yang_dnode_get_string( + dnode, "./rmap-match-condition/list-name")); } else if (IS_MATCH_IPv6_ADDRESS_LIST(condition)) { vty_out(vty, " match ipv6 address %s\n", yang_dnode_get_string( @@ -719,91 +714,47 @@ void route_map_condition_show(struct vty *vty, struct lyd_node *dnode, dnode, "./rmap-match-condition/frr-bgp-route-map:list-name")); } else if (IS_MATCH_ROUTE_SRC(condition)) { - acl = NULL; - if ((ln = yang_dnode_get( - dnode, - "./rmap-match-condition/frr-bgp-route-map:list-name")) - != NULL) - acl = yang_dnode_get_string(ln, NULL); - - assert(acl); - - vty_out(vty, " match ip route-source %s\n", acl); + vty_out(vty, " match ip route-source %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:list-name")); } else if (IS_MATCH_ROUTE_SRC_PL(condition)) { vty_out(vty, " match ip route-source prefix-list %s\n", yang_dnode_get_string( dnode, "./rmap-match-condition/frr-bgp-route-map:list-name")); - } else if (IS_MATCH_ROUTE_SRC(condition)) { - acl = NULL; - if ((ln = yang_dnode_get( - dnode, - "./rmap-match-condition/frr-bgp-route-map:list-name")) - != NULL) - acl = yang_dnode_get_string(ln, NULL); - - assert(acl); - - vty_out(vty, " match ip route-source %s\n", acl); } else if (IS_MATCH_COMMUNITY(condition)) { - acl = NULL; - if ((ln = yang_dnode_get( - dnode, - "./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name")) - != NULL) { - acl = yang_dnode_get_string(ln, NULL); - - if (true - == yang_dnode_get_bool( - dnode, - "./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match")) - vty_out(vty, - " match community %s exact-match\n", - acl); - else - vty_out(vty, " match community %s\n", acl); - } - - assert(acl); + vty_out(vty, " match community %s", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name")); + if (yang_dnode_get_bool( + dnode, + "./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match")) + vty_out(vty, " exact-match"); + vty_out(vty, "\n"); } else if (IS_MATCH_LCOMMUNITY(condition)) { - acl = NULL; - if ((ln = yang_dnode_get( - dnode, - "./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name")) - != NULL) { - acl = yang_dnode_get_string(ln, NULL); - - if (true - == yang_dnode_get_bool( - dnode, - "./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match")) - vty_out(vty, - " match large-community %s exact-match\n", - acl); - else - vty_out(vty, " match large-community %s\n", - acl); - } - - assert(acl); + vty_out(vty, " match large-community %s", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name")); + if (yang_dnode_get_bool( + dnode, + "./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match")) + vty_out(vty, " exact-match"); + vty_out(vty, "\n"); } else if (IS_MATCH_EXTCOMMUNITY(condition)) { - acl = NULL; - if ((ln = yang_dnode_get( - dnode, - "./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name")) - != NULL) - acl = yang_dnode_get_string(ln, NULL); - - assert(acl); - - vty_out(vty, " match extcommunity %s\n", acl); + vty_out(vty, " match extcommunity %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name")); } else if (IS_MATCH_IPV4_NH(condition)) { - vty_out(vty, " match ip next-hop %s\n", + vty_out(vty, " match ip next-hop address %s\n", yang_dnode_get_string( dnode, "./rmap-match-condition/frr-bgp-route-map:ipv4-address")); } else if (IS_MATCH_IPV6_NH(condition)) { - vty_out(vty, " match ipv6 next-hop %s\n", + vty_out(vty, " match ipv6 next-hop address %s\n", yang_dnode_get_string( dnode, "./rmap-match-condition/frr-bgp-route-map:ipv6-address")); @@ -1021,11 +972,11 @@ DEFUN_YANG (no_set_srte_color, } -void route_map_action_show(struct vty *vty, struct lyd_node *dnode, +void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { const char *action = yang_dnode_get_string(dnode, "./action"); - struct lyd_node *ln; + const struct lyd_node *ln; const char *acl; if (IS_SET_IPv4_NH(action)) { @@ -1363,7 +1314,7 @@ ALIAS_YANG( "Continue on a different entry within the route-map\n" "Route-map entry sequence number\n") -void route_map_exit_policy_show(struct vty *vty, struct lyd_node *dnode, +void route_map_exit_policy_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { int exit_policy = yang_dnode_get_enum(dnode, NULL); @@ -1405,7 +1356,7 @@ DEFPY_YANG( return nb_cli_apply_changes(vty, NULL); } -void route_map_call_show(struct vty *vty, struct lyd_node *dnode, +void route_map_call_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { vty_out(vty, " call %s\n", yang_dnode_get_string(dnode, NULL)); @@ -1439,7 +1390,7 @@ DEFUN_YANG (no_rmap_description, return nb_cli_apply_changes(vty, NULL); } -void route_map_description_show(struct vty *vty, struct lyd_node *dnode, +void route_map_description_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { vty_out(vty, " description %s\n", yang_dnode_get_string(dnode, NULL)); @@ -1447,7 +1398,7 @@ void route_map_description_show(struct vty *vty, struct lyd_node *dnode, DEFPY_YANG( route_map_optimization, route_map_optimization_cmd, - "[no] route-map WORD$name optimization", + "[no] route-map RMAP_NAME$name optimization", NO_STR ROUTE_MAP_CMD_STR "Configure route-map optimization\n") @@ -1468,7 +1419,7 @@ DEFPY_YANG( } void route_map_optimization_disabled_show(struct vty *vty, - struct lyd_node *dnode, + const struct lyd_node *dnode, bool show_defaults) { const char *name = yang_dnode_get_string(dnode, "../name"); @@ -1515,7 +1466,7 @@ DEFPY_HIDDEN( static int route_map_config_write(struct vty *vty) { - struct lyd_node *dnode; + const struct lyd_node *dnode; int written = 0; dnode = yang_dnode_get(running_config->dnode, diff --git a/lib/routemap_northbound.c b/lib/routemap_northbound.c index 3473ca2aea..51b879959f 100644 --- a/lib/routemap_northbound.c +++ b/lib/routemap_northbound.c @@ -601,6 +601,16 @@ static int lib_route_map_entry_match_condition_list_name_modify( rhc->rhc_rmi, "ip next-hop", acl, RMAP_EVENT_FILTER_ADDED, args->errmsg, args->errmsg_len); + } else if (IS_MATCH_IPv6_NEXTHOP_LIST(condition)) { + if (rmap_match_set_hook.match_ipv6_next_hop == NULL) + return NB_OK; + rhc->rhc_mhook = rmap_match_set_hook.no_match_ipv6_next_hop; + rhc->rhc_rule = "ipv6 next-hop"; + rhc->rhc_event = RMAP_EVENT_FILTER_DELETED; + rv = rmap_match_set_hook.match_ipv6_next_hop( + rhc->rhc_rmi, "ipv6 next-hop", acl, + RMAP_EVENT_FILTER_ADDED, args->errmsg, + args->errmsg_len); } else if (IS_MATCH_IPv4_NEXTHOP_PREFIX_LIST(condition)) { if (rmap_match_set_hook.match_ip_next_hop_prefix_list == NULL) return NB_OK; @@ -612,6 +622,16 @@ static int lib_route_map_entry_match_condition_list_name_modify( rhc->rhc_rmi, "ip next-hop prefix-list", acl, RMAP_EVENT_PLIST_ADDED, args->errmsg, args->errmsg_len); + } else if (IS_MATCH_IPv6_NEXTHOP_PREFIX_LIST(condition)) { + if (rmap_match_set_hook.match_ipv6_next_hop_prefix_list == NULL) + return NB_OK; + rhc->rhc_mhook = + rmap_match_set_hook.no_match_ipv6_next_hop_prefix_list; + rhc->rhc_rule = "ipv6 next-hop prefix-list"; + rhc->rhc_event = RMAP_EVENT_PLIST_DELETED; + rv = rmap_match_set_hook.match_ipv6_next_hop_prefix_list( + rhc->rhc_rmi, "ipv6 next-hop prefix-list", acl, + RMAP_EVENT_PLIST_ADDED, args->errmsg, args->errmsg_len); } else if (IS_MATCH_IPv6_ADDRESS_LIST(condition)) { if (rmap_match_set_hook.match_ipv6_address == NULL) return NB_OK; diff --git a/lib/sbuf.h b/lib/sbuf.h index 9f0311006d..aaa2db0edb 100644 --- a/lib/sbuf.h +++ b/lib/sbuf.h @@ -35,7 +35,7 @@ extern "C" { * without any information about the previous parsing steps, is usually not very * helpful. * Using sbuf, the parser can log the whole parsing process into a buffer using - * a printf like API. When an error ocurrs, all the information about previous + * a printf like API. When an error occurs, all the information about previous * parsing steps is there in the log, without any need for backtracking, and can * be used to give a detailed and useful error description. * When parsing completes successfully without any error, the log can just be diff --git a/lib/sigevent.c b/lib/sigevent.c index be7297f264..00bc31f517 100644 --- a/lib/sigevent.c +++ b/lib/sigevent.c @@ -36,10 +36,10 @@ /* master signals descriptor struct */ -static struct quagga_sigevent_master_t { +static struct frr_sigevent_master_t { struct thread *t; - struct quagga_signal_t *signals; + struct frr_signal_t *signals; int sigc; volatile sig_atomic_t caught; @@ -48,10 +48,10 @@ static struct quagga_sigevent_master_t { /* Generic signal handler * Schedules signal event thread */ -static void quagga_signal_handler(int signo) +static void frr_signal_handler(int signo) { int i; - struct quagga_signal_t *sig; + struct frr_signal_t *sig; for (i = 0; i < sigmaster.sigc; i++) { sig = &(sigmaster.signals[i]); @@ -91,12 +91,12 @@ bool frr_sigevent_check(sigset_t *setp) } /* check if signals have been caught and run appropriate handlers */ -int quagga_sigevent_process(void) +int frr_sigevent_process(void) { - struct quagga_signal_t *sig; + struct frr_signal_t *sig; int i; #ifdef SIGEVENT_BLOCK_SIGNALS - /* shouldnt need to block signals, but potentially may be needed */ + /* shouldn't need to block signals, but potentially may be needed */ sigset_t newmask, oldmask; /* @@ -110,7 +110,7 @@ int quagga_sigevent_process(void) if ((sigprocmask(SIG_BLOCK, &newmask, &oldmask)) < 0) { flog_err_sys(EC_LIB_SYSTEM_CALL, - "quagga_signal_timer: couldnt block signals!"); + "frr_signal_timer: couldnt block signals!"); return -1; } #endif /* SIGEVENT_BLOCK_SIGNALS */ @@ -142,16 +142,16 @@ int quagga_sigevent_process(void) } #ifdef SIGEVENT_SCHEDULE_THREAD -/* timer thread to check signals. Shouldnt be needed */ -int quagga_signal_timer(struct thread *t) +/* timer thread to check signals. shouldn't be needed */ +int frr_signal_timer(struct thread *t) { - struct quagga_sigevent_master_t *sigm; + struct frr_sigevent_master_t *sigm; sigm = THREAD_ARG(t); sigm->t = NULL; - thread_add_timer(sigm->t->master, quagga_signal_timer, &sigmaster, - QUAGGA_SIGNAL_TIMER_INTERVAL, &sigm->t); - return quagga_sigevent_process(); + thread_add_timer(sigm->t->master, frr_signal_timer, &sigmaster, + FRR_SIGNAL_TIMER_INTERVAL, &sigm->t); + return frr_sigevent_process(); } #endif /* SIGEVENT_SCHEDULE_THREAD */ @@ -163,7 +163,7 @@ static int signal_set(int signo) struct sigaction sig; struct sigaction osig; - sig.sa_handler = &quagga_signal_handler; + sig.sa_handler = &frr_signal_handler; sigfillset(&sig.sa_mask); sig.sa_flags = 0; if (signo == SIGALRM) { @@ -348,11 +348,11 @@ static void trap_default_signals(void) } void signal_init(struct thread_master *m, int sigc, - struct quagga_signal_t signals[]) + struct frr_signal_t signals[]) { int i = 0; - struct quagga_signal_t *sig; + struct frr_signal_t *sig; /* First establish some default handlers that can be overridden by the application. */ @@ -370,7 +370,7 @@ void signal_init(struct thread_master *m, int sigc, #ifdef SIGEVENT_SCHEDULE_THREAD sigmaster.t = NULL; - thread_add_timer(m, quagga_signal_timer, &sigmaster, - QUAGGA_SIGNAL_TIMER_INTERVAL, &sigmaster.t); + thread_add_timer(m, frr_signal_timer, &sigmaster, + FRR_SIGNAL_TIMER_INTERVAL, &sigmaster.t); #endif /* SIGEVENT_SCHEDULE_THREAD */ } diff --git a/lib/sigevent.h b/lib/sigevent.h index 4a39b22889..dd1ee99587 100644 --- a/lib/sigevent.h +++ b/lib/sigevent.h @@ -20,8 +20,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef _QUAGGA_SIGNAL_H -#define _QUAGGA_SIGNAL_H +#ifndef _FRR_SIGNAL_H +#define _FRR_SIGNAL_H #include <thread.h> @@ -29,9 +29,9 @@ extern "C" { #endif -#define QUAGGA_SIGNAL_TIMER_INTERVAL 2L +#define FRR_SIGNAL_TIMER_INTERVAL 2L -struct quagga_signal_t { +struct frr_signal_t { int signal; /* signal number */ void (*handler)(void); /* handler to call */ @@ -42,11 +42,11 @@ struct quagga_signal_t { * takes: * - pointer to valid struct thread_master * - number of elements in passed in signals array - * - array of quagga_signal_t's describing signals to handle + * - array of frr_signal_t's describing signals to handle * and handlers to use for each signal */ extern void signal_init(struct thread_master *m, int sigc, - struct quagga_signal_t *signals); + struct frr_signal_t *signals); /* @@ -58,10 +58,10 @@ extern void signal_init(struct thread_master *m, int sigc, bool frr_sigevent_check(sigset_t *setp); /* check whether there are signals to handle, process any found */ -extern int quagga_sigevent_process(void); +extern int frr_sigevent_process(void); #ifdef __cplusplus } #endif -#endif /* _QUAGGA_SIGNAL_H */ +#endif /* _FRR_SIGNAL_H */ diff --git a/lib/skiplist.c b/lib/skiplist.c index c5219f7381..81407826f2 100644 --- a/lib/skiplist.c +++ b/lib/skiplist.c @@ -21,7 +21,7 @@ */ /* - * Skip List impementation based on code from William Pugh. + * Skip List implementation based on code from William Pugh. * ftp://ftp.cs.umd.edu/pub/skipLists/ * * Skip Lists are a probabilistic alternative to balanced trees, as diff --git a/lib/skiplist.h b/lib/skiplist.h index 00950e13bb..165607820a 100644 --- a/lib/skiplist.h +++ b/lib/skiplist.h @@ -21,7 +21,7 @@ */ /* - * Skip List impementation based on code from William Pugh. + * Skip List implementation based on code from William Pugh. * ftp://ftp.cs.umd.edu/pub/skipLists/ */ diff --git a/lib/snmp.c b/lib/snmp.c index 23d3f38b31..8d8b3c950c 100644 --- a/lib/snmp.c +++ b/lib/snmp.c @@ -25,13 +25,11 @@ #include "smux.h" -#define min(A,B) ((A) < (B) ? (A) : (B)) - int oid_compare(const oid *o1, int o1_len, const oid *o2, int o2_len) { int i; - for (i = 0; i < min(o1_len, o2_len); i++) { + for (i = 0; i < MIN(o1_len, o2_len); i++) { if (o1[i] < o2[i]) return -1; else if (o1[i] > o2[i]) diff --git a/lib/sockopt.c b/lib/sockopt.c index 150736e00c..45f3c23330 100644 --- a/lib/sockopt.c +++ b/lib/sockopt.c @@ -122,7 +122,7 @@ int setsockopt_ipv6_pktinfo(int sock, int val) if (ret < 0) flog_err(EC_LIB_SOCKET, "can't setsockopt IPV6_PKTINFO : %s", safe_strerror(errno)); -#endif /* INIA_IPV6 */ +#endif /* IANA_IPV6 */ return ret; } @@ -595,7 +595,7 @@ int sockopt_tcp_signature_ext(int sock, union sockunion *su, uint16_t prefixlen, /* If this does not work, then all users of this sockopt will * need to - * differentiate between IPv4 and IPv6, and keep seperate + * differentiate between IPv4 and IPv6, and keep separate * sockets for * each. * diff --git a/lib/srv6.c b/lib/srv6.c index ccb94b2f76..6a658444c6 100644 --- a/lib/srv6.c +++ b/lib/srv6.c @@ -161,21 +161,59 @@ void srv6_locator_chunk_free(struct srv6_locator_chunk *chunk) json_object *srv6_locator_chunk_json(const struct srv6_locator_chunk *chunk) { - char str[256]; json_object *jo_root = NULL; jo_root = json_object_new_object(); - prefix2str(&chunk->prefix, str, sizeof(str)); - json_object_string_add(jo_root, "prefix", str); + json_object_string_addf(jo_root, "prefix", "%pFX", &chunk->prefix); json_object_string_add(jo_root, "proto", zebra_route_string(chunk->proto)); return jo_root; } +json_object * +srv6_locator_chunk_detailed_json(const struct srv6_locator_chunk *chunk) +{ + json_object *jo_root = NULL; + + jo_root = json_object_new_object(); + + /* set prefix */ + json_object_string_addf(jo_root, "prefix", "%pFX", &chunk->prefix); + + /* set block_bits_length */ + json_object_int_add(jo_root, "blockBitsLength", + chunk->block_bits_length); + + /* set node_bits_length */ + json_object_int_add(jo_root, "nodeBitsLength", chunk->node_bits_length); + + /* set function_bits_length */ + json_object_int_add(jo_root, "functionBitsLength", + chunk->function_bits_length); + + /* set argument_bits_length */ + json_object_int_add(jo_root, "argumentBitsLength", + chunk->argument_bits_length); + + /* set keep */ + json_object_int_add(jo_root, "keep", chunk->keep); + + /* set proto */ + json_object_string_add(jo_root, "proto", + zebra_route_string(chunk->proto)); + + /* set instance */ + json_object_int_add(jo_root, "instance", chunk->instance); + + /* set session_id */ + json_object_int_add(jo_root, "sessionId", chunk->session_id); + + return jo_root; +} + json_object *srv6_locator_json(const struct srv6_locator *loc) { - char str[256]; struct listnode *node; struct srv6_locator_chunk *chunk; json_object *jo_root = NULL; @@ -188,8 +226,7 @@ json_object *srv6_locator_json(const struct srv6_locator *loc) json_object_string_add(jo_root, "name", loc->name); /* set prefix */ - prefix2str(&loc->prefix, str, sizeof(str)); - json_object_string_add(jo_root, "prefix", str); + json_object_string_addf(jo_root, "prefix", "%pFX", &loc->prefix); /* set function_bits_length */ json_object_int_add(jo_root, "functionBitsLength", @@ -209,3 +246,50 @@ json_object *srv6_locator_json(const struct srv6_locator *loc) return jo_root; } + +json_object *srv6_locator_detailed_json(const struct srv6_locator *loc) +{ + struct listnode *node; + struct srv6_locator_chunk *chunk; + json_object *jo_root = NULL; + json_object *jo_chunk = NULL; + json_object *jo_chunks = NULL; + + jo_root = json_object_new_object(); + + /* set name */ + json_object_string_add(jo_root, "name", loc->name); + + /* 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 algonum */ + json_object_int_add(jo_root, "algoNum", loc->algonum); + + /* set status_up */ + json_object_boolean_add(jo_root, "statusUp", loc->status_up); + + /* set chunks */ + jo_chunks = json_object_new_array(); + json_object_object_add(jo_root, "chunks", jo_chunks); + for (ALL_LIST_ELEMENTS_RO((struct list *)loc->chunks, node, chunk)) { + jo_chunk = srv6_locator_chunk_detailed_json(chunk); + json_object_array_add(jo_chunks, jo_chunk); + } + + return jo_root; +} diff --git a/lib/srv6.h b/lib/srv6.h index 715fc3723b..e0db30cd13 100644 --- a/lib/srv6.h +++ b/lib/srv6.h @@ -189,6 +189,9 @@ extern void srv6_locator_free(struct srv6_locator *locator); extern void srv6_locator_chunk_free(struct srv6_locator_chunk *chunk); 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); #ifdef __cplusplus } diff --git a/lib/subdir.am b/lib/subdir.am index dab5fb9e83..bb10d71ed1 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -3,7 +3,7 @@ # lib_LTLIBRARIES += lib/libfrr.la lib_libfrr_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0 -Xlinker -e_libfrr_version -lib_libfrr_la_LIBADD = $(LIBCAP) $(UNWIND_LIBS) $(LIBYANG_LIBS) $(LUA_LIB) $(UST_LIBS) $(LIBM) +lib_libfrr_la_LIBADD = $(LIBCAP) $(UNWIND_LIBS) $(LIBYANG_LIBS) $(LUA_LIB) $(UST_LIBS) $(LIBCRYPT) $(LIBDL) $(LIBM) lib_libfrr_la_SOURCES = \ lib/agg_table.c \ @@ -130,7 +130,6 @@ nodist_lib_libfrr_la_SOURCES = \ yang/ietf/ietf-interfaces.yang.c \ yang/ietf/ietf-bgp-types.yang.c \ yang/frr-module-translator.yang.c \ - yang/frr-nexthop.yang.c \ # end vtysh_scan += \ diff --git a/lib/table.c b/lib/table.c index e6030ca4ca..523183bef2 100644 --- a/lib/table.c +++ b/lib/table.c @@ -393,7 +393,7 @@ void route_node_delete(struct route_node *node) route_node_delete(parent); } -/* Get fist node and lock it. This function is useful when one want +/* Get first node and lock it. This function is useful when one wants to lookup all the node exist in the routing table. */ struct route_node *route_top(struct route_table *table) { diff --git a/lib/thread.c b/lib/thread.c index 835aa38115..7b223ed6de 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -922,10 +922,10 @@ done: } /* Add new read thread. */ -struct thread *_thread_add_read_write(const struct xref_threadsched *xref, - struct thread_master *m, - int (*func)(struct thread *), - void *arg, int fd, struct thread **t_ptr) +void _thread_add_read_write(const struct xref_threadsched *xref, + struct thread_master *m, + int (*func)(struct thread *), void *arg, int fd, + struct thread **t_ptr) { int dir = xref->thread_type; struct thread *thread = NULL; @@ -1000,15 +1000,13 @@ struct thread *_thread_add_read_write(const struct xref_threadsched *xref, AWAKEN(m); } - - return thread; } -static struct thread * -_thread_add_timer_timeval(const struct xref_threadsched *xref, - struct thread_master *m, int (*func)(struct thread *), - void *arg, struct timeval *time_relative, - struct thread **t_ptr) +static void _thread_add_timer_timeval(const struct xref_threadsched *xref, + struct thread_master *m, + int (*func)(struct thread *), void *arg, + struct timeval *time_relative, + struct thread **t_ptr) { struct thread *thread; struct timeval t; @@ -1028,7 +1026,7 @@ _thread_add_timer_timeval(const struct xref_threadsched *xref, frr_with_mutex(&m->mtx) { if (t_ptr && *t_ptr) /* thread is already scheduled; don't reschedule */ - return NULL; + return; thread = thread_get(m, THREAD_TIMER, func, arg, xref); @@ -1048,16 +1046,13 @@ _thread_add_timer_timeval(const struct xref_threadsched *xref, if (thread_timer_list_first(&m->timer) == thread) AWAKEN(m); } - - return thread; } /* Add timer event thread. */ -struct thread *_thread_add_timer(const struct xref_threadsched *xref, - struct thread_master *m, - int (*func)(struct thread *), - void *arg, long timer, struct thread **t_ptr) +void _thread_add_timer(const struct xref_threadsched *xref, + struct thread_master *m, int (*func)(struct thread *), + void *arg, long timer, struct thread **t_ptr) { struct timeval trel; @@ -1066,15 +1061,14 @@ struct thread *_thread_add_timer(const struct xref_threadsched *xref, trel.tv_sec = timer; trel.tv_usec = 0; - return _thread_add_timer_timeval(xref, m, func, arg, &trel, t_ptr); + _thread_add_timer_timeval(xref, m, func, arg, &trel, t_ptr); } /* Add timer event thread with "millisecond" resolution */ -struct thread *_thread_add_timer_msec(const struct xref_threadsched *xref, - struct thread_master *m, - int (*func)(struct thread *), - void *arg, long timer, - struct thread **t_ptr) +void _thread_add_timer_msec(const struct xref_threadsched *xref, + struct thread_master *m, + int (*func)(struct thread *), void *arg, long timer, + struct thread **t_ptr) { struct timeval trel; @@ -1083,24 +1077,21 @@ struct thread *_thread_add_timer_msec(const struct xref_threadsched *xref, trel.tv_sec = timer / 1000; trel.tv_usec = 1000 * (timer % 1000); - return _thread_add_timer_timeval(xref, m, func, arg, &trel, t_ptr); + _thread_add_timer_timeval(xref, m, func, arg, &trel, t_ptr); } /* Add timer event thread with "timeval" resolution */ -struct thread *_thread_add_timer_tv(const struct xref_threadsched *xref, - struct thread_master *m, - int (*func)(struct thread *), - void *arg, struct timeval *tv, - struct thread **t_ptr) +void _thread_add_timer_tv(const struct xref_threadsched *xref, + struct thread_master *m, int (*func)(struct thread *), + void *arg, struct timeval *tv, struct thread **t_ptr) { - return _thread_add_timer_timeval(xref, m, func, arg, tv, t_ptr); + _thread_add_timer_timeval(xref, m, func, arg, tv, t_ptr); } /* Add simple event thread. */ -struct thread *_thread_add_event(const struct xref_threadsched *xref, - struct thread_master *m, - int (*func)(struct thread *), - void *arg, int val, struct thread **t_ptr) +void _thread_add_event(const struct xref_threadsched *xref, + struct thread_master *m, int (*func)(struct thread *), + void *arg, int val, struct thread **t_ptr) { struct thread *thread = NULL; @@ -1128,8 +1119,6 @@ struct thread *_thread_add_event(const struct xref_threadsched *xref, AWAKEN(m); } - - return thread; } /* Thread cancellation ------------------------------------------------------ */ @@ -1706,7 +1695,7 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) do { /* Handle signals if any */ if (m->handle_signals) - quagga_sigevent_process(); + frr_sigevent_process(); pthread_mutex_lock(&m->mtx); @@ -2059,3 +2048,11 @@ void debug_signals(const sigset_t *sigs) zlog_debug("%s: %s", __func__, buf); } + +bool thread_is_scheduled(struct thread *thread) +{ + if (thread == NULL) + return false; + + return true; +} diff --git a/lib/thread.h b/lib/thread.h index abd94ff4f0..39f21da11d 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -219,26 +219,30 @@ void thread_master_set_name(struct thread_master *master, const char *name); extern void thread_master_free(struct thread_master *); extern void thread_master_free_unused(struct thread_master *); -extern struct thread *_thread_add_read_write( - const struct xref_threadsched *xref, struct thread_master *master, - int (*fn)(struct thread *), void *arg, int fd, struct thread **tref); - -extern struct thread *_thread_add_timer( - const struct xref_threadsched *xref, struct thread_master *master, - int (*fn)(struct thread *), void *arg, long t, struct thread **tref); - -extern struct thread *_thread_add_timer_msec( - const struct xref_threadsched *xref, struct thread_master *master, - int (*fn)(struct thread *), void *arg, long t, struct thread **tref); - -extern struct thread *_thread_add_timer_tv( - const struct xref_threadsched *xref, struct thread_master *master, - int (*fn)(struct thread *), void *arg, struct timeval *tv, - struct thread **tref); - -extern struct thread *_thread_add_event( - const struct xref_threadsched *xref, struct thread_master *master, - int (*fn)(struct thread *), void *arg, int val, struct thread **tref); +extern void _thread_add_read_write(const struct xref_threadsched *xref, + struct thread_master *master, + int (*fn)(struct thread *), void *arg, + int fd, struct thread **tref); + +extern void _thread_add_timer(const struct xref_threadsched *xref, + struct thread_master *master, + int (*fn)(struct thread *), void *arg, long t, + struct thread **tref); + +extern void _thread_add_timer_msec(const struct xref_threadsched *xref, + struct thread_master *master, + int (*fn)(struct thread *), void *arg, + long t, struct thread **tref); + +extern void _thread_add_timer_tv(const struct xref_threadsched *xref, + struct thread_master *master, + int (*fn)(struct thread *), void *arg, + struct timeval *tv, struct thread **tref); + +extern void _thread_add_event(const struct xref_threadsched *xref, + struct thread_master *master, + int (*fn)(struct thread *), void *arg, int val, + struct thread **tref); extern void _thread_execute(const struct xref_threadsched *xref, struct thread_master *master, @@ -273,6 +277,7 @@ extern pthread_key_t thread_current; extern char *thread_timer_to_hhmmss(char *buf, int buf_size, struct thread *t_timer); +extern bool thread_is_scheduled(struct thread *thread); /* Debug signal mask */ void debug_signals(const sigset_t *sigs); diff --git a/lib/typerb.c b/lib/typerb.c index 092faa4cc9..e1346df191 100644 --- a/lib/typerb.c +++ b/lib/typerb.c @@ -45,6 +45,7 @@ #include "config.h" #endif +#include <string.h> #include "typerb.h" #define RB_BLACK 0 @@ -330,6 +331,7 @@ color: rbe_remove_color(rbt, parent, child); rbt->count--; + memset(old, 0, sizeof(*old)); return (old); } @@ -478,3 +480,11 @@ struct rb_entry *typed_rb_min(const struct rbt_tree *rbt) return parent; } + +bool typed_rb_member(const struct typed_rb_root *rbt, + const struct typed_rb_entry *rbe) +{ + while (rbe->rbt_parent) + rbe = rbe->rbt_parent; + return rbe == rbt->rbt_root; +} diff --git a/lib/typerb.h b/lib/typerb.h index cbed8d4893..75a1de77b3 100644 --- a/lib/typerb.h +++ b/lib/typerb.h @@ -20,6 +20,7 @@ #ifndef _FRR_TYPERB_H #define _FRR_TYPERB_H +#include <string.h> #include "typesafe.h" #ifdef __cplusplus @@ -62,6 +63,8 @@ const struct typed_rb_entry *typed_rb_find_lt(const struct typed_rb_root *rbt, const struct typed_rb_entry *b)); struct typed_rb_entry *typed_rb_min(const struct typed_rb_root *rbt); struct typed_rb_entry *typed_rb_next(const struct typed_rb_entry *rbe); +bool typed_rb_member(const struct typed_rb_root *rbt, + const struct typed_rb_entry *rbe); #define _PREDECL_RBTREE(prefix) \ struct prefix ## _head { struct typed_rb_root rr; }; \ @@ -142,6 +145,11 @@ macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->rr.count; \ } \ +macro_pure bool prefix ## _member(const struct prefix##_head *h, \ + const type *item) \ +{ \ + return typed_rb_member(&h->rr, &item->field.re); \ +} \ MACRO_REQUIRE_SEMICOLON() /* end */ #define PREDECL_RBTREE_UNIQ(prefix) \ diff --git a/lib/typesafe.c b/lib/typesafe.c index 76705fad0d..3b65a2d02a 100644 --- a/lib/typesafe.c +++ b/lib/typesafe.c @@ -20,6 +20,7 @@ #include <stdlib.h> #include <string.h> +#include <assert.h> #include "typesafe.h" #include "memory.h" @@ -29,6 +30,46 @@ DEFINE_MTYPE_STATIC(LIB, TYPEDHASH_BUCKET, "Typed-hash bucket"); DEFINE_MTYPE_STATIC(LIB, SKIPLIST_OFLOW, "Skiplist overflow"); DEFINE_MTYPE_STATIC(LIB, HEAP_ARRAY, "Typed-heap array"); +struct slist_item typesafe_slist_sentinel = { NULL }; + +bool typesafe_list_member(const struct slist_head *head, + const struct slist_item *item) +{ + struct slist_item *fromhead = head->first; + struct slist_item **fromnext = (struct slist_item **)&item->next; + + while (fromhead != _SLIST_LAST) { + if (fromhead == item || fromnext == head->last_next) + return true; + + fromhead = fromhead->next; + if (!*fromnext || *fromnext == _SLIST_LAST) + break; + fromnext = &(*fromnext)->next; + } + + return false; +} + +bool typesafe_dlist_member(const struct dlist_head *head, + const struct dlist_item *item) +{ + const struct dlist_item *fromhead = head->hitem.next; + const struct dlist_item *fromitem = item->next; + + if (!item->prev || !item->next) + return false; + + while (fromhead != &head->hitem && fromitem != item) { + if (fromitem == &head->hitem || fromhead == item) + return true; + fromhead = fromhead->next; + fromitem = fromitem->next; + } + + return false; +} + #if 0 static void hash_consistency_check(struct thash_head *head) { diff --git a/lib/typesafe.h b/lib/typesafe.h index ecac1a4381..b284397d98 100644 --- a/lib/typesafe.h +++ b/lib/typesafe.h @@ -20,7 +20,6 @@ #include <stddef.h> #include <stdint.h> #include <stdbool.h> -#include <assert.h> #include "compiler.h" #ifdef __cplusplus @@ -78,8 +77,34 @@ macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ } \ /* ... */ +/* *_member via find - when there is no better membership check than find() */ +#define TYPESAFE_MEMBER_VIA_FIND(prefix, type) \ +macro_inline bool prefix ## _member(struct prefix##_head *h, \ + const type *item) \ +{ \ + return item == prefix ## _const_find(h, item); \ +} \ +/* ... */ + +/* *_member via find_gteq - same for non-unique containers */ +#define TYPESAFE_MEMBER_VIA_FIND_GTEQ(prefix, type, cmpfn) \ +macro_inline bool prefix ## _member(struct prefix##_head *h, \ + const type *item) \ +{ \ + const type *iter; \ + for (iter = prefix ## _const_find_gteq(h, item); iter; \ + iter = prefix ## _const_next(h, iter)) { \ + if (iter == item) \ + return true; \ + if (cmpfn(iter, item) > 0) \ + break; \ + } \ + return false; \ +} \ +/* ... */ + /* SWAP_ALL_SIMPLE = for containers where the items don't point back to the - * head *AND* the head doesn'T points to itself (= everything except LIST, + * head *AND* the head doesn't point to itself (= everything except LIST, * DLIST and SKIPLIST), just switch out the entire head */ #define TYPESAFE_SWAP_ALL_SIMPLE(prefix) \ @@ -106,6 +131,10 @@ struct slist_head { size_t count; }; +/* this replaces NULL as the value for ->next on the last item. */ +extern struct slist_item typesafe_slist_sentinel; +#define _SLIST_LAST &typesafe_slist_sentinel + static inline void typesafe_list_add(struct slist_head *head, struct slist_item **pos, struct slist_item *item) { @@ -116,6 +145,9 @@ static inline void typesafe_list_add(struct slist_head *head, head->count++; } +extern bool typesafe_list_member(const struct slist_head *head, + const struct slist_item *item); + /* use as: * * PREDECL_LIST(namelist); @@ -136,6 +168,7 @@ MACRO_REQUIRE_SEMICOLON() /* end */ macro_inline void prefix ## _init(struct prefix##_head *h) \ { \ memset(h, 0, sizeof(*h)); \ + h->sh.first = _SLIST_LAST; \ h->sh.last_next = &h->sh.first; \ } \ macro_inline void prefix ## _fini(struct prefix##_head *h) \ @@ -161,25 +194,27 @@ macro_inline void prefix ## _add_after(struct prefix##_head *h, \ macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ struct slist_item **iter = &h->sh.first; \ - while (*iter && *iter != &item->field.si) \ + while (*iter != _SLIST_LAST && *iter != &item->field.si) \ iter = &(*iter)->next; \ - if (!*iter) \ + if (*iter == _SLIST_LAST) \ return NULL; \ h->sh.count--; \ *iter = item->field.si.next; \ - if (!item->field.si.next) \ + if (item->field.si.next == _SLIST_LAST) \ h->sh.last_next = iter; \ + item->field.si.next = NULL; \ return item; \ } \ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ { \ struct slist_item *sitem = h->sh.first; \ - if (!sitem) \ + if (sitem == _SLIST_LAST) \ return NULL; \ h->sh.count--; \ h->sh.first = sitem->next; \ - if (h->sh.first == NULL) \ + if (h->sh.first == _SLIST_LAST) \ h->sh.last_next = &h->sh.first; \ + sitem->next = NULL; \ return container_of(sitem, type, field.si); \ } \ macro_inline void prefix ## _swap_all(struct prefix##_head *a, \ @@ -195,13 +230,17 @@ macro_inline void prefix ## _swap_all(struct prefix##_head *a, \ } \ macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \ { \ - return container_of_null(h->sh.first, type, field.si); \ + if (h->sh.first != _SLIST_LAST) \ + return container_of(h->sh.first, type, field.si); \ + return NULL; \ } \ macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \ const type *item) \ { \ const struct slist_item *sitem = &item->field.si; \ - return container_of_null(sitem->next, type, field.si); \ + if (sitem->next != _SLIST_LAST) \ + return container_of(sitem->next, type, field.si); \ + return NULL; \ } \ TYPESAFE_FIRST_NEXT(prefix, type) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ @@ -210,12 +249,23 @@ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ if (!item) \ return NULL; \ sitem = &item->field.si; \ - return container_of_null(sitem->next, type, field.si); \ + if (sitem->next != _SLIST_LAST) \ + return container_of(sitem->next, type, field.si); \ + return NULL; \ } \ macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->sh.count; \ } \ +macro_pure bool prefix ## _anywhere(const type *item) \ +{ \ + return item->field.si.next != NULL; \ +} \ +macro_pure bool prefix ## _member(const struct prefix##_head *h, \ + const type *item) \ +{ \ + return typesafe_list_member(&h->sh, &item->field.si); \ +} \ MACRO_REQUIRE_SEMICOLON() /* end */ /* don't use these structs directly */ @@ -267,6 +317,9 @@ static inline void typesafe_dlist_swap_all(struct dlist_head *a, } } +extern bool typesafe_dlist_member(const struct dlist_head *head, + const struct dlist_item *item); + /* double-linked list, for fast item deletion */ #define PREDECL_DLIST(prefix) \ @@ -321,6 +374,7 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ ditem->prev->next = ditem->next; \ ditem->next->prev = ditem->prev; \ h->dh.count--; \ + ditem->prev = ditem->next = NULL; \ return container_of(ditem, type, field.di); \ } \ macro_inline void prefix ## _swap_all(struct prefix##_head *a, \ @@ -354,6 +408,16 @@ macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->dh.count; \ } \ +macro_pure bool prefix ## _anywhere(const type *item) \ +{ \ + const struct dlist_item *ditem = &item->field.di; \ + return ditem->next && ditem->prev; \ +} \ +macro_pure bool prefix ## _member(const struct prefix##_head *h, \ + const type *item) \ +{ \ + return typesafe_dlist_member(&h->dh, &item->field.di); \ +} \ MACRO_REQUIRE_SEMICOLON() /* end */ /* note: heap currently caps out at 4G items */ @@ -463,6 +527,14 @@ macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->hh.count; \ } \ +macro_pure bool prefix ## _member(const struct prefix##_head *h, \ + const type *item) \ +{ \ + uint32_t idx = item->field.hi.index; \ + if (idx >= h->hh.count) \ + return false; \ + return h->hh.array[idx] == &item->field.hi; \ +} \ MACRO_REQUIRE_SEMICOLON() /* end */ extern void typesafe_heap_resize(struct heap_head *head, bool grow); @@ -565,6 +637,7 @@ macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ return NULL; \ h->sh.count--; \ *iter = item->field.si.next; \ + item->field.si.next = NULL; \ return item; \ } \ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ @@ -618,6 +691,7 @@ macro_inline const type *prefix ## _const_find(const struct prefix##_head *h, \ return container_of(sitem, type, field.si); \ } \ TYPESAFE_FIND(prefix, type) \ +TYPESAFE_MEMBER_VIA_FIND(prefix, type) \ MACRO_REQUIRE_SEMICOLON() /* end */ #define DECLARE_SORTLIST_NONUNIQ(prefix, type, field, cmpfn) \ @@ -633,6 +707,7 @@ macro_inline int _ ## prefix ## _cmp(const type *a, const type *b) \ return 0; \ } \ _DECLARE_SORTLIST(prefix, type, field, cmpfn, _ ## prefix ## _cmp); \ +TYPESAFE_MEMBER_VIA_FIND_GTEQ(prefix, type, cmpfn) \ MACRO_REQUIRE_SEMICOLON() /* end */ @@ -799,6 +874,19 @@ macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->hh.count; \ } \ +macro_pure bool prefix ## _member(const struct prefix##_head *h, \ + const type *item) \ +{ \ + uint32_t hval = item->field.hi.hashval, hbits = HASH_KEY(h->hh, hval); \ + const struct thash_item *hitem = h->hh.entries[hbits]; \ + while (hitem && hitem->hashval < hval) \ + hitem = hitem->next; \ + for (hitem = h->hh.entries[hbits]; hitem && hitem->hashval <= hval; \ + hitem = hitem->next) \ + if (hitem == &item->field.hi) \ + return true; \ + return false; \ +} \ MACRO_REQUIRE_SEMICOLON() /* end */ /* skiplist, sorted. @@ -937,6 +1025,7 @@ macro_inline const type *prefix ## _const_find(const struct prefix##_head *h, \ return container_of_null(sitem, type, field.si); \ } \ TYPESAFE_FIND(prefix, type) \ +TYPESAFE_MEMBER_VIA_FIND(prefix, type) \ \ _DECLARE_SKIPLIST(prefix, type, field, \ prefix ## __cmp, prefix ## __cmp); \ @@ -968,6 +1057,7 @@ macro_inline int prefix ## __cmp_uq(const struct sskip_item *a, \ \ _DECLARE_SKIPLIST(prefix, type, field, \ prefix ## __cmp, prefix ## __cmp_uq); \ +TYPESAFE_MEMBER_VIA_FIND_GTEQ(prefix, type, cmpfn) \ MACRO_REQUIRE_SEMICOLON() /* end */ diff --git a/lib/vector.c b/lib/vector.c index 4af564a82f..5497c24280 100644 --- a/lib/vector.c +++ b/lib/vector.c @@ -37,6 +37,7 @@ vector vector_init(unsigned int size) v->alloced = size; v->active = 0; + v->count = 0; v->index = XCALLOC(MTYPE_VECTOR_INDEX, sizeof(void *) * size); return v; } @@ -54,6 +55,7 @@ vector vector_copy(vector v) new->active = v->active; new->alloced = v->alloced; + new->count = v->count; size = sizeof(void *) * (v->alloced); new->index = XCALLOC(MTYPE_VECTOR_INDEX, size); @@ -84,6 +86,9 @@ int vector_empty_slot(vector v) { unsigned int i; + if (v->active == v->count) + return v->active; + if (v->active == 0) return 0; @@ -102,6 +107,10 @@ int vector_set(vector v, void *val) i = vector_empty_slot(v); vector_ensure(v, i); + if (v->index[i]) + v->count--; + if (val) + v->count++; v->index[i] = val; if (v->active <= i) @@ -115,6 +124,10 @@ int vector_set_index(vector v, unsigned int i, void *val) { vector_ensure(v, i); + if (v->index[i]) + v->count--; + if (val) + v->count++; v->index[i] = val; if (v->active <= i) @@ -123,17 +136,6 @@ int vector_set_index(vector v, unsigned int i, void *val) return i; } -/* Make a specified index slot active and return its address. */ -void **vector_get_index(vector v, unsigned int i) -{ - vector_ensure(v, i); - - if (v->active <= i) - v->active = i + 1; - - return &v->index[i]; -} - /* Look up vector. */ void *vector_lookup(vector v, unsigned int i) { @@ -155,6 +157,9 @@ void vector_unset(vector v, unsigned int i) if (i >= v->alloced) return; + if (v->index[i]) + v->count--; + v->index[i] = NULL; if (i + 1 == v->active) { @@ -169,6 +174,9 @@ void vector_remove(vector v, unsigned int ix) if (ix >= v->active) return; + if (v->index[ix]) + v->count--; + int n = (--v->active) - ix; memmove(&v->index[ix], &v->index[ix + 1], n * sizeof(void *)); @@ -192,6 +200,7 @@ void vector_unset_value(vector v, void *val) for (i = 0; i < v->active; i++) if (v->index[i] == val) { v->index[i] = NULL; + v->count--; break; } @@ -201,19 +210,6 @@ void vector_unset_value(vector v, void *val) while (i && v->index[--i] == NULL); } -/* Count the number of not emplty slot. */ -unsigned int vector_count(vector v) -{ - unsigned int i; - unsigned count = 0; - - for (i = 0; i < v->active; i++) - if (v->index[i] != NULL) - count++; - - return count; -} - void vector_to_array(vector v, void ***dest, int *argc) { *dest = XCALLOC(MTYPE_TMP, sizeof(void *) * v->active); diff --git a/lib/vector.h b/lib/vector.h index 845c8d8b04..71c497a1b7 100644 --- a/lib/vector.h +++ b/lib/vector.h @@ -32,6 +32,7 @@ extern "C" { struct _vector { unsigned int active; /* number of active slots */ unsigned int alloced; /* number of allocated slot */ + unsigned int count; void **index; /* index to data */ }; typedef struct _vector *vector; @@ -54,13 +55,16 @@ extern void vector_ensure(vector v, unsigned int num); extern int vector_empty_slot(vector v); extern int vector_set(vector v, void *val); extern int vector_set_index(vector v, unsigned int i, void *val); -extern void **vector_get_index(vector v, unsigned int i); extern void vector_unset(vector v, unsigned int i); extern void vector_unset_value(vector v, void *val); extern void vector_remove(vector v, unsigned int ix); extern void vector_compact(vector v); -extern unsigned int vector_count(vector v); +static inline unsigned int vector_count(vector v) +{ + return v->count; +} + extern void vector_free(vector v); extern vector vector_copy(vector v); @@ -21,9 +21,6 @@ #include <zebra.h> -/* for basename */ -#include <libgen.h> - #include "if.h" #include "vrf.h" #include "vrf_int.h" @@ -97,28 +94,6 @@ static int vrf_name_compare(const struct vrf *a, const struct vrf *b) return strcmp(a->name, b->name); } -/* if ns_id is different and not VRF_UNKNOWN, - * then update vrf identifier, and enable VRF - */ -static void vrf_update_vrf_id(ns_id_t ns_id, void *opaqueptr) -{ - ns_id_t vrf_id = (vrf_id_t)ns_id; - vrf_id_t old_vrf_id; - struct vrf *vrf = (struct vrf *)opaqueptr; - - if (!vrf) - return; - old_vrf_id = vrf->vrf_id; - if (vrf_id == vrf->vrf_id) - return; - if (vrf->vrf_id != VRF_UNKNOWN) - RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf); - vrf->vrf_id = vrf_id; - RB_INSERT(vrf_id_head, &vrfs_by_id, vrf); - if (old_vrf_id == VRF_UNKNOWN) - vrf_enable(vrf); -} - int vrf_switch_to_netns(vrf_id_t vrf_id) { char *name; @@ -272,32 +247,27 @@ void vrf_delete(struct vrf *vrf) if (vrf_is_enabled(vrf)) vrf_disable(vrf); + if (vrf->vrf_id != VRF_UNKNOWN) { + RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf); + vrf->vrf_id = VRF_UNKNOWN; + } + /* If the VRF is user configured, it'll stick around, just remove * the ID mapping. Interfaces assigned to this VRF should've been * removed already as part of the VRF going down. */ - if (vrf_is_user_cfged(vrf)) { - if (vrf->vrf_id != VRF_UNKNOWN) { - /* Delete any VRF interfaces - should be only - * the VRF itself, other interfaces should've - * been moved out of the VRF. - */ - if_terminate(vrf); - RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf); - vrf->vrf_id = VRF_UNKNOWN; - } - vrf->ns_ctxt = NULL; + if (vrf_is_user_cfged(vrf)) + return; + + /* Do not delete the VRF if it has interfaces configured in it. */ + if (!RB_EMPTY(if_name_head, &vrf->ifaces_by_name)) return; - } if (vrf_master.vrf_delete_hook) (*vrf_master.vrf_delete_hook)(vrf); QOBJ_UNREG(vrf); - if_terminate(vrf); - if (vrf->vrf_id != VRF_UNKNOWN) - RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf); if (vrf->name[0] != '\0') RB_REMOVE(vrf_name_head, &vrfs_by_name, vrf); @@ -384,13 +354,6 @@ const char *vrf_id_to_name(vrf_id_t vrf_id) return VRF_LOGNAME(vrf); } -/* Get the data pointer of the specified VRF. If not found, create one. */ -void *vrf_info_get(vrf_id_t vrf_id) -{ - struct vrf *vrf = vrf_get(vrf_id, NULL); - return vrf->info; -} - /* Look up the data pointer of the specified VRF. */ void *vrf_info_lookup(vrf_id_t vrf_id) { @@ -571,6 +534,7 @@ static void vrf_terminate_single(struct vrf *vrf) { /* Clear configured flag and invoke delete. */ UNSET_FLAG(vrf->status, VRF_CONFIGURED); + if_terminate(vrf); vrf_delete(vrf); } @@ -598,7 +562,8 @@ void vrf_terminate(void) /* Finally terminate default VRF */ vrf = vrf_lookup_by_id(VRF_DEFAULT); - vrf_terminate_single(vrf); + if (vrf) + vrf_terminate_single(vrf); } int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id, @@ -659,115 +624,6 @@ int vrf_configure_backend(enum vrf_backend_type backend) return 0; } -int vrf_handler_create(struct vty *vty, const char *vrfname, - struct vrf **vrf) -{ - struct vrf *vrfp; - char xpath_list[XPATH_MAXLEN]; - int ret; - - if (strlen(vrfname) > VRF_NAMSIZ) { - if (vty) - vty_out(vty, - "%% VRF name %s invalid: length exceeds %d bytes\n", - vrfname, VRF_NAMSIZ); - else - flog_warn( - EC_LIB_VRF_LENGTH, - "%% VRF name %s invalid: length exceeds %d bytes", - vrfname, VRF_NAMSIZ); - return CMD_WARNING_CONFIG_FAILED; - } - - if (vty) { - snprintf(xpath_list, sizeof(xpath_list), FRR_VRF_KEY_XPATH, - vrfname); - - nb_cli_enqueue_change(vty, xpath_list, NB_OP_CREATE, NULL); - ret = nb_cli_apply_changes_clear_pending(vty, xpath_list); - if (ret == CMD_SUCCESS) { - VTY_PUSH_XPATH(VRF_NODE, xpath_list); - vrfp = vrf_lookup_by_name(vrfname); - if (vrfp) - VTY_PUSH_CONTEXT(VRF_NODE, vrfp); - } - } else { - vrfp = vrf_get(VRF_UNKNOWN, vrfname); - - if (vrf) - *vrf = vrfp; - } - return CMD_SUCCESS; -} - -int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, - ns_id_t ns_id, ns_id_t internal_ns_id, - ns_id_t rel_def_ns_id) -{ - struct ns *ns = NULL; - - if (!vrf) - return CMD_WARNING_CONFIG_FAILED; - if (vrf->vrf_id != VRF_UNKNOWN && vrf->ns_ctxt == NULL) { - if (vty) - vty_out(vty, - "VRF %u is already configured with VRF %s\n", - vrf->vrf_id, vrf->name); - else - zlog_info("VRF %u is already configured with VRF %s", - vrf->vrf_id, vrf->name); - return CMD_WARNING_CONFIG_FAILED; - } - if (vrf->ns_ctxt != NULL) { - ns = (struct ns *)vrf->ns_ctxt; - if (!strcmp(ns->name, pathname)) { - if (vty) - vty_out(vty, - "VRF %u already configured with NETNS %s\n", - vrf->vrf_id, ns->name); - else - zlog_info( - "VRF %u already configured with NETNS %s", - vrf->vrf_id, ns->name); - return CMD_WARNING_CONFIG_FAILED; - } - } - ns = ns_lookup_name(pathname); - if (ns && ns->vrf_ctxt) { - struct vrf *vrf2 = (struct vrf *)ns->vrf_ctxt; - - if (vrf2 == vrf) - return CMD_SUCCESS; - if (vty) - vty_out(vty, - "NS %s is already configured with VRF %u(%s)\n", - ns->name, vrf2->vrf_id, vrf2->name); - else - zlog_info("NS %s is already configured with VRF %u(%s)", - ns->name, vrf2->vrf_id, vrf2->name); - return CMD_WARNING_CONFIG_FAILED; - } - ns = ns_get_created(ns, pathname, ns_id); - ns->internal_ns_id = internal_ns_id; - ns->relative_default_ns = rel_def_ns_id; - ns->vrf_ctxt = (void *)vrf; - vrf->ns_ctxt = (void *)ns; - /* update VRF netns NAME */ - strlcpy(vrf->data.l.netns_name, basename(pathname), NS_NAMSIZ); - - if (!ns_enable(ns, vrf_update_vrf_id)) { - if (vty) - vty_out(vty, "Can not associate NS %u with NETNS %s\n", - ns->ns_id, ns->name); - else - zlog_info("Can not associate NS %u with NETNS %s", - ns->ns_id, ns->name); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - /* vrf CLI commands */ DEFUN_NOSH(vrf_exit, vrf_exit_cmd, @@ -786,8 +642,29 @@ DEFUN_YANG_NOSH (vrf, { int idx_name = 1; const char *vrfname = argv[idx_name]->arg; + char xpath_list[XPATH_MAXLEN]; + struct vrf *vrf; + int ret; - return vrf_handler_create(vty, vrfname, NULL); + if (strlen(vrfname) > VRF_NAMSIZ) { + vty_out(vty, + "%% VRF name %s invalid: length exceeds %d bytes\n", + vrfname, VRF_NAMSIZ); + return CMD_WARNING_CONFIG_FAILED; + } + + snprintf(xpath_list, sizeof(xpath_list), FRR_VRF_KEY_XPATH, vrfname); + + nb_cli_enqueue_change(vty, xpath_list, NB_OP_CREATE, NULL); + ret = nb_cli_apply_changes_clear_pending(vty, xpath_list); + if (ret == CMD_SUCCESS) { + VTY_PUSH_XPATH(VRF_NODE, xpath_list); + vrf = vrf_lookup_by_name(vrfname); + if (vrf) + VTY_PUSH_CONTEXT(VRF_NODE, vrf); + } + + return ret; } DEFUN_YANG (no_vrf, @@ -814,15 +691,10 @@ DEFUN_YANG (no_vrf, if (vrf_get_backend() == VRF_BACKEND_VRF_LITE) { /* - * Remove the VRF interface config. Currently, we allow to - * remove only inactive VRFs, so we use VRF_DEFAULT_NAME here, - * because when the VRF is removed from kernel, the interface - * is moved to the default VRF. If we ever allow removing - * active VRFs, this code have to be updated accordingly. + * Remove the VRF interface config when removing the VRF. */ snprintf(xpath_list, sizeof(xpath_list), - "/frr-interface:lib/interface[name='%s'][vrf='%s']", - vrfname, VRF_DEFAULT_NAME); + "/frr-interface:lib/interface[name='%s']", vrfname); nb_cli_enqueue_change(vty, xpath_list, NB_OP_DESTROY, NULL); } @@ -176,8 +176,6 @@ static inline uint32_t vrf_interface_count(struct vrf *vrf) * Utilities to obtain the user data */ -/* Get the data pointer of the specified VRF. If not found, create one. */ -extern void *vrf_info_get(vrf_id_t); /* Look up the data pointer of the specified VRF. */ extern void *vrf_info_lookup(vrf_id_t); @@ -301,21 +299,6 @@ extern int vrf_configure_backend(enum vrf_backend_type backend); extern int vrf_get_backend(void); extern int vrf_is_backend_netns(void); - -/* API to create a VRF. either from vty - * or through discovery - */ -extern int vrf_handler_create(struct vty *vty, const char *name, - struct vrf **vrf); - -/* API to associate a VRF with a NETNS. - * called either from vty or through discovery - * should be called from zebra only - */ -extern int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, - char *pathname, ns_id_t ext_ns_id, - ns_id_t ns_id, ns_id_t rel_def_ns_id); - /* used internally to enable or disable VRF. * Notify a change in the VRF ID of the VRF */ @@ -48,6 +48,7 @@ #include "lib_errors.h" #include "northbound_cli.h" #include "printfrr.h" +#include "json.h" #include <arpa/telnet.h> #include <termios.h> @@ -57,9 +58,12 @@ #endif DEFINE_MTYPE_STATIC(LIB, VTY, "VTY"); +DEFINE_MTYPE_STATIC(LIB, VTY_SERV, "VTY server"); DEFINE_MTYPE_STATIC(LIB, VTY_OUT_BUF, "VTY output buffer"); DEFINE_MTYPE_STATIC(LIB, VTY_HIST, "VTY history"); +DECLARE_DLIST(vtys, struct vty, itm); + /* Vty events */ enum event { VTY_SERV, @@ -73,17 +77,31 @@ enum event { #endif /* VTYSH */ }; -static void vty_event_serv(enum event event, int sock); +PREDECL_DLIST(vtyservs); + +struct vty_serv { + struct vtyservs_item itm; + + int sock; + bool vtysh; + + struct thread *t_accept; +}; + +DECLARE_DLIST(vtyservs, struct vty_serv, itm); + +static void vty_event_serv(enum event event, struct vty_serv *); static void vty_event(enum event, struct vty *); /* Extern host structure from command.c */ extern struct host host; -/* Vector which store each vty structure. */ -static vector vtyvec; +/* active listeners */ +static struct vtyservs_head vty_servs[1] = {INIT_DLIST(vty_servs[0])}; -/* Vector for vtysh connections. */ -static vector vtyshvec; +/* active connections */ +static struct vtys_head vty_sessions[1] = {INIT_DLIST(vty_sessions[0])}; +static struct vtys_head vtysh_sessions[1] = {INIT_DLIST(vtysh_sessions[0])}; /* Vty timeout value. */ static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT; @@ -94,9 +112,6 @@ static char *vty_accesslist_name = NULL; /* Vty access-calss for IPv6. */ static char *vty_ipv6_accesslist_name = NULL; -/* VTY server thread. */ -static vector Vvty_serv_thread; - /* Current directory. */ static char vty_cwd[MAXPATHLEN]; @@ -266,70 +281,28 @@ done: return len; } -static int vty_log_out(struct vty *vty, const char *level, - const char *proto_str, const char *msg, - struct timestamp_control *ctl) +int vty_json(struct vty *vty, struct json_object *json) { - int ret; - int len; - char buf[1024]; + const char *text; - if (!ctl->already_rendered) { - ctl->len = quagga_timestamp(ctl->precision, ctl->buf, - sizeof(ctl->buf)); - ctl->already_rendered = 1; - } - if (ctl->len + 1 >= sizeof(buf)) - return -1; - memcpy(buf, ctl->buf, len = ctl->len); - buf[len++] = ' '; - buf[len] = '\0'; - - if (level) - ret = snprintf(buf + len, sizeof(buf) - len, "%s: %s: ", level, - proto_str); - else - ret = snprintf(buf + len, sizeof(buf) - len, "%s: ", proto_str); - if ((ret < 0) || ((size_t)(len += ret) >= sizeof(buf))) - return -1; + if (!json) + return CMD_SUCCESS; - if (((ret = snprintf(buf + len, sizeof(buf) - len, "%s", msg)) < 0) - || ((size_t)((len += ret) + 2) > sizeof(buf))) - return -1; + text = json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_NOSLASHESCAPE); + vty_out(vty, "%s\n", text); + json_object_free(json); - buf[len++] = '\r'; - buf[len++] = '\n'; - - if (write(vty->wfd, buf, len) < 0) { - if (ERRNO_IO_RETRY(errno)) - /* Kernel buffer is full, probably too much debugging - output, so just - drop the data and ignore. */ - return -1; - /* Fatal I/O error. */ - vty->monitor = - 0; /* disable monitoring to avoid infinite recursion */ - flog_err(EC_LIB_SOCKET, - "%s: write failed to vty client fd %d, closing: %s", - __func__, vty->fd, safe_strerror(errno)); - buffer_reset(vty->obuf); - buffer_reset(vty->lbuf); - /* cannot call vty_close, because a parent routine may still try - to access the vty struct */ - vty->status = VTY_CLOSE; - shutdown(vty->fd, SHUT_RDWR); - return -1; - } - return 0; + return CMD_SUCCESS; } /* Output current time to the vty. */ void vty_time_print(struct vty *vty, int cr) { - char buf[QUAGGA_TIMESTAMP_LEN]; + char buf[FRR_TIMESTAMP_LEN]; - if (quagga_timestamp(0, buf, sizeof(buf)) == 0) { - zlog_info("quagga_timestamp error"); + if (frr_timestamp(0, buf, sizeof(buf)) == 0) { + zlog_info("frr_timestamp error"); return; } if (cr) @@ -480,19 +453,12 @@ static int vty_command(struct vty *vty, char *buf) cp++; } if (cp != NULL && *cp != '\0') { - unsigned i; char vty_str[VTY_BUFSIZ]; char prompt_str[VTY_BUFSIZ]; /* format the base vty info */ - snprintf(vty_str, sizeof(vty_str), "vty[??]@%s", vty->address); - - for (i = 0; i < vector_active(vtyvec); i++) - if (vty == vector_slot(vtyvec, i)) { - snprintf(vty_str, sizeof(vty_str), "vty[%d]@%s", - i, vty->address); - break; - } + snprintf(vty_str, sizeof(vty_str), "vty[%d]@%s", vty->fd, + vty->address); /* format the prompt */ snprintf(prompt_str, sizeof(prompt_str), cmd_prompt(vty->node), @@ -776,7 +742,7 @@ static void vty_end_config(struct vty *vty) vty->cp = 0; } -/* Delete a charcter at the current point. */ +/* Delete a character at the current point. */ static void vty_delete_char(struct vty *vty) { int i; @@ -1624,13 +1590,14 @@ static struct vty *vty_new_init(int vty_sock) memset(vty->xpath, 0, sizeof(vty->xpath)); vty->private_config = false; vty->candidate_config = vty_shared_candidate_config; - vector_set_index(vtyvec, vty_sock, vty); vty->status = VTY_NORMAL; vty->lines = -1; vty->iac = 0; vty->iac_sb_in_progress = 0; vty->sb_len = 0; + vtys_add_tail(vty_sessions, vty); + return vty; } @@ -1788,18 +1755,17 @@ struct vty *vty_stdio(void (*atclose)(int isexit)) /* Accept connection from the network. */ static int vty_accept(struct thread *thread) { + struct vty_serv *vtyserv = THREAD_ARG(thread); int vty_sock; union sockunion su; int ret; unsigned int on; - int accept_sock; + int accept_sock = vtyserv->sock; struct prefix p; struct access_list *acl = NULL; - accept_sock = THREAD_FD(thread); - /* We continue hearing vty socket. */ - vty_event_serv(VTY_SERV, accept_sock); + vty_event_serv(VTY_SERV, vtyserv); memset(&su, 0, sizeof(union sockunion)); @@ -1826,10 +1792,6 @@ static int vty_accept(struct thread *thread) && (access_list_apply(acl, &p) == FILTER_DENY)) { zlog_info("Vty connection refused from %pSU", &su); close(vty_sock); - - /* continue accepting connections */ - vty_event_serv(VTY_SERV, accept_sock); - return 0; } } @@ -1841,10 +1803,6 @@ static int vty_accept(struct thread *thread) && (access_list_apply(acl, &p) == FILTER_DENY)) { zlog_info("Vty connection refused from %pSU", &su); close(vty_sock); - - /* continue accepting connections */ - vty_event_serv(VTY_SERV, accept_sock); - return 0; } } @@ -1890,6 +1848,8 @@ static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port) ainfo_save = ainfo; do { + struct vty_serv *vtyserv; + if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6) continue; @@ -1915,7 +1875,11 @@ static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port) continue; } - vty_event_serv(VTY_SERV, sock); + vtyserv = XCALLOC(MTYPE_VTY_SERV, sizeof(*vtyserv)); + vtyserv->sock = sock; + vtyservs_add_tail(vty_servs, vtyserv); + + vty_event_serv(VTY_SERV, vtyserv); } while ((ainfo = ainfo->ai_next) != NULL); freeaddrinfo(ainfo_save); @@ -1928,6 +1892,7 @@ static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port) /* VTY shell UNIX domain socket. */ static void vty_serv_un(const char *path) { + struct vty_serv *vtyserv; int ret; int sock, len; struct sockaddr_un serv; @@ -1993,22 +1958,26 @@ static void vty_serv_un(const char *path) } } - vty_event_serv(VTYSH_SERV, sock); + vtyserv = XCALLOC(MTYPE_VTY_SERV, sizeof(*vtyserv)); + vtyserv->sock = sock; + vtyserv->vtysh = true; + vtyservs_add_tail(vty_servs, vtyserv); + + vty_event_serv(VTYSH_SERV, vtyserv); } /* #define VTYSH_DEBUG 1 */ static int vtysh_accept(struct thread *thread) { - int accept_sock; + struct vty_serv *vtyserv = THREAD_ARG(thread); + int accept_sock = vtyserv->sock; int sock; int client_len; struct sockaddr_un client; struct vty *vty; - accept_sock = THREAD_FD(thread); - - vty_event_serv(VTYSH_SERV, accept_sock); + vty_event_serv(VTYSH_SERV, vtyserv); memset(&client, 0, sizeof(struct sockaddr_un)); client_len = sizeof(struct sockaddr_un); @@ -2041,7 +2010,7 @@ static int vtysh_accept(struct thread *thread) vty->wfd = sock; vty->type = VTY_SHELL_SERV; vty->node = VIEW_NODE; - vector_set_index(vtyshvec, sock, vty); + vtys_add_tail(vtysh_sessions, vty); vty_event(VTYSH_READ, vty); @@ -2217,9 +2186,9 @@ void vty_close(struct vty *vty) /* Unset vector. */ if (vty->fd != -1) { if (vty->type == VTY_SHELL_SERV) - vector_unset(vtyshvec, vty->fd); + vtys_del(vtysh_sessions, vty); else - vector_unset(vtyvec, vty->fd); + vtys_del(vty_sessions, vty); } if (vty->wfd > 0 && vty->type == VTY_FILE) @@ -2282,7 +2251,7 @@ static void vty_read_file(struct nb_config *config, FILE *confp) vty = vty_new(); /* vty_close won't close stderr; if some config command prints - * something it'll end up there. (not ideal; it'd be beter if output + * something it'll end up there. (not ideal; it'd be better if output * from a file-load went to logging instead. Also note that if this * function is called after daemonizing, stderr will be /dev/null.) * @@ -2533,52 +2502,6 @@ tmp_free_and_out: return read_success; } -/* Small utility function which output log to the VTY. */ -void vty_log(const char *level, const char *proto_str, const char *msg, - struct timestamp_control *ctl) -{ - unsigned int i; - struct vty *vty; - - if (!vtyvec) - return; - - for (i = 0; i < vector_active(vtyvec); i++) - if ((vty = vector_slot(vtyvec, i)) != NULL) - if (vty->monitor) - vty_log_out(vty, level, proto_str, msg, ctl); -} - -/* Async-signal-safe version of vty_log for fixed strings. */ -void vty_log_fixed(char *buf, size_t len) -{ - unsigned int i; - struct iovec iov[2]; - char crlf[4] = "\r\n"; - - /* vty may not have been initialised */ - if (!vtyvec) - return; - - iov[0].iov_base = buf; - iov[0].iov_len = len; - iov[1].iov_base = crlf; - iov[1].iov_len = 2; - - for (i = 0; i < vector_active(vtyvec); i++) { - struct vty *vty; - if (((vty = vector_slot(vtyvec, i)) != NULL) && vty->monitor) - /* N.B. We don't care about the return code, since - process is - most likely just about to die anyway. */ - if (writev(vty->wfd, iov, 2) == -1) { - fprintf(stderr, "Failure to writev: %d\n", - errno); - exit(-1); - } - } -} - static void update_xpath(struct vty *vty, const char *oldpath, const char *newpath) { @@ -2597,21 +2520,11 @@ static void update_xpath(struct vty *vty, const char *oldpath, void vty_update_xpath(const char *oldpath, const char *newpath) { struct vty *vty; - unsigned int i; - - for (i = 0; i < vector_active(vtyshvec); i++) { - if ((vty = vector_slot(vtyshvec, i)) == NULL) - continue; + frr_each (vtys, vtysh_sessions, vty) update_xpath(vty, oldpath, newpath); - } - - for (i = 0; i < vector_active(vtyvec); i++) { - if ((vty = vector_slot(vtyvec, i)) == NULL) - continue; - + frr_each (vtys, vty_sessions, vty) update_xpath(vty, oldpath, newpath); - } } int vty_config_enter(struct vty *vty, bool private_config, bool exclusive) @@ -2695,21 +2608,17 @@ int vty_config_node_exit(struct vty *vty) /* Master of the threads. */ static struct thread_master *vty_master; -static void vty_event_serv(enum event event, int sock) +static void vty_event_serv(enum event event, struct vty_serv *vty_serv) { - struct thread *vty_serv_thread = NULL; - switch (event) { case VTY_SERV: - vty_serv_thread = thread_add_read(vty_master, vty_accept, - NULL, sock, NULL); - vector_set_index(Vvty_serv_thread, sock, vty_serv_thread); + thread_add_read(vty_master, vty_accept, vty_serv, + vty_serv->sock, &vty_serv->t_accept); break; #ifdef VTYSH case VTYSH_SERV: - vty_serv_thread = thread_add_read(vty_master, vtysh_accept, - NULL, sock, NULL); - vector_set_index(Vvty_serv_thread, sock, vty_serv_thread); + thread_add_read(vty_master, vtysh_accept, vty_serv, + vty_serv->sock, &vty_serv->t_accept); break; #endif /* VTYSH */ default: @@ -2761,13 +2670,11 @@ DEFUN_NOSH (config_who, "who", "Display who is on vty\n") { - unsigned int i; struct vty *v; - for (i = 0; i < vector_active(vtyvec); i++) - if ((v = vector_slot(vtyvec, i)) != NULL) - vty_out(vty, "%svty[%d] connected from %s.\n", - v->config ? "*" : " ", i, v->address); + frr_each (vtys, vty_sessions, v) + vty_out(vty, "%svty[%d] connected from %s.\n", + v->config ? "*" : " ", v->fd, v->address); return CMD_SUCCESS; } @@ -3080,25 +2987,14 @@ struct cmd_node vty_node = { /* Reset all VTY status. */ void vty_reset(void) { - unsigned int i; struct vty *vty; - struct thread *vty_serv_thread; - - for (i = 0; i < vector_active(vtyvec); i++) - if ((vty = vector_slot(vtyvec, i)) != NULL) { - buffer_reset(vty->lbuf); - buffer_reset(vty->obuf); - vty->status = VTY_CLOSE; - vty_close(vty); - } - for (i = 0; i < vector_active(Vvty_serv_thread); i++) - if ((vty_serv_thread = vector_slot(Vvty_serv_thread, i)) - != NULL) { - THREAD_OFF(vty_serv_thread); - vector_slot(Vvty_serv_thread, i) = NULL; - close(i); - } + frr_each_safe (vtys, vty_sessions, vty) { + buffer_reset(vty->lbuf); + buffer_reset(vty->obuf); + vty->status = VTY_CLOSE; + vty_close(vty); + } vty_timeout_val = VTY_TIMEOUT_DEFAULT; @@ -3118,7 +3014,7 @@ static void vty_save_cwd(void) * the whole world is coming down around us * Hence not worrying about it too much. */ - if (!chdir(SYSCONFDIR)) { + if (chdir(SYSCONFDIR)) { flog_err_sys(EC_LIB_SYSTEM_CALL, "Failure to chdir to %s, errno: %d", SYSCONFDIR, errno); @@ -3149,7 +3045,7 @@ int vty_shell_serv(struct vty *vty) void vty_init_vtysh(void) { - vtyvec = vector_init(VECTOR_MIN_SIZE); + /* currently nothing to do, but likely to have future use */ } /* Install vty's own commands like `who' command. */ @@ -3158,16 +3054,10 @@ void vty_init(struct thread_master *master_thread, bool do_command_logging) /* For further configuration read, preserve current directory. */ vty_save_cwd(); - vtyvec = vector_init(VECTOR_MIN_SIZE); - vtyshvec = vector_init(VECTOR_MIN_SIZE); - vty_master = master_thread; atexit(vty_stdio_atexit); - /* Initilize server thread vector. */ - Vvty_serv_thread = vector_init(VECTOR_MIN_SIZE); - /* Install bgp top node. */ install_node(&vty_node); @@ -3202,17 +3092,34 @@ void vty_init(struct thread_master *master_thread, bool do_command_logging) void vty_terminate(void) { + struct vty *vty; + struct vty_serv *vtyserv; + memset(vty_cwd, 0x00, sizeof(vty_cwd)); - if (vtyvec && Vvty_serv_thread) { - vty_reset(); - vector_free(vtyvec); - vector_free(Vvty_serv_thread); - vtyvec = NULL; - Vvty_serv_thread = NULL; + vty_reset(); + + /* default state of vty_sessions is initialized & empty. */ + vtys_fini(vty_sessions); + vtys_init(vty_sessions); + + /* vty_reset() doesn't close vtysh sessions */ + frr_each_safe (vtys, vtysh_sessions, vty) { + buffer_reset(vty->lbuf); + buffer_reset(vty->obuf); + vty->status = VTY_CLOSE; + vty_close(vty); } - if (vtyshvec) { - vector_free(vtyshvec); - vtyshvec = NULL; + + vtys_fini(vtysh_sessions); + vtys_init(vtysh_sessions); + + while ((vtyserv = vtyservs_pop(vty_servs))) { + THREAD_OFF(vtyserv->t_accept); + close(vtyserv->sock); + XFREE(MTYPE_VTY_SERV, vtyserv); } + + vtyservs_fini(vty_servs); + vtyservs_init(vty_servs); } @@ -39,6 +39,8 @@ extern "C" { #endif +struct json_object; + #define VTY_BUFSIZ 4096 #define VTY_MAXHIST 20 #define VTY_MAXDEPTH 8 @@ -56,8 +58,12 @@ struct vty_cfg_change { const char *value; }; +PREDECL_DLIST(vtys); + /* VTY struct. */ struct vty { + struct vtys_item itm; + /* File descripter of this vty. */ int fd; @@ -317,7 +323,11 @@ extern struct vty *vty_stdio(void (*atclose)(int isexit)); extern int vty_out(struct vty *, const char *, ...) PRINTFRR(2, 3); extern void vty_frame(struct vty *, const char *, ...) PRINTFRR(2, 3); extern void vty_endframe(struct vty *, const char *); -bool vty_set_include(struct vty *vty, const char *regexp); +extern bool vty_set_include(struct vty *vty, const char *regexp); +/* returns CMD_SUCCESS so you can do a one-line "return vty_json(...)" + * NULL check and json_object_free() is included. + */ +extern int vty_json(struct vty *vty, struct json_object *json); extern bool vty_read_config(struct nb_config *config, const char *config_file, char *config_default_dir); @@ -325,8 +335,6 @@ extern void vty_time_print(struct vty *, int); extern void vty_serv_sock(const char *, unsigned short, const char *); extern void vty_close(struct vty *); extern char *vty_get_cwd(void); -extern void vty_log(const char *level, const char *proto, const char *msg, - struct timestamp_control *); extern void vty_update_xpath(const char *oldpath, const char *newpath); extern int vty_config_enter(struct vty *vty, bool private_config, bool exclusive); @@ -341,10 +349,6 @@ extern void vty_stdio_suspend(void); extern void vty_stdio_resume(void); extern void vty_stdio_close(void); -/* Send a fixed-size message to all vty terminal monitors; this should be - an async-signal-safe function. */ -extern void vty_log_fixed(char *buf, size_t len); - #ifdef __cplusplus } #endif diff --git a/lib/vxlan.h b/lib/vxlan.h index 62963a6097..220fd8d232 100644 --- a/lib/vxlan.h +++ b/lib/vxlan.h @@ -26,7 +26,7 @@ extern "C" { #endif -/* EVPN MH DF election alogorithm */ +/* EVPN MH DF election algorithm */ #define EVPN_MH_DF_ALG_SERVICE_CARVING 0 #define EVPN_MH_DF_ALG_HRW 1 #define EVPN_MH_DF_ALG_PREF 2 diff --git a/lib/xref.c b/lib/xref.c index a41f91a228..0d3549d062 100644 --- a/lib/xref.c +++ b/lib/xref.c @@ -35,6 +35,8 @@ struct xref_block *xref_blocks; static struct xref_block **xref_block_last = &xref_blocks; +struct xrefdata_uid_head xrefdata_uid = INIT_RBTREE_UNIQ(xrefdata_uid); + static void base32(uint8_t **inpos, int *bitpos, char *out, size_t n_chars) { @@ -109,6 +111,8 @@ static void xref_add_one(const struct xref *xref) base32(&h, &bitpos, &xrefdata->uid[0], 5); xrefdata->uid[5] = '-'; base32(&h, &bitpos, &xrefdata->uid[6], 5); + + xrefdata_uid_add(&xrefdata_uid, xrefdata); } void xref_gcc_workaround(const struct xref *xref) diff --git a/lib/xref.h b/lib/xref.h index 6cff1a3769..0e3f00f690 100644 --- a/lib/xref.h +++ b/lib/xref.h @@ -22,6 +22,7 @@ #include <limits.h> #include <errno.h> #include "compiler.h" +#include "typesafe.h" #ifdef __cplusplus extern "C" { @@ -63,6 +64,8 @@ struct xref { /* type-specific bits appended by embedding this struct */ }; +PREDECL_RBTREE_UNIQ(xrefdata_uid); + struct xrefdata { /* pointer back to the const part; this will be initialized at * program startup by xref_block_add(). (Creating structs with @@ -88,8 +91,18 @@ struct xrefdata { uint32_t hashu32[2]; /* -- 32 bytes (on 64bit) -- */ + struct xrefdata_uid_item xui; }; +static inline int xrefdata_uid_cmp(const struct xrefdata *a, + const struct xrefdata *b) +{ + return strcmp(a->uid, b->uid); +} + +DECLARE_RBTREE_UNIQ(xrefdata_uid, struct xrefdata, xui, xrefdata_uid_cmp); +extern struct xrefdata_uid_head xrefdata_uid; + /* linker "magic" is used to create an array of pointers to struct xref. * the result is a contiguous block of pointers, each pointing to an xref * somewhere in the code. The linker gives us start and end pointers, we diff --git a/lib/yang.h b/lib/yang.h index d4517f969a..d625b24f6a 100644 --- a/lib/yang.h +++ b/lib/yang.h @@ -183,7 +183,7 @@ extern int yang_snodes_iterate_subtree(const struct lysc_node *snode, void *arg); /* - * Iterate over all libyang schema nodes from all loeaded modules of from the + * Iterate over all libyang schema nodes from all loaded modules of the * given YANG module. * * module diff --git a/lib/zclient.c b/lib/zclient.c index dde60a6c90..000dcaac8f 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -66,7 +66,8 @@ static int zclient_debug; /* Allocate zclient structure. */ struct zclient *zclient_new(struct thread_master *master, - struct zclient_options *opt) + struct zclient_options *opt, + zclient_handler *const *handlers, size_t n_handlers) { struct zclient *zclient; size_t stream_size = @@ -79,6 +80,9 @@ struct zclient *zclient_new(struct thread_master *master, zclient->wb = buffer_new(0); zclient->master = master; + zclient->handlers = handlers; + zclient->n_handlers = n_handlers; + zclient->receive_notify = opt->receive_notify; zclient->synchronous = opt->synchronous; @@ -450,7 +454,7 @@ enum zclient_send_status zclient_send_localsid(struct zclient *zclient, p.prefix = *sid; api.vrf_id = VRF_DEFAULT; - api.type = ZEBRA_ROUTE_BGP; + api.type = zclient->redist_default; api.instance = 0; api.safi = SAFI_UNICAST; memcpy(&api.prefix, &p, sizeof(p)); @@ -765,15 +769,17 @@ static int zclient_connect(struct thread *t) enum zclient_send_status zclient_send_rnh(struct zclient *zclient, int command, const struct prefix *p, - bool exact_match, vrf_id_t vrf_id) + bool connected, + bool resolve_via_def, vrf_id_t vrf_id) { struct stream *s; s = zclient->obuf; stream_reset(s); zclient_create_header(s, command, vrf_id); - stream_putc(s, (exact_match) ? 1 : 0); - + stream_putc(s, (connected) ? 1 : 0); + stream_putc(s, (resolve_via_def) ? 1 : 0); + stream_putw(s, SAFI_UNICAST); stream_putw(s, PREFIX_FAMILY(p)); stream_putc(s, p->prefixlen); switch (PREFIX_FAMILY(p)) { @@ -1925,6 +1931,7 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) memset(nhr, 0, sizeof(*nhr)); STREAM_GETL(s, nhr->message); + STREAM_GETW(s, nhr->safi); STREAM_GETW(s, nhr->prefix.family); STREAM_GETC(s, nhr->prefix.prefixlen); switch (nhr->prefix.family) { @@ -2096,7 +2103,7 @@ stream_failure: * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Harware Address Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Hardware Address if HW lenght different from 0 | + * | Hardware Address if HW length different from 0 | * | ... max INTERFACE_HWADDR_MAX | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Link_params? | Whether a link-params follows: 1 or 0. @@ -2106,7 +2113,7 @@ stream_failure: * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ -static int zclient_vrf_add(struct zclient *zclient, vrf_id_t vrf_id) +static int zclient_vrf_add(ZAPI_CALLBACK_ARGS) { struct vrf *vrf; char vrfname_tmp[VRF_NAMSIZ + 1] = {}; @@ -2138,7 +2145,7 @@ stream_failure: return -1; } -static void zclient_vrf_delete(struct zclient *zclient, vrf_id_t vrf_id) +static int zclient_vrf_delete(ZAPI_CALLBACK_ARGS) { struct vrf *vrf; @@ -2151,29 +2158,32 @@ static void zclient_vrf_delete(struct zclient *zclient, vrf_id_t vrf_id) * no point in attempting to delete it. */ if (!vrf) - return; + return 0; vrf_delete(vrf); + return 0; } -static int zclient_interface_add(struct zclient *zclient, vrf_id_t vrf_id) +static int zclient_interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; char ifname_tmp[INTERFACE_NAMSIZ + 1] = {}; struct stream *s = zclient->ibuf; + struct vrf *vrf; /* Read interface name. */ STREAM_GET(ifname_tmp, s, INTERFACE_NAMSIZ); /* Lookup/create interface by name. */ - if (!vrf_get(vrf_id, NULL)) { + vrf = vrf_lookup_by_id(vrf_id); + if (!vrf) { zlog_debug( "Rx'd interface add from Zebra, but VRF %u does not exist", vrf_id); return -1; } - ifp = if_get_by_name(ifname_tmp, vrf_id); + ifp = if_get_by_name(ifname_tmp, vrf_id, vrf->name); zebra_interface_if_set_value(s, ifp); @@ -2214,7 +2224,7 @@ stream_failure: return NULL; } -static void zclient_interface_delete(struct zclient *zclient, vrf_id_t vrf_id) +static int zclient_interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s = zclient->ibuf; @@ -2222,13 +2232,13 @@ static void zclient_interface_delete(struct zclient *zclient, vrf_id_t vrf_id) ifp = zebra_interface_state_read(s, vrf_id); if (ifp == NULL) - return; + return 0; if_destroy_via_zapi(ifp); - return; + return 0; } -static void zclient_interface_up(struct zclient *zclient, vrf_id_t vrf_id) +static int zclient_interface_up(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s = zclient->ibuf; @@ -2236,12 +2246,13 @@ static void zclient_interface_up(struct zclient *zclient, vrf_id_t vrf_id) ifp = zebra_interface_state_read(s, vrf_id); if (!ifp) - return; + return 0; if_up_via_zapi(ifp); + return 0; } -static void zclient_interface_down(struct zclient *zclient, vrf_id_t vrf_id) +static int zclient_interface_down(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s = zclient->ibuf; @@ -2249,12 +2260,13 @@ static void zclient_interface_down(struct zclient *zclient, vrf_id_t vrf_id) ifp = zebra_interface_state_read(s, vrf_id); if (!ifp) - return; + return 0; if_down_via_zapi(ifp); + return 0; } -static void zclient_handle_error(ZAPI_CALLBACK_ARGS) +static int zclient_handle_error(ZAPI_CALLBACK_ARGS) { enum zebra_error_types error; struct stream *s = zclient->ibuf; @@ -2263,6 +2275,7 @@ static void zclient_handle_error(ZAPI_CALLBACK_ARGS) if (zclient->handle_error) (*zclient->handle_error)(error); + return 0; } static int link_params_set_value(struct stream *s, struct if_link_params *iflp) @@ -2435,7 +2448,7 @@ size_t zebra_interface_link_params_write(struct stream *s, } /* - * format of message for address additon is: + * format of message for address addition is: * 0 * 0 1 2 3 4 5 6 7 * +-+-+-+-+-+-+-+-+ @@ -2700,9 +2713,9 @@ static int zclient_read_sync_response(struct zclient *zclient, return 0; } /** - * Connect to label manager in a syncronous way + * Connect to label manager in a synchronous way * - * It first writes the request to zcient output buffer and then + * It first writes the request to zclient output buffer and then * immediately reads the answer from the input buffer. * * @param zclient Zclient used to connect to label manager (zebra) @@ -2795,7 +2808,7 @@ stream_failure: } /** - * Function to request a srv6-locator chunk in an Asyncronous way + * Function to request a srv6-locator chunk in an asynchronous way * * @param zclient Zclient used to connect to table manager (zebra) * @param locator_name Name of SRv6-locator @@ -2905,9 +2918,9 @@ enum zclient_send_status zclient_send_get_label_chunk(struct zclient *zclient, } /** - * Function to request a label chunk in a syncronous way + * Function to request a label chunk in a synchronous way * - * It first writes the request to zlcient output buffer and then + * It first writes the request to zclient output buffer and then * immediately reads the answer from the input buffer. * * @param zclient Zclient used to connect to label manager (zebra) @@ -3082,9 +3095,9 @@ int lm_release_label_chunk(struct zclient *zclient, uint32_t start, } /** - * Connect to table manager in a syncronous way + * Connect to table manager in a synchronous way * - * It first writes the request to zcient output buffer and then + * It first writes the request to zclient output buffer and then * immediately reads the answer from the input buffer. * * @param zclient Zclient used to connect to table manager (zebra) @@ -3141,7 +3154,7 @@ stream_failure: } /** - * Function to request a table chunk in a syncronous way + * Function to request a table chunk in a synchronous way * * It first writes the request to zclient output buffer and then * immediately reads the answer from the input buffer. @@ -3580,7 +3593,7 @@ stream_failure: return -1; } -static void zclient_capability_decode(ZAPI_CALLBACK_ARGS) +static int zclient_capability_decode(ZAPI_CALLBACK_ARGS) { struct zclient_capabilities cap; struct stream *s = zclient->ibuf; @@ -3607,7 +3620,7 @@ static void zclient_capability_decode(ZAPI_CALLBACK_ARGS) (*zclient->zebra_capabilities)(&cap); stream_failure: - return; + return 0; } enum zclient_send_status zclient_send_mlag_register(struct zclient *client, @@ -3646,24 +3659,6 @@ enum zclient_send_status zclient_send_mlag_data(struct zclient *client, return zclient_send_message(client); } -static void zclient_mlag_process_up(ZAPI_CALLBACK_ARGS) -{ - if (zclient->mlag_process_up) - (*zclient->mlag_process_up)(); -} - -static void zclient_mlag_process_down(ZAPI_CALLBACK_ARGS) -{ - if (zclient->mlag_process_down) - (*zclient->mlag_process_down)(); -} - -static void zclient_mlag_handle_msg(ZAPI_CALLBACK_ARGS) -{ - if (zclient->mlag_handle_msg) - (*zclient->mlag_handle_msg)(zclient->ibuf, length); -} - /* * Send an OPAQUE message, contents opaque to zebra. The message header * is a message subtype. @@ -3853,6 +3848,24 @@ stream_failure: return -1; } +static zclient_handler *const lib_handlers[] = { + /* fundamentals */ + [ZEBRA_CAPABILITIES] = zclient_capability_decode, + [ZEBRA_ERROR] = zclient_handle_error, + + /* VRF & interface code is shared in lib */ + [ZEBRA_VRF_ADD] = zclient_vrf_add, + [ZEBRA_VRF_DELETE] = zclient_vrf_delete, + [ZEBRA_INTERFACE_ADD] = zclient_interface_add, + [ZEBRA_INTERFACE_DELETE] = zclient_interface_delete, + [ZEBRA_INTERFACE_UP] = zclient_interface_up, + [ZEBRA_INTERFACE_DOWN] = zclient_interface_down, + + /* BFD */ + [ZEBRA_BFD_DEST_REPLAY] = zclient_bfd_session_replay, + [ZEBRA_INTERFACE_BFD_DEST_UPDATE] = zclient_bfd_session_update, +}; + /* Zebra client message read function. */ static int zclient_read(struct thread *thread) { @@ -3952,290 +3965,10 @@ static int zclient_read(struct thread *thread) zlog_debug("zclient %p command %s VRF %u", zclient, zserv_command_string(command), vrf_id); - switch (command) { - case ZEBRA_CAPABILITIES: - zclient_capability_decode(command, zclient, length, vrf_id); - break; - case ZEBRA_ROUTER_ID_UPDATE: - if (zclient->router_id_update) - (*zclient->router_id_update)(command, zclient, length, - vrf_id); - break; - case ZEBRA_VRF_ADD: - zclient_vrf_add(zclient, vrf_id); - break; - case ZEBRA_VRF_DELETE: - zclient_vrf_delete(zclient, vrf_id); - break; - case ZEBRA_INTERFACE_ADD: - zclient_interface_add(zclient, vrf_id); - break; - case ZEBRA_INTERFACE_DELETE: - zclient_interface_delete(zclient, vrf_id); - break; - case ZEBRA_INTERFACE_ADDRESS_ADD: - if (zclient->interface_address_add) - (*zclient->interface_address_add)(command, zclient, - length, vrf_id); - break; - case ZEBRA_INTERFACE_ADDRESS_DELETE: - if (zclient->interface_address_delete) - (*zclient->interface_address_delete)(command, zclient, - length, vrf_id); - break; - case ZEBRA_INTERFACE_BFD_DEST_UPDATE: - if (zclient->interface_bfd_dest_update) - (*zclient->interface_bfd_dest_update)(command, zclient, - length, vrf_id); - break; - case ZEBRA_INTERFACE_NBR_ADDRESS_ADD: - if (zclient->interface_nbr_address_add) - (*zclient->interface_nbr_address_add)(command, zclient, - length, vrf_id); - break; - case ZEBRA_INTERFACE_NBR_ADDRESS_DELETE: - if (zclient->interface_nbr_address_delete) - (*zclient->interface_nbr_address_delete)( - command, zclient, length, vrf_id); - break; - case ZEBRA_INTERFACE_UP: - zclient_interface_up(zclient, vrf_id); - break; - case ZEBRA_INTERFACE_DOWN: - zclient_interface_down(zclient, vrf_id); - break; - case ZEBRA_INTERFACE_VRF_UPDATE: - if (zclient->interface_vrf_update) - (*zclient->interface_vrf_update)(command, zclient, - length, vrf_id); - break; - case ZEBRA_NEXTHOP_UPDATE: - if (zclient_debug) - zlog_debug("zclient rcvd nexthop update"); - if (zclient->nexthop_update) - (*zclient->nexthop_update)(command, zclient, length, - vrf_id); - break; - case ZEBRA_IMPORT_CHECK_UPDATE: - if (zclient_debug) - zlog_debug("zclient rcvd import check update"); - if (zclient->import_check_update) - (*zclient->import_check_update)(command, zclient, - length, vrf_id); - break; - case ZEBRA_BFD_DEST_REPLAY: - if (zclient->bfd_dest_replay) - (*zclient->bfd_dest_replay)(command, zclient, length, - vrf_id); - break; - case ZEBRA_REDISTRIBUTE_ROUTE_ADD: - if (zclient->redistribute_route_add) - (*zclient->redistribute_route_add)(command, zclient, - length, vrf_id); - break; - case ZEBRA_REDISTRIBUTE_ROUTE_DEL: - if (zclient->redistribute_route_del) - (*zclient->redistribute_route_del)(command, zclient, - length, vrf_id); - break; - case ZEBRA_INTERFACE_LINK_PARAMS: - if (zclient->interface_link_params) - (*zclient->interface_link_params)(command, zclient, - length, vrf_id); - break; - case ZEBRA_FEC_UPDATE: - if (zclient_debug) - zlog_debug("zclient rcvd fec update"); - if (zclient->fec_update) - (*zclient->fec_update)(command, zclient, length); - break; - case ZEBRA_LOCAL_ES_ADD: - if (zclient->local_es_add) - (*zclient->local_es_add)(command, zclient, length, - vrf_id); - break; - case ZEBRA_LOCAL_ES_DEL: - if (zclient->local_es_del) - (*zclient->local_es_del)(command, zclient, length, - vrf_id); - break; - case ZEBRA_LOCAL_ES_EVI_ADD: - if (zclient->local_es_evi_add) - (*zclient->local_es_evi_add)(command, zclient, length, - vrf_id); - break; - case ZEBRA_LOCAL_ES_EVI_DEL: - if (zclient->local_es_evi_del) - (*zclient->local_es_evi_del)(command, zclient, length, - vrf_id); - break; - case ZEBRA_VNI_ADD: - if (zclient->local_vni_add) - (*zclient->local_vni_add)(command, zclient, length, - vrf_id); - break; - case ZEBRA_VNI_DEL: - if (zclient->local_vni_del) - (*zclient->local_vni_del)(command, zclient, length, - vrf_id); - break; - case ZEBRA_L3VNI_ADD: - if (zclient->local_l3vni_add) - (*zclient->local_l3vni_add)(command, zclient, length, - vrf_id); - break; - case ZEBRA_L3VNI_DEL: - if (zclient->local_l3vni_del) - (*zclient->local_l3vni_del)(command, zclient, length, - vrf_id); - break; - case ZEBRA_MACIP_ADD: - if (zclient->local_macip_add) - (*zclient->local_macip_add)(command, zclient, length, - vrf_id); - break; - case ZEBRA_MACIP_DEL: - if (zclient->local_macip_del) - (*zclient->local_macip_del)(command, zclient, length, - vrf_id); - break; - case ZEBRA_IP_PREFIX_ROUTE_ADD: - if (zclient->local_ip_prefix_add) - (*zclient->local_ip_prefix_add)(command, zclient, - length, vrf_id); - break; - case ZEBRA_IP_PREFIX_ROUTE_DEL: - if (zclient->local_ip_prefix_del) - (*zclient->local_ip_prefix_del)(command, zclient, - length, vrf_id); - break; - case ZEBRA_PW_STATUS_UPDATE: - if (zclient->pw_status_update) - (*zclient->pw_status_update)(command, zclient, length, - vrf_id); - break; - case ZEBRA_ROUTE_NOTIFY_OWNER: - if (zclient->route_notify_owner) - (*zclient->route_notify_owner)(command, zclient, length, - vrf_id); - break; - case ZEBRA_RULE_NOTIFY_OWNER: - if (zclient->rule_notify_owner) - (*zclient->rule_notify_owner)(command, zclient, length, - vrf_id); - break; - case ZEBRA_NHG_NOTIFY_OWNER: - if (zclient->nhg_notify_owner) - (*zclient->nhg_notify_owner)(command, zclient, length, - vrf_id); - break; - case ZEBRA_GET_LABEL_CHUNK: - if (zclient->label_chunk) - (*zclient->label_chunk)(command, zclient, length, - vrf_id); - break; - case ZEBRA_IPSET_NOTIFY_OWNER: - if (zclient->ipset_notify_owner) - (*zclient->ipset_notify_owner)(command, zclient, length, - vrf_id); - break; - case ZEBRA_IPSET_ENTRY_NOTIFY_OWNER: - if (zclient->ipset_entry_notify_owner) - (*zclient->ipset_entry_notify_owner)(command, - zclient, length, - vrf_id); - break; - case ZEBRA_IPTABLE_NOTIFY_OWNER: - if (zclient->iptable_notify_owner) - (*zclient->iptable_notify_owner)(command, - zclient, length, - vrf_id); - break; - case ZEBRA_VXLAN_SG_ADD: - if (zclient->vxlan_sg_add) - (*zclient->vxlan_sg_add)(command, zclient, length, - vrf_id); - break; - case ZEBRA_VXLAN_SG_DEL: - if (zclient->vxlan_sg_del) - (*zclient->vxlan_sg_del)(command, zclient, length, - vrf_id); - break; - case ZEBRA_MLAG_PROCESS_UP: - zclient_mlag_process_up(command, zclient, length, vrf_id); - break; - case ZEBRA_MLAG_PROCESS_DOWN: - zclient_mlag_process_down(command, zclient, length, vrf_id); - break; - case ZEBRA_MLAG_FORWARD_MSG: - zclient_mlag_handle_msg(command, zclient, length, vrf_id); - break; - case ZEBRA_SRV6_LOCATOR_ADD: - if (zclient->srv6_locator_add) - (*zclient->srv6_locator_add)(command, zclient, length, - vrf_id); - break; - case ZEBRA_SRV6_LOCATOR_DELETE: - if (zclient->srv6_locator_delete) - (*zclient->srv6_locator_delete)(command, zclient, - length, vrf_id); - break; - case ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK: - if (zclient->process_srv6_locator_chunk) - (*zclient->process_srv6_locator_chunk)(command, zclient, - length, vrf_id); - break; - case ZEBRA_ERROR: - zclient_handle_error(command, zclient, length, vrf_id); - break; - case ZEBRA_OPAQUE_MESSAGE: - if (zclient->opaque_msg_handler) - (*zclient->opaque_msg_handler)(command, zclient, length, - vrf_id); - break; - case ZEBRA_OPAQUE_REGISTER: - if (zclient->opaque_register_handler) - (*zclient->opaque_register_handler)(command, zclient, - length, vrf_id); - break; - case ZEBRA_OPAQUE_UNREGISTER: - if (zclient->opaque_unregister_handler) - (*zclient->opaque_unregister_handler)(command, zclient, - length, vrf_id); - break; - case ZEBRA_SR_POLICY_NOTIFY_STATUS: - if (zclient->sr_policy_notify_status) - (*zclient->sr_policy_notify_status)(command, zclient, - length, vrf_id); - break; - case ZEBRA_CLIENT_CLOSE_NOTIFY: - if (zclient->zebra_client_close_notify) - (*zclient->zebra_client_close_notify)(command, zclient, - length, vrf_id); - break; - case ZEBRA_NHRP_NEIGH_ADDED: - if (zclient->neighbor_added) - (*zclient->neighbor_added)(command, zclient, length, - vrf_id); - break; - case ZEBRA_NHRP_NEIGH_REMOVED: - if (zclient->neighbor_removed) - (*zclient->neighbor_removed)(command, zclient, length, - vrf_id); - break; - case ZEBRA_NHRP_NEIGH_GET: - if (zclient->neighbor_get) - (*zclient->neighbor_get)(command, zclient, length, - vrf_id); - break; - case ZEBRA_GRE_UPDATE: - if (zclient->gre_update) - (*zclient->gre_update)(command, zclient, - length, vrf_id); - break; - default: - break; - } + if (command < array_size(lib_handlers) && lib_handlers[command]) + lib_handlers[command](command, zclient, length, vrf_id); + if (command < zclient->n_handlers && zclient->handlers[command]) + zclient->handlers[command](command, zclient, length, vrf_id); if (zclient->sock < 0) /* Connection was closed during packet processing. */ @@ -4339,11 +4072,12 @@ enum zclient_send_status zclient_interface_set_master(struct zclient *client, s = client->obuf; stream_reset(s); - zclient_create_header(s, ZEBRA_INTERFACE_SET_MASTER, master->vrf_id); + zclient_create_header(s, ZEBRA_INTERFACE_SET_MASTER, + master->vrf->vrf_id); - stream_putl(s, master->vrf_id); + stream_putl(s, master->vrf->vrf_id); stream_putl(s, master->ifindex); - stream_putl(s, slave->vrf_id); + stream_putl(s, slave->vrf->vrf_id); stream_putl(s, slave->ifindex); stream_putw_at(s, 0, stream_get_endp(s)); @@ -4430,7 +4164,7 @@ zclient_send_neigh_discovery_req(struct zclient *zclient, s = zclient->obuf; stream_reset(s); - zclient_create_header(s, ZEBRA_NEIGH_DISCOVER, ifp->vrf_id); + zclient_create_header(s, ZEBRA_NEIGH_DISCOVER, ifp->vrf->vrf_id); stream_putl(s, ifp->ifindex); stream_putc(s, p->family); @@ -4520,7 +4254,7 @@ int zclient_neigh_ip_encode(struct stream *s, uint16_t cmd, union sockunion *in, { int ret = 0; - zclient_create_header(s, cmd, ifp->vrf_id); + zclient_create_header(s, cmd, ifp->vrf->vrf_id); stream_putc(s, sockunion_family(in)); stream_write(s, sockunion_get_addr(in), sockunion_get_addrlen(in)); if (out && sockunion_family(out) != AF_UNSPEC) { @@ -4564,9 +4298,7 @@ int zclient_send_zebra_gre_request(struct zclient *client, } s = client->obuf; stream_reset(s); - zclient_create_header(s, - ZEBRA_GRE_GET, - ifp->vrf_id); + zclient_create_header(s, ZEBRA_GRE_GET, ifp->vrf->vrf_id); stream_putl(s, ifp->ifindex); stream_putw_at(s, 0, stream_get_endp(s)); zclient_send_message(client); diff --git a/lib/zclient.h b/lib/zclient.h index f9438d5db7..ca62b1afeb 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -21,6 +21,8 @@ #ifndef _ZEBRA_ZCLIENT_H #define _ZEBRA_ZCLIENT_H +struct zclient; + /* For struct zapi_route. */ #include "prefix.h" #include "ipaddr.h" @@ -126,9 +128,6 @@ typedef enum { ZEBRA_INTERFACE_NBR_ADDRESS_ADD, ZEBRA_INTERFACE_NBR_ADDRESS_DELETE, ZEBRA_INTERFACE_BFD_DEST_UPDATE, - ZEBRA_IMPORT_ROUTE_REGISTER, - ZEBRA_IMPORT_ROUTE_UNREGISTER, - ZEBRA_IMPORT_CHECK_UPDATE, ZEBRA_BFD_DEST_REGISTER, ZEBRA_BFD_DEST_DEREGISTER, ZEBRA_BFD_DEST_UPDATE, @@ -287,12 +286,20 @@ struct zapi_cap { vrf_id_t vrf_id; }; +/* clang-format off */ +#define ZAPI_CALLBACK_ARGS \ + int cmd, struct zclient *zclient, uint16_t length, vrf_id_t vrf_id + +/* function-type typedef (pointer not included) */ +typedef int (zclient_handler)(ZAPI_CALLBACK_ARGS); +/* clang-format on */ + /* Structure for the zebra client. */ struct zclient { /* The thread master we schedule ourselves on */ struct thread_master *master; - /* Priviledges to change socket values */ + /* Privileges to change socket values */ struct zebra_privs_t *privs; /* Do we care about failure events for route install? */ @@ -301,6 +308,9 @@ struct zclient { /* Is this a synchronous client? */ bool synchronous; + /* BFD enabled with bfd_protocol_integration_init() */ + bool bfd_integration; + /* Session id (optional) to support clients with multiple sessions */ uint32_t session_id; @@ -332,16 +342,15 @@ struct zclient { struct redist_proto mi_redist[AFI_MAX][ZEBRA_ROUTE_MAX]; vrf_bitmap_t redist[AFI_MAX][ZEBRA_ROUTE_MAX]; - /* Redistribute defauilt. */ + /* Redistribute default. */ vrf_bitmap_t default_information[AFI_MAX]; -#define ZAPI_CALLBACK_ARGS \ - int cmd, struct zclient *zclient, uint16_t length, vrf_id_t vrf_id - /* Pointer to the callback functions. */ void (*zebra_connected)(struct zclient *); void (*zebra_capabilities)(struct zclient_capabilities *cap); + int (*handle_error)(enum zebra_error_types error); + /* * When the zclient attempts to write the stream data to * it's named pipe to/from zebra, we may have a situation @@ -353,62 +362,15 @@ struct zclient { * more data. */ void (*zebra_buffer_write_ready)(void); - int (*router_id_update)(ZAPI_CALLBACK_ARGS); - int (*interface_address_add)(ZAPI_CALLBACK_ARGS); - int (*interface_address_delete)(ZAPI_CALLBACK_ARGS); - int (*interface_link_params)(ZAPI_CALLBACK_ARGS); - int (*interface_bfd_dest_update)(ZAPI_CALLBACK_ARGS); - int (*interface_nbr_address_add)(ZAPI_CALLBACK_ARGS); - int (*interface_nbr_address_delete)(ZAPI_CALLBACK_ARGS); - int (*interface_vrf_update)(ZAPI_CALLBACK_ARGS); - int (*nexthop_update)(ZAPI_CALLBACK_ARGS); - int (*import_check_update)(ZAPI_CALLBACK_ARGS); - int (*bfd_dest_replay)(ZAPI_CALLBACK_ARGS); - int (*redistribute_route_add)(ZAPI_CALLBACK_ARGS); - int (*redistribute_route_del)(ZAPI_CALLBACK_ARGS); - int (*fec_update)(int, struct zclient *, uint16_t); - int (*local_es_add)(ZAPI_CALLBACK_ARGS); - int (*local_es_del)(ZAPI_CALLBACK_ARGS); - int (*local_es_evi_add)(ZAPI_CALLBACK_ARGS); - int (*local_es_evi_del)(ZAPI_CALLBACK_ARGS); - int (*local_vni_add)(ZAPI_CALLBACK_ARGS); - int (*local_vni_del)(ZAPI_CALLBACK_ARGS); - int (*local_l3vni_add)(ZAPI_CALLBACK_ARGS); - int (*local_l3vni_del)(ZAPI_CALLBACK_ARGS); - void (*local_ip_prefix_add)(ZAPI_CALLBACK_ARGS); - void (*local_ip_prefix_del)(ZAPI_CALLBACK_ARGS); - int (*local_macip_add)(ZAPI_CALLBACK_ARGS); - int (*local_macip_del)(ZAPI_CALLBACK_ARGS); - int (*pw_status_update)(ZAPI_CALLBACK_ARGS); - int (*route_notify_owner)(ZAPI_CALLBACK_ARGS); - int (*rule_notify_owner)(ZAPI_CALLBACK_ARGS); - void (*label_chunk)(ZAPI_CALLBACK_ARGS); - int (*ipset_notify_owner)(ZAPI_CALLBACK_ARGS); - int (*ipset_entry_notify_owner)(ZAPI_CALLBACK_ARGS); - int (*iptable_notify_owner)(ZAPI_CALLBACK_ARGS); - int (*vxlan_sg_add)(ZAPI_CALLBACK_ARGS); - int (*vxlan_sg_del)(ZAPI_CALLBACK_ARGS); - int (*mlag_process_up)(void); - int (*mlag_process_down)(void); - int (*mlag_handle_msg)(struct stream *msg, int len); - int (*nhg_notify_owner)(ZAPI_CALLBACK_ARGS); - int (*srv6_locator_add)(ZAPI_CALLBACK_ARGS); - int (*srv6_locator_delete)(ZAPI_CALLBACK_ARGS); - int (*srv6_function_add)(ZAPI_CALLBACK_ARGS); - int (*srv6_function_delete)(ZAPI_CALLBACK_ARGS); - void (*process_srv6_locator_chunk)(ZAPI_CALLBACK_ARGS); - int (*handle_error)(enum zebra_error_types error); - int (*opaque_msg_handler)(ZAPI_CALLBACK_ARGS); - int (*opaque_register_handler)(ZAPI_CALLBACK_ARGS); - int (*opaque_unregister_handler)(ZAPI_CALLBACK_ARGS); - int (*sr_policy_notify_status)(ZAPI_CALLBACK_ARGS); - int (*zebra_client_close_notify)(ZAPI_CALLBACK_ARGS); - void (*neighbor_added)(ZAPI_CALLBACK_ARGS); - void (*neighbor_removed)(ZAPI_CALLBACK_ARGS); - void (*neighbor_get)(ZAPI_CALLBACK_ARGS); - void (*gre_update)(ZAPI_CALLBACK_ARGS); + + zclient_handler *const *handlers; + size_t n_handlers; }; +/* lib handlers added in bfd.c */ +extern int zclient_bfd_session_replay(ZAPI_CALLBACK_ARGS); +extern int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS); + /* Zebra API message flag. */ #define ZAPI_MESSAGE_NEXTHOP 0x01 #define ZAPI_MESSAGE_DISTANCE 0x02 @@ -897,7 +859,9 @@ int zclient_neigh_ip_encode(struct stream *s, uint16_t cmd, union sockunion *in, extern uint32_t zclient_get_nhg_start(uint32_t proto); extern struct zclient *zclient_new(struct thread_master *m, - struct zclient_options *opt); + struct zclient_options *opt, + zclient_handler *const *handlers, + size_t n_handlers); extern void zclient_init(struct zclient *, int, unsigned short, struct zebra_privs_t *privs); @@ -1107,7 +1071,7 @@ extern enum zclient_send_status zclient_route_send(uint8_t, struct zclient *, struct zapi_route *); extern enum zclient_send_status zclient_send_rnh(struct zclient *zclient, int command, const struct prefix *p, - bool exact_match, vrf_id_t vrf_id); + bool connected, bool resolve_via_default, vrf_id_t vrf_id); int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, uint32_t api_flags, uint32_t api_message); extern int zapi_route_encode(uint8_t, struct stream *, struct zapi_route *); diff --git a/lib/zebra.h b/lib/zebra.h index 6a02dcb922..c9794352c7 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -383,6 +383,9 @@ typedef uint32_t route_tag_t; #define ROUTE_TAG_MAX UINT32_MAX #define ROUTE_TAG_PRI PRIu32 +/* Name of hook calls */ +#define ZEBRA_ON_RIB_PROCESS_HOOK_CALL "on_rib_process_dplane_results" + #ifdef __cplusplus } #endif diff --git a/lib/zlog.c b/lib/zlog.c index 6fd52cae62..1b0751559d 100644 --- a/lib/zlog.c +++ b/lib/zlog.c @@ -47,12 +47,19 @@ #include <mach/mach_traps.h> #endif +#ifdef HAVE_LIBUNWIND +#define UNW_LOCAL_ONLY +#include <libunwind.h> +#include <dlfcn.h> +#endif + #include "memory.h" #include "atomlist.h" #include "printfrr.h" #include "frrcu.h" #include "zlog.h" #include "libfrr_trace.h" +#include "thread.h" DEFINE_MTYPE_STATIC(LIB, LOG_MESSAGE, "log message"); DEFINE_MTYPE_STATIC(LIB, LOG_TLSBUF, "log thread-local buffer"); @@ -508,6 +515,87 @@ static void vzlog_tls(struct zlog_tls *zlog_tls, const struct xref_logmsg *xref, XFREE(MTYPE_LOG_MESSAGE, msg->text); } +static void zlog_backtrace_msg(const struct xref_logmsg *xref, int prio) +{ + struct thread *tc = pthread_getspecific(thread_current); + const char *uid = xref->xref.xrefdata->uid; + bool found_thread = false; + + zlog(prio, "| (%s) message in thread %jd, at %s(), %s:%d", uid, + zlog_gettid(), xref->xref.func, xref->xref.file, xref->xref.line); + +#ifdef HAVE_LIBUNWIND + const char *threadfunc = tc ? tc->xref->funcname : NULL; + bool found_caller = false; + unw_cursor_t cursor; + unw_context_t uc; + unw_word_t ip, off, sp; + Dl_info dlinfo; + + unw_getcontext(&uc); + + unw_init_local(&cursor, &uc); + while (unw_step(&cursor) > 0) { + char buf[96], name[128] = "?"; + bool is_thread = false; + + unw_get_reg(&cursor, UNW_REG_IP, &ip); + unw_get_reg(&cursor, UNW_REG_SP, &sp); + + if (unw_is_signal_frame(&cursor)) + zlog(prio, "| (%s) ---- signal ----", uid); + + if (!unw_get_proc_name(&cursor, buf, sizeof(buf), &off)) { + if (!strcmp(buf, xref->xref.func)) + found_caller = true; + if (threadfunc && !strcmp(buf, threadfunc)) + found_thread = is_thread = true; + + snprintf(name, sizeof(name), "%s+%#lx", buf, (long)off); + } + + if (!found_caller) + continue; + + if (dladdr((void *)ip, &dlinfo)) + zlog(prio, "| (%s) %-36s %16lx+%08lx %16lx %s", uid, + name, (long)dlinfo.dli_fbase, + (long)ip - (long)dlinfo.dli_fbase, (long)sp, + dlinfo.dli_fname); + else + zlog(prio, "| (%s) %-36s %16lx %16lx", uid, name, + (long)ip, (long)sp); + + if (is_thread) + zlog(prio, "| (%s) ^- scheduled from %s(), %s:%u", uid, + tc->xref->xref.func, tc->xref->xref.file, + tc->xref->xref.line); + } +#elif defined(HAVE_GLIBC_BACKTRACE) + void *frames[64]; + char **names = NULL; + int n_frames, i; + + n_frames = backtrace(frames, array_size(frames)); + if (n_frames < 0) + n_frames = 0; + if (n_frames) + names = backtrace_symbols(frames, n_frames); + + for (i = 0; i < n_frames; i++) { + void *retaddr = frames[i]; + char *loc = names[i]; + + zlog(prio, "| (%s) %16lx %-36s", uid, (long)retaddr, loc); + } + free(names); +#endif + if (!found_thread && tc) + zlog(prio, "| (%s) scheduled from %s(), %s:%u", uid, + tc->xref->xref.func, tc->xref->xref.file, + tc->xref->xref.line); +} + void vzlogx(const struct xref_logmsg *xref, int prio, const char *fmt, va_list ap) { @@ -545,6 +633,15 @@ void vzlogx(const struct xref_logmsg *xref, int prio, vzlog_tls(zlog_tls, xref, prio, fmt, ap); else vzlog_notls(xref, prio, fmt, ap); + + if (xref) { + struct xrefdata_logmsg *xrdl; + + xrdl = container_of(xref->xref.xrefdata, struct xrefdata_logmsg, + xrefdata); + if (xrdl->fl_print_bt) + zlog_backtrace_msg(xref, prio); + } } void zlog_sigsafe(const char *text, size_t len) diff --git a/lib/zlog.h b/lib/zlog.h index d9c8952ac5..6e84fe8923 100644 --- a/lib/zlog.h +++ b/lib/zlog.h @@ -54,10 +54,14 @@ struct xref_logmsg { const char *args; }; +/* whether flag was added in config mode or enable mode */ +#define LOGMSG_FLAG_EPHEMERAL (1 << 0) +#define LOGMSG_FLAG_PERSISTENT (1 << 1) + struct xrefdata_logmsg { struct xrefdata xrefdata; - /* nothing more here right now */ + uint8_t fl_print_bt; }; /* These functions are set up to write to stdout/stderr without explicit @@ -94,15 +98,19 @@ static inline void zlog_ref(const struct xref_logmsg *xref, #define _zlog_ecref(ec_, prio, msg, ...) \ do { \ - static struct xrefdata _xrefdata = { \ - .xref = NULL, \ - .uid = {}, \ - .hashstr = (msg), \ - .hashu32 = {(prio), (ec_)}, \ + static struct xrefdata_logmsg _xrefdata = { \ + .xrefdata = \ + { \ + .xref = NULL, \ + .uid = {}, \ + .hashstr = (msg), \ + .hashu32 = {(prio), (ec_)}, \ + }, \ }; \ static const struct xref_logmsg _xref __attribute__( \ (used)) = { \ - .xref = XREF_INIT(XREFT_LOGMSG, &_xrefdata, __func__), \ + .xref = XREF_INIT(XREFT_LOGMSG, &_xrefdata.xrefdata, \ + __func__), \ .fmtstring = (msg), \ .priority = (prio), \ .ec = (ec_), \ |
