diff options
40 files changed, 250 insertions, 80 deletions
diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c index 5b2cb57921..208a2947ef 100644 --- a/bgpd/bgp_advertise.c +++ b/bgpd/bgp_advertise.c @@ -154,7 +154,7 @@ int bgp_adj_out_lookup(struct peer *peer, struct bgp_node *rn, safi_t safi; int addpath_capable; - for (adj = rn->adj_out; adj; adj = adj->next) + RB_FOREACH (adj, bgp_adj_out_rb, &rn->adj_out) SUBGRP_FOREACH_PEER (adj->subgroup, paf) if (paf->peer == peer) { afi = SUBGRP_AFI(adj->subgroup); diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h index 1912aec1bf..9aa5a0eaff 100644 --- a/bgpd/bgp_advertise.h +++ b/bgpd/bgp_advertise.h @@ -67,9 +67,8 @@ struct bgp_advertise { /* BGP adjacency out. */ struct bgp_adj_out { - /* Lined list pointer. */ - struct bgp_adj_out *next; - struct bgp_adj_out *prev; + /* RB Tree of adjacency entries */ + RB_ENTRY(bgp_adj_out) adj_entry; /* Advertised subgroup. */ struct update_subgroup *subgroup; @@ -89,6 +88,10 @@ struct bgp_adj_out { struct bgp_advertise *adv; }; +RB_HEAD(bgp_adj_out_rb, bgp_adj_out); +RB_PROTOTYPE(bgp_adj_out_rb, bgp_adj_out, adj_entry, + bgp_adj_out_compare); + /* BGP adjacency in. */ struct bgp_adj_in { /* Linked list pointer. */ @@ -134,8 +137,6 @@ struct bgp_synchronize { #define BGP_ADJ_IN_ADD(N, A) BGP_PATH_INFO_ADD(N, A, adj_in) #define BGP_ADJ_IN_DEL(N, A) BGP_PATH_INFO_DEL(N, A, adj_in) -#define BGP_ADJ_OUT_ADD(N, A) BGP_PATH_INFO_ADD(N, A, adj_out) -#define BGP_ADJ_OUT_DEL(N, A) BGP_PATH_INFO_DEL(N, A, adj_out) #define BGP_ADV_FIFO_ADD(F, N) \ do { \ diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 7be7937786..ac5880938f 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -2498,6 +2498,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, &attr_new->mp_nexthop_global))) SET_FLAG(pi->flags, BGP_PATH_IGP_CHANGED); + bgp_path_info_set_flag(rn, pi, BGP_PATH_ATTR_CHANGED); /* Unintern existing, set to new. */ bgp_attr_unintern(&pi->attr); pi->attr = attr_new; diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index f8ff4f2f07..47e7c1686f 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -235,6 +235,9 @@ static __attribute__((__noreturn__)) void bgp_exit(int status) bf_free(bm->rd_idspace); list_delete(&bm->bgp); + + bgp_lp_finish(); + memset(bm, 0, sizeof(*bm)); frr_fini(); diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index baf2c1e029..d4204126e1 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -578,7 +578,7 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ return bpi; } - new = info_make(bpi_ultimate->type, BGP_ROUTE_IMPORTED, 0, + new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_IMPORTED, 0, bgp->peer_self, new_attr, bn); if (nexthop_self_flag) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 74e4276c06..8cefb3ff39 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -10560,7 +10560,7 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, output_count++; } } else if (type == bgp_show_adj_route_advertised) { - for (adj = rn->adj_out; adj; adj = adj->next) + RB_FOREACH (adj, bgp_adj_out_rb, &rn->adj_out) SUBGRP_FOREACH_PEER (adj->subgroup, paf) { if (paf->peer != peer || !adj->attr) continue; diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c index 728eeaa3a9..0321412263 100644 --- a/bgpd/bgp_table.c +++ b/bgpd/bgp_table.c @@ -67,6 +67,8 @@ static struct route_node *bgp_node_create(route_table_delegate_t *delegate, { struct bgp_node *node; node = XCALLOC(MTYPE_BGP_NODE, sizeof(struct bgp_node)); + + RB_INIT(bgp_adj_out_rb, &node->adj_out); return bgp_node_to_rnode(node); } diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h index c267b4fe8a..4795ab741c 100644 --- a/bgpd/bgp_table.h +++ b/bgpd/bgp_table.h @@ -26,6 +26,7 @@ #include "queue.h" #include "linklist.h" #include "bgpd.h" +#include "bgp_advertise.h" struct bgp_table { /* table belongs to this instance */ @@ -52,7 +53,7 @@ struct bgp_node { */ ROUTE_NODE_FIELDS - struct bgp_adj_out *adj_out; + struct bgp_adj_out_rb adj_out; struct bgp_adj_in *adj_in; diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index 7196bbbf12..cefbf72b58 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -55,12 +55,24 @@ /******************** * PRIVATE FUNCTIONS ********************/ +static int bgp_adj_out_compare(const struct bgp_adj_out *o1, + const struct bgp_adj_out *o2) +{ + if (o1->subgroup < o2->subgroup) + return -1; + + if (o1->subgroup > o2->subgroup) + return 1; + + return 0; +} +RB_GENERATE(bgp_adj_out_rb, bgp_adj_out, adj_entry, bgp_adj_out_compare); static inline struct bgp_adj_out *adj_lookup(struct bgp_node *rn, struct update_subgroup *subgrp, uint32_t addpath_tx_id) { - struct bgp_adj_out *adj; + struct bgp_adj_out *adj, lookup; struct peer *peer; afi_t afi; safi_t safi; @@ -76,19 +88,16 @@ static inline struct bgp_adj_out *adj_lookup(struct bgp_node *rn, /* update-groups that do not support addpath will pass 0 for * addpath_tx_id so do not both matching against it */ - for (adj = rn->adj_out; adj; adj = adj->next) { - if (adj->subgroup == subgrp) { - if (addpath_capable) { - if (adj->addpath_tx_id == addpath_tx_id) { - break; - } - } else { - break; - } - } + lookup.subgroup = subgrp; + adj = RB_FIND(bgp_adj_out_rb, &rn->adj_out, &lookup); + if (adj) { + if (addpath_capable) { + if (adj->addpath_tx_id == addpath_tx_id) + return adj; + } else + return adj; } - - return adj; + return NULL; } static void adj_free(struct bgp_adj_out *adj) @@ -110,8 +119,7 @@ static void subgrp_withdraw_stale_addpath(struct updwalk_context *ctx, /* Look through all of the paths we have advertised for this rn and send * a withdraw for the ones that are no longer present */ - for (adj = ctx->rn->adj_out; adj; adj = adj_next) { - adj_next = adj->next; + RB_FOREACH_SAFE (adj, bgp_adj_out_rb, &ctx->rn->adj_out, adj_next) { if (adj->subgroup == subgrp) { for (pi = ctx->rn->info; pi; pi = pi->next) { @@ -204,10 +212,9 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg) /* Find the addpath_tx_id of the path we * had advertised and * send a withdraw */ - for (adj = ctx->rn->adj_out; adj; - adj = adj_next) { - adj_next = adj->next; - + RB_FOREACH_SAFE (adj, bgp_adj_out_rb, + &ctx->rn->adj_out, + adj_next) { if (adj->subgroup == subgrp) { subgroup_process_announce_selected( subgrp, NULL, @@ -243,7 +250,7 @@ static void subgrp_show_adjq_vty(struct update_subgroup *subgrp, output_count = 0; for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) - for (adj = rn->adj_out; adj; adj = adj->next) + RB_FOREACH (adj, bgp_adj_out_rb, &rn->adj_out) if (adj->subgroup == subgrp) { if (header1) { vty_out(vty, @@ -394,7 +401,7 @@ struct bgp_adj_out *bgp_adj_out_alloc(struct update_subgroup *subgrp, adj = XCALLOC(MTYPE_BGP_ADJ_OUT, sizeof(struct bgp_adj_out)); adj->subgroup = subgrp; if (rn) { - BGP_ADJ_OUT_ADD(rn, adj); + RB_INSERT(bgp_adj_out_rb, &rn->adj_out, adj); bgp_lock_node(rn); adj->rn = rn; } @@ -551,7 +558,7 @@ void bgp_adj_out_unset_subgroup(struct bgp_node *rn, subgroup_trigger_write(subgrp); } else { /* Remove myself from adjacency. */ - BGP_ADJ_OUT_DEL(rn, adj); + RB_REMOVE(bgp_adj_out_rb, &rn->adj_out, adj); /* Free allocated information. */ adj_free(adj); @@ -572,7 +579,7 @@ void bgp_adj_out_remove_subgroup(struct bgp_node *rn, struct bgp_adj_out *adj, if (adj->adv) bgp_advertise_clean_subgroup(subgrp, adj); - BGP_ADJ_OUT_DEL(rn, adj); + RB_REMOVE(bgp_adj_out_rb, &rn->adj_out, adj); adj_free(adj); } diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 06caebe567..f83b146175 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -9722,7 +9722,7 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_object_string_add( json_nxt, print_store, - "received"); + "recieved"); /* misspelled for compatibility */ } } json_object_object_add( diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 94cb285a03..d6be3228fe 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -7963,5 +7963,4 @@ void bgp_terminate(void) if (bm->t_rmap_update) BGP_TIMER_OFF(bm->t_rmap_update); - bgp_lp_finish(); } diff --git a/bgpd/rfp-example/librfp/rfp_example.c b/bgpd/rfp-example/librfp/rfp_example.c index af3092232c..e8f670cf12 100644 --- a/bgpd/rfp-example/librfp/rfp_example.c +++ b/bgpd/rfp-example/librfp/rfp_example.c @@ -255,8 +255,8 @@ static int rfp_cfg_write_cb(struct vty *vty, void *rfp_start_val) rfi->rfapi_config.holddown_factor); write++; } - if (rfi->rfapi_config.download_type != RFAPI_RFP_DOWNLOAD_FULL) { - vty_out(vty, " rfp full-table-download off\n"); + if (rfi->rfapi_config.download_type == RFAPI_RFP_DOWNLOAD_FULL) { + vty_out(vty, " rfp full-table-download on\n"); write++; } return write; diff --git a/configure.ac b/configure.ac index f7328988b2..0d75f7d319 100755 --- a/configure.ac +++ b/configure.ac @@ -13,7 +13,7 @@ AC_SUBST([PACKAGE_URL]) PACKAGE_FULLNAME="FRRouting" AC_SUBST([PACKAGE_FULLNAME]) -CONFIG_ARGS="$ac_configure_args" +CONFIG_ARGS="`echo $ac_configure_args | sed -e \"s% '[[A-Z]]*FLAGS=[[^']]\+'%%g\"`" AC_SUBST([CONFIG_ARGS]) AC_CONFIG_SRCDIR([lib/zebra.h]) diff --git a/lib/libfrr.c b/lib/libfrr.c index 6ebe24eef7..9119b04992 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -653,7 +653,7 @@ struct thread_master *frr_init(void) lib_error_init(); yang_init(); - nb_init(di->yang_modules, di->n_yang_modules); + nb_init(master, di->yang_modules, di->n_yang_modules); return master; } diff --git a/lib/northbound.c b/lib/northbound.c index 490b3abe57..8503f87d60 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -1539,7 +1539,8 @@ static void nb_load_callbacks(const struct frr_yang_module_info *module) } } -void nb_init(const struct frr_yang_module_info *modules[], size_t nmodules) +void nb_init(struct thread_master *tm, + const struct frr_yang_module_info *modules[], size_t nmodules) { unsigned int errors = 0; @@ -1574,7 +1575,7 @@ void nb_init(const struct frr_yang_module_info *modules[], size_t nmodules) running_config = nb_config_new(NULL); /* Initialize the northbound CLI. */ - nb_cli_init(); + nb_cli_init(tm); } void nb_terminate(void) diff --git a/lib/northbound.h b/lib/northbound.h index e26a2f8617..c8e8d75701 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -20,6 +20,7 @@ #ifndef _FRR_NORTHBOUND_H_ #define _FRR_NORTHBOUND_H_ +#include "thread.h" #include "hook.h" #include "linklist.h" #include "openbsd-tree.h" @@ -825,7 +826,7 @@ extern const char *nb_client_name(enum nb_client client); * nmodules * Size of the modules array. */ -extern void nb_init(const struct frr_yang_module_info *modules[], +extern void nb_init(struct thread_master *tm, const struct frr_yang_module_info *modules[], size_t nmodules); /* diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index 2cacc6b1dc..d685a4e7c2 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -36,6 +36,7 @@ int debug_northbound; struct nb_config *vty_shared_candidate_config; +static struct thread_master *master; static void vty_show_libyang_errors(struct vty *vty, struct ly_ctx *ly_ctx) { @@ -213,16 +214,80 @@ int nb_cli_rpc(const char *xpath, struct list *input, struct list *output) } } -static int nb_cli_commit(struct vty *vty, bool force, char *comment) +void nb_cli_confirmed_commit_clean(struct vty *vty) +{ + THREAD_TIMER_OFF(vty->t_confirmed_commit_timeout); + nb_config_free(vty->confirmed_commit_rollback); + vty->confirmed_commit_rollback = NULL; +} + +int nb_cli_confirmed_commit_rollback(struct vty *vty) +{ + uint32_t transaction_id; + int ret; + + /* Perform the rollback. */ + ret = nb_candidate_commit( + vty->confirmed_commit_rollback, NB_CLIENT_CLI, true, + "Rollback to previous configuration - confirmed commit has timed out", + &transaction_id); + if (ret == NB_OK) + vty_out(vty, + "Rollback performed successfully (Transaction ID #%u).\n", + transaction_id); + else + vty_out(vty, "Failed to rollback to previous configuration.\n"); + + return ret; +} + +static int nb_cli_confirmed_commit_timeout(struct thread *thread) +{ + struct vty *vty = THREAD_ARG(thread); + + /* XXX: broadcast this message to all logged-in users? */ + vty_out(vty, + "\nConfirmed commit has timed out, rolling back to previous configuration.\n\n"); + + nb_cli_confirmed_commit_rollback(vty); + nb_cli_confirmed_commit_clean(vty); + + return 0; +} + +static int nb_cli_commit(struct vty *vty, bool force, + unsigned int confirmed_timeout, char *comment) { uint32_t transaction_id; int ret; + /* Check if there's a pending confirmed commit. */ + if (vty->t_confirmed_commit_timeout) { + if (confirmed_timeout) { + /* Reset timeout if "commit confirmed" is used again. */ + vty_out(vty, + "%% Resetting confirmed-commit timeout to %u minute(s)\n\n", + confirmed_timeout); + + THREAD_TIMER_OFF(vty->t_confirmed_commit_timeout); + thread_add_timer(master, + nb_cli_confirmed_commit_timeout, vty, + confirmed_timeout * 60, + &vty->t_confirmed_commit_timeout); + } else { + /* Accept commit confirmation. */ + vty_out(vty, "%% Commit complete.\n\n"); + nb_cli_confirmed_commit_clean(vty); + } + return CMD_SUCCESS; + } + if (vty_exclusive_lock != NULL && vty_exclusive_lock != vty) { vty_out(vty, "%% Configuration is locked by another VTY.\n\n"); return CMD_WARNING; } + /* "force" parameter. */ if (!force && nb_candidate_needs_update(vty->candidate_config)) { vty_out(vty, "%% Candidate configuration needs to be updated before commit.\n\n"); @@ -231,6 +296,16 @@ static int nb_cli_commit(struct vty *vty, bool force, char *comment) return CMD_WARNING; } + /* "confirm" parameter. */ + if (confirmed_timeout) { + vty->confirmed_commit_rollback = nb_config_dup(running_config); + + vty->t_confirmed_commit_timeout = NULL; + thread_add_timer(master, nb_cli_confirmed_commit_timeout, vty, + confirmed_timeout * 60, + &vty->t_confirmed_commit_timeout); + } + ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI, true, comment, &transaction_id); @@ -534,18 +609,22 @@ DEFUN (config_private, DEFPY (config_commit, config_commit_cmd, - "commit [force$force]", + "commit [{force$force|confirmed (1-60)}]", "Commit changes into the running configuration\n" - "Force commit even if the candidate is outdated\n") + "Force commit even if the candidate is outdated\n" + "Rollback this commit unless there is a confirming commit\n" + "Timeout in minutes for the commit to be confirmed\n") { - return nb_cli_commit(vty, !!force, NULL); + return nb_cli_commit(vty, !!force, confirmed, NULL); } DEFPY (config_commit_comment, config_commit_comment_cmd, - "commit [force$force] comment LINE...", + "commit [{force$force|confirmed (1-60)}] comment LINE...", "Commit changes into the running configuration\n" "Force commit even if the candidate is outdated\n" + "Rollback this commit unless there is a confirming commit\n" + "Timeout in minutes for the commit to be confirmed\n" "Assign a comment to this commit\n" "Comment for this commit (Max 80 characters)\n") { @@ -555,7 +634,7 @@ DEFPY (config_commit_comment, argv_find(argv, argc, "LINE", &idx); comment = argv_concat(argv, argc, idx); - ret = nb_cli_commit(vty, !!force, comment); + ret = nb_cli_commit(vty, !!force, confirmed, comment); XFREE(MTYPE_TMP, comment); return ret; @@ -754,6 +833,30 @@ DEFPY (show_config_candidate, return CMD_SUCCESS; } +DEFPY (show_config_candidate_section, + show_config_candidate_section_cmd, + "show", + SHOW_STR) +{ + struct lyd_node *dnode; + + /* Top-level configuration node, display everything. */ + if (vty->xpath_index == 0) + return nb_cli_show_config(vty, vty->candidate_config, + NB_CFG_FMT_CMDS, NULL, false); + + /* Display only the current section of the candidate configuration. */ + dnode = yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH); + if (!dnode) + /* Shouldn't happen. */ + return CMD_WARNING; + + nb_cli_show_dnode_cmds(vty, dnode, 0); + vty_out(vty, "!\n"); + + return CMD_SUCCESS; +} + DEFPY (show_config_compare, show_config_compare_cmd, "show configuration compare\ @@ -1468,6 +1571,8 @@ static struct cmd_node nb_debug_node = {NORTHBOUND_DEBUG_NODE, "", 1}; void nb_cli_install_default(int node) { + install_element(node, &show_config_candidate_section_cmd); + if (frr_get_cli_mode() != FRR_CLI_TRANSACTIONAL) return; @@ -1517,8 +1622,10 @@ static const struct cmd_variable_handler yang_var_handlers[] = { .completions = yang_translator_autocomplete}, {.completions = NULL}}; -void nb_cli_init(void) +void nb_cli_init(struct thread_master *tm) { + master = tm; + /* Initialize the shared candidate configuration. */ vty_shared_candidate_config = nb_config_new(NULL); diff --git a/lib/northbound_cli.h b/lib/northbound_cli.h index febcbd86f1..362a4bc325 100644 --- a/lib/northbound_cli.h +++ b/lib/northbound_cli.h @@ -105,8 +105,10 @@ extern void nb_cli_show_dnode_cmds(struct vty *vty, struct lyd_node *dnode, bool show_defaults); /* Prototypes of internal functions. */ +extern void nb_cli_confirmed_commit_clean(struct vty *vty); +extern int nb_cli_confirmed_commit_rollback(struct vty *vty); extern void nb_cli_install_default(int node); -extern void nb_cli_init(void); +extern void nb_cli_init(struct thread_master *tm); extern void nb_cli_terminate(void); #endif /* _FRR_NORTHBOUND_CLI_H_ */ @@ -2714,6 +2714,14 @@ int vty_config_enter(struct vty *vty, bool private_config, bool exclusive) void vty_config_exit(struct vty *vty) { + /* Check if there's a pending confirmed commit. */ + if (vty->t_confirmed_commit_timeout) { + vty_out(vty, + "WARNING: exiting with a pending confirmed commit. Rolling back to previous configuration.\n\n"); + nb_cli_confirmed_commit_rollback(vty); + nb_cli_confirmed_commit_clean(vty); + } + vty_config_exclusive_unlock(vty); if (vty->candidate_config) { @@ -126,6 +126,10 @@ struct vty { /* Base candidate configuration. */ struct nb_config *candidate_config_base; + /* Confirmed-commit timeout and rollback configuration. */ + struct thread *t_confirmed_commit_timeout; + struct nb_config *confirmed_commit_rollback; + /* qobj object ID (replacement for "index") */ uint64_t qobj_index; diff --git a/staticd/static_vty.c b/staticd/static_vty.c index 59d4ae924b..ae0026cc97 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -1211,7 +1211,7 @@ DEFPY(ipv6_route_address_interface, { struct static_vrf *svrf; struct static_vrf *nh_svrf; - const char *flag; + const char *flag = NULL; if (table_str && vrf && !vrf_is_mapped_on_netns(vrf_lookup_by_name(vrf))) { vty_out(vty, @@ -1280,7 +1280,7 @@ DEFPY(ipv6_route_address_interface_vrf, VTY_DECLVAR_CONTEXT(vrf, vrf); struct static_vrf *svrf = vrf->info; struct static_vrf *nh_svrf; - const char *flag; + const char *flag = NULL; if (table_str && !vrf_is_mapped_on_netns(vrf)) { vty_out(vty, @@ -1341,7 +1341,7 @@ DEFPY(ipv6_route, { struct static_vrf *svrf; struct static_vrf *nh_svrf; - const char *flag; + const char *flag = NULL; if (table_str && vrf && !vrf_is_mapped_on_netns(vrf_lookup_by_name(vrf))) { vty_out(vty, @@ -1407,7 +1407,7 @@ DEFPY(ipv6_route_vrf, VTY_DECLVAR_CONTEXT(vrf, vrf); struct static_vrf *svrf = vrf->info; struct static_vrf *nh_svrf; - const char *flag; + const char *flag = NULL; if (table_str && !vrf_is_mapped_on_netns(vrf)) { vty_out(vty, diff --git a/tests/bgpd/test_peer_attr.c b/tests/bgpd/test_peer_attr.c index 2fbc686e1e..78016dc9ce 100644 --- a/tests/bgpd/test_peer_attr.c +++ b/tests/bgpd/test_peer_attr.c @@ -1384,10 +1384,10 @@ static void bgp_startup(void) LOG_DAEMON); zprivs_preinit(&bgpd_privs); zprivs_init(&bgpd_privs); - yang_init(); - nb_init(NULL, 0); master = thread_master_create(NULL); + yang_init(); + nb_init(master, NULL, 0); bgp_master_init(master); bgp_option_set(BGP_OPT_NO_LISTEN); vrf_init(NULL, NULL, NULL, NULL, NULL); diff --git a/tests/helpers/c/main.c b/tests/helpers/c/main.c index 9e34a7c255..768cf296ad 100644 --- a/tests/helpers/c/main.c +++ b/tests/helpers/c/main.c @@ -156,7 +156,7 @@ int main(int argc, char **argv) vty_init(master); memory_init(); yang_init(); - nb_init(NULL, 0); + nb_init(master, NULL, 0); /* OSPF vty inits. */ test_vty_init(); diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c index 04f1e3253d..393b588745 100644 --- a/tests/lib/cli/common_cli.c +++ b/tests/lib/cli/common_cli.c @@ -80,11 +80,12 @@ int main(int argc, char **argv) /* Library inits. */ cmd_init(1); cmd_hostname_set("test"); + cmd_domainname_set("test.domain"); vty_init(master); memory_init(); yang_init(); - nb_init(NULL, 0); + nb_init(master, NULL, 0); test_init(argc, argv); diff --git a/tests/lib/cli/test_cli.refout.in b/tests/lib/cli/test_cli.refout.in index af8f9ce56a..8f9959cc47 100644 --- a/tests/lib/cli/test_cli.refout.in +++ b/tests/lib/cli/test_cli.refout.in @@ -311,6 +311,7 @@ frr version @PACKAGE_VERSION@ frr defaults @DFLT_NAME@
!
hostname test
+domainname test.domain
!
!
!
@@ -327,6 +328,7 @@ frr version @PACKAGE_VERSION@ frr defaults @DFLT_NAME@
!
hostname foohost
+domainname test.domain
!
!
!
diff --git a/tests/lib/cli/test_commands.c b/tests/lib/cli/test_commands.c index 74816ece8c..ba46bdcea9 100644 --- a/tests/lib/cli/test_commands.c +++ b/tests/lib/cli/test_commands.c @@ -143,7 +143,7 @@ static void test_init(void) cmd_init(1); yang_init(); - nb_init(NULL, 0); + nb_init(master, NULL, 0); install_node(&bgp_node, NULL); install_node(&rip_node, NULL); diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c index a9a89ee491..7c5713d8f9 100644 --- a/tests/lib/northbound/test_oper_data.c +++ b/tests/lib/northbound/test_oper_data.c @@ -449,7 +449,7 @@ int main(int argc, char **argv) vty_init(master); memory_init(); yang_init(); - nb_init(modules, array_size(modules)); + nb_init(master, modules, array_size(modules)); /* Create artificial data. */ create_data(num_vrfs, num_interfaces, num_routes); diff --git a/tests/topotests/Dockerfile b/tests/topotests/Dockerfile index 72a876ed83..ea6fa4b9e0 100644 --- a/tests/topotests/Dockerfile +++ b/tests/topotests/Dockerfile @@ -72,7 +72,6 @@ RUN echo "" >> /etc/security/limits.conf; \ # Copy run scripts to facilitate users wanting to run the tests COPY docker/inner /opt/topotests -WORKDIR /root/topotests ENV PATH "$PATH:/opt/topotests" RUN echo "cat /opt/topotests/motd.txt" >> /root/.profile && \ diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf index 274eceaf0f..cb08db5314 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf @@ -7,7 +7,7 @@ log monitor notifications log commands log file bgpd.log -router bgp 5228 +router bgp 5228 vrf ce4-cust2 bgp router-id 99.0.0.4 neighbor 192.168.2.1 remote-as 5228 neighbor 192.168.2.1 update-source 192.168.2.2 diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/zebra.conf index bfd8ba8435..e55c9e779a 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/zebra.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/zebra.conf @@ -2,7 +2,7 @@ log file zebra.log ! hostname ce4 ! -interface lo +interface ce4-cust2 ip address 99.0.0.4/32 ! interface ce4-eth0 diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py index 596701cee2..31e23faede 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py @@ -196,7 +196,18 @@ def ltemplatePreRouterStartHook(): for intf in intfs: cc.doCmd(tgen, rtr, 'echo 1 > /proc/sys/net/mpls/conf/{}/input'.format(intf)) logger.info('setup {0} vrf {0}-cust2, {0}-eth5. enabled mpls input.'.format(rtr)) - if cc.getOutput() != 3: + #put ce4-eth0 into a VRF (no default instance!) + rtrs = ['ce4'] + cmds = ['ip link add {0}-cust2 type vrf table 20', + 'ip ru add oif {0}-cust2 table 20', + 'ip ru add iif {0}-cust2 table 20', + 'ip link set dev {0}-cust2 up', + 'sysctl -w net.ipv4.udp_l3mdev_accept={}'.format(l3mdev_accept)] + for rtr in rtrs: + for cmd in cmds: + cc.doCmd(tgen, rtr, cmd.format(rtr)) + cc.doCmd(tgen, rtr, 'ip link set dev {0}-eth0 master {0}-cust2'.format(rtr)) + if cc.getOutput() != 4: InitSuccess = False logger.info('Unexpected output seen ({} times, tests will be skipped'.format(cc.getOutput())) else: diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py index 1dfd22f6bd..28ecfeec5a 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py @@ -2,7 +2,7 @@ from lutil import luCommand luCommand('ce1','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up',180) luCommand('ce2','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up') luCommand('ce3','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up') -luCommand('ce4','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up') +luCommand('ce4','vtysh -c "show bgp vrf all summary"',' 00:0','wait','Adjacencies up',180) luCommand('r1','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60) luCommand('r3','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60) luCommand('r4','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py index 174666a075..9827a9e2c1 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py @@ -31,13 +31,14 @@ if ret != False and found != None: luCommand('ce1', 'ping 99.0.0.4 -I 99.0.0.1 -c 1', ' 0. packet loss','wait','CE->CE (loopback) ping - l3vpn+zebra case') - luCommand('ce4', 'ping 99.0.0.1 -I 99.0.0.4 -c 1', - ' 0. packet loss','wait','CE->CE (loopback) ping - l3vpn+zebra case') + #skip due to VRF weirdness + #luCommand('ce4', 'ping 99.0.0.1 -I 99.0.0.4 -c 1', + # ' 0. packet loss','wait','CE->CE (loopback) ping - l3vpn+zebra case') luCommand('ce1', 'ping 99.0.0.4 -I 99.0.0.1 -c 1', ' 0. packet loss','wait','CE->CE (loopback) ping') - luCommand('ce4', 'ping 99.0.0.1 -I 99.0.0.4 -c 1', - ' 0. packet loss','wait','CE->CE (loopback) ping') + #luCommand('ce4', 'ping 99.0.0.1 -I 99.0.0.4 -c 1', + # ' 0. packet loss','wait','CE->CE (loopback) ping') luCommand('r3', 'ip -M route show', '103', 'pass', 'MPLS->VRF route installed') luCommand('ce2', 'ping 99.0.0.3 -I 99.0.0.2 -c 1', diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py index 6239f77a82..778d504040 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py @@ -1,4 +1,12 @@ from lutil import luCommand + +rtrs = ['r1', 'r3', 'r4', 'ce1', 'ce2', 'ce3', 'ce4'] +for rtr in rtrs: + luCommand(rtr,'sysctl net.ipv4.tcp_l3mdev_accept',' = \d*','none','') + found = luLast() + luCommand(rtr,'ss -aep',':bgp','pass','IPv4:bgp, l3mdev%s' % found.group(0)) + luCommand(rtr,'ss -aep',':.:bgp','pass','IPv6:bgp') + rtrs = ['r1', 'r3', 'r4'] for rtr in rtrs: luCommand(rtr, 'ip link show type vrf {}-cust1'.format(rtr),'cust1: .*UP,LOWER_UP','pass','VRF cust1 up') @@ -11,4 +19,6 @@ rtrs = ['ce1', 'ce2', 'ce3'] for rtr in rtrs: luCommand(rtr, 'ip route show','192.168...0/24 dev ce.-eth0','pass','CE interface route') luCommand(rtr,'ping 192.168.1.1 -c 1',' 0. packet loss','wait','CE->PE ping') -luCommand('ce4','ping 192.168.2.1 -c 1',' 0. packet loss','wait','CE4->PE4 ping') +luCommand('ce4', 'ip link show type vrf ce4-cust2','cust2: .*UP,LOWER_UP','pass','VRF cust2 up') +luCommand('ce4', 'ip route show vrf ce4-cust2','192.168...0/24 dev ce.-eth0','pass','CE interface route') +luCommand('ce4','ping 192.168.2.1 -c 1 -I ce4-cust2',' 0. packet loss','wait','CE4->PE4 ping') diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py index 7b2387bd0b..e47ea5f2cd 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py @@ -37,7 +37,7 @@ want = [ {'p':'5.4.3.0/24', 'n':'99.0.0.4'}, {'p':'99.0.0.4/32', 'n':'0.0.0.0'}, ] -bgpribRequireUnicastRoutes('ce4','ipv4','','Cust 4 routes in ce1',want) +bgpribRequireUnicastRoutes('ce4','ipv4','ce4-cust2','Cust 4 routes in ce1',want) ######################################################################## @@ -307,12 +307,12 @@ want = [ ] bgpribRequireUnicastRoutes('ce3','ipv4','','Cust 1 routes from remote',want) -luCommand('ce4','vtysh -c "show bgp ipv4 uni"','10 routes and 10','wait','Local and remote routes', 10) +luCommand('ce4','vtysh -c "show bgp vrf ce4-cust2 ipv4 uni"','10 routes and 10','wait','Local and remote routes', 10) want = [ {'p':'5.1.0.0/24', 'n':'192.168.2.1'}, {'p':'5.1.1.0/24', 'n':'192.168.2.1'}, {'p':'5.1.2.0/24', 'n':'192.168.2.1'}, {'p':'5.1.3.0/24', 'n':'192.168.2.1'}, ] -bgpribRequireUnicastRoutes('ce4','ipv4','','Cust 2 routes from remote',want) +bgpribRequireUnicastRoutes('ce4','ipv4','ce4-cust2','Cust 2 routes from remote',want) diff --git a/tests/topotests/docker/frr-topotests.sh b/tests/topotests/docker/frr-topotests.sh index 673354f5da..8e93ed31ff 100755 --- a/tests/topotests/docker/frr-topotests.sh +++ b/tests/topotests/docker/frr-topotests.sh @@ -80,6 +80,8 @@ fi # them from the host however, they can be used just fine. # +export PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" + for module in mpls-router mpls-iptunnel; do if modprobe -n $module 2> /dev/null; then : @@ -133,7 +135,6 @@ fi set -- --rm -i \ -v "$TOPOTEST_LOGS:/tmp" \ -v "$TOPOTEST_FRR:/root/host-frr:ro" \ - -v "$TOPOTEST_FRR/tests/topotests:/root/topotests:ro" \ -v "$TOPOTEST_BUILDCACHE:/root/persist" \ -e "TOPOTEST_CLEAN=$TOPOTEST_CLEAN" \ -e "TOPOTEST_VERBOSE=$TOPOTEST_VERBOSE" \ diff --git a/tests/topotests/docker/inner/entrypoint.sh b/tests/topotests/docker/inner/entrypoint.sh index 3050ec86d0..451d0a27d9 100755 --- a/tests/topotests/docker/inner/entrypoint.sh +++ b/tests/topotests/docker/inner/entrypoint.sh @@ -34,6 +34,8 @@ set -e "${CDIR}/compile_frr.sh" "${CDIR}/openvswitch.sh" +cd "${FRR_BUILD_DIR}/tests/topotests" + log_info "Setting permissions on /tmp so we can generate logs" chmod 1777 /tmp @@ -42,7 +44,6 @@ if [ $# -eq 0 ] || ([[ "$1" != /* ]] && [[ "$1" != ./* ]]); then export TOPOTESTS_CHECK_STDERR=Yes set -- pytest \ --junitxml /tmp/topotests.xml \ - -o cache_dir=/tmp \ "$@" fi diff --git a/tests/topotests/lib/bgprib.py b/tests/topotests/lib/bgprib.py index 8ec1511e10..5a81036643 100644 --- a/tests/topotests/lib/bgprib.py +++ b/tests/topotests/lib/bgprib.py @@ -123,7 +123,16 @@ class BgpRib: return luResult(target, True, title, logstr) rib = json.loads(ret) - table = rib['routes'] + try: + table = rib['routes'] + # KeyError: 'routes' probably means missing/bad VRF + except KeyError as err: + if vrf != '': + errstr = '-script ERROR: check if wrong vrf (%s)' % (vrf) + else: + errstr = '-script ERROR: check if vrf missing' + luResult(target, False, title + errstr, logstr) + return for want in wantroutes: if not self.routes_include_wanted(table,want,debug): luResult(target, False, title, logstr) diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index f25b066288..e9b8d34ec3 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -863,7 +863,6 @@ class Router(Node): )) self.waitOutput() logger.debug('{}: {} staticd started'.format(self, self.routertype)) - sleep(1, '{}: waiting for staticd to start'.format(self.name)) # Fix Link-Local Addresses # Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this self.cmd('for i in `ls /sys/class/net/` ; do mac=`cat /sys/class/net/$i/address`; IFS=\':\'; set $mac; unset IFS; ip address add dev $i scope link fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64; done') diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 7052fab01c..e6cc802d08 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -732,15 +732,14 @@ static void vty_show_ip_route_detail_json(struct vty *vty, char buf[BUFSIZ]; json = json_object_new_object(); + json_prefix = json_object_new_array(); RNODE_FOREACH_RE (rn, re) { - json_prefix = json_object_new_array(); vty_show_ip_route(vty, rn, re, json_prefix); - prefix2str(&rn->p, buf, sizeof buf); - json_object_object_add(json, buf, json_prefix); - json_prefix = NULL; } + prefix2str(&rn->p, buf, sizeof(buf)); + json_object_object_add(json, buf, json_prefix); vty_out(vty, "%s\n", json_object_to_json_string_ext( json, JSON_C_TO_STRING_PRETTY)); json_object_free(json); |
