diff options
66 files changed, 1035 insertions, 2680 deletions
diff --git a/.dir-locals.el b/.dir-locals.el index 1332f7b6a2..b2d7cf376d 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -5,6 +5,4 @@ ((c-mode . ((indent-tabs-mode . t) (show-trailing-whitespace . t) (c-basic-offset . 8))) - (json-mode . ((js-indent-level 4))) - (python-mode . ((python-formatter . black) - (python-fill-column . 88)))) + (json-mode . ((js-indent-level 4)))) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index f1c953f21d..eeb0ac5c6a 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -691,6 +691,8 @@ unsigned int attrhash_key_make(const void *p) key = jhash(attr->mp_nexthop_local.s6_addr, IPV6_MAX_BYTELEN, key); MIX3(attr->nh_ifindex, attr->nh_lla_ifindex, attr->distance); MIX(attr->rmap_table_id); + MIX(attr->nh_type); + MIX(attr->bh_type); return key; } @@ -747,7 +749,9 @@ bool attrhash_cmp(const void *p1, const void *p2) && attr1->distance == attr2->distance && srv6_l3vpn_same(attr1->srv6_l3vpn, attr2->srv6_l3vpn) && srv6_vpn_same(attr1->srv6_vpn, attr2->srv6_vpn) - && attr1->srte_color == attr2->srte_color) + && attr1->srte_color == attr2->srte_color + && attr1->nh_type == attr2->nh_type + && attr1->bh_type == attr2->bh_type) return true; } diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index a583581030..6c49cf509f 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -307,6 +307,12 @@ struct attr { /* EVPN DF preference and algorithm for DF election on local ESs */ uint16_t df_pref; uint8_t df_alg; + + /* Nexthop type */ + enum nexthop_types_t nh_type; + + /* If NEXTHOP_TYPE_BLACKHOLE, then blackhole type */ + enum blackhole_type bh_type; }; /* rmap_change_flags definition */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index b164d710a5..db0ee58e72 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -8066,8 +8066,9 @@ DEFPY(aggregate_addressv6, aggregate_addressv6_cmd, void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, const union g_addr *nexthop, ifindex_t ifindex, enum nexthop_types_t nhtype, uint8_t distance, - uint32_t metric, uint8_t type, - unsigned short instance, route_tag_t tag) + enum blackhole_type bhtype, uint32_t metric, + uint8_t type, unsigned short instance, + route_tag_t tag) { struct bgp_path_info *new; struct bgp_path_info *bpi; @@ -8109,8 +8110,10 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; break; } + attr.bh_type = bhtype; break; } + attr.nh_type = nhtype; attr.nh_ifindex = ifindex; attr.med = metric; diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 75da2723e6..d052a3f408 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -642,8 +642,9 @@ extern bool bgp_maximum_prefix_overflow(struct peer *, afi_t, safi_t, int); extern void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, const union g_addr *nexthop, ifindex_t ifindex, enum nexthop_types_t nhtype, uint8_t distance, - uint32_t metric, uint8_t type, - unsigned short instance, route_tag_t tag); + enum blackhole_type bhtype, uint32_t metric, + uint8_t type, unsigned short instance, + route_tag_t tag); extern void bgp_redistribute_delete(struct bgp *, struct prefix *, uint8_t, unsigned short); extern void bgp_redistribute_withdraw(struct bgp *, afi_t, int, unsigned short); diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index 9c2288cba3..3b7ee8b0b6 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -812,6 +812,10 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) } if (peer->default_rmap[afi][safi].name) { + struct bgp_path_info tmp_pi = {0}; + + tmp_pi.peer = bgp->peer_self; + SET_FLAG(bgp->peer_self->rmap_type, PEER_RMAP_TYPE_DEFAULT); /* Iterate over the RIB to see if we can announce @@ -825,24 +829,16 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { - struct attr tmp_attr; - struct bgp_path_info tmp_pi; - struct bgp_path_info_extra tmp_pie; + struct attr tmp_attr = attr; - tmp_attr = *pi->attr; - tmp_attr.aspath = attr.aspath; + tmp_pi.attr = &tmp_attr; - prep_for_rmap_apply(&tmp_pi, &tmp_pie, dest, pi, - pi->peer, &tmp_attr); - - ret = route_map_apply( + ret = route_map_apply_ext( peer->default_rmap[afi][safi].map, - bgp_dest_get_prefix(dest), &tmp_pi); + bgp_dest_get_prefix(dest), pi, &tmp_pi); if (ret == RMAP_DENYMATCH) { - /* The aspath belongs to 'attr' */ - tmp_attr.aspath = NULL; - bgp_attr_flush(&tmp_attr); + bgp_attr_undup(&tmp_attr, &attr); continue; } else { new_attr = bgp_attr_intern(&tmp_attr); @@ -939,6 +935,8 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) subgroup_default_update_packet(subgrp, new_attr, from); } } + + aspath_unintern(&attr.aspath); } /* diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 6e427c0b7c..2f9b8b86cf 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -9974,21 +9974,12 @@ static void bgp_show_failed_summary(struct vty *vty, struct bgp *bgp, } } -/* If the peer's description includes whitespaces - * then return the first occurrence. Also strip description - * to the given size if needed. - */ +/* Strip peer's description to the given size. */ static char *bgp_peer_description_stripped(char *desc, uint32_t size) { static char stripped[BUFSIZ]; - char *pnt; uint32_t len = size > strlen(desc) ? strlen(desc) : size; - pnt = strchr(desc, ' '); - if (pnt) - len = size > (uint32_t)(pnt - desc) ? (uint32_t)(pnt - desc) - : size; - strlcpy(stripped, desc, len + 1); return stripped; @@ -10020,7 +10011,15 @@ static bool bgp_show_summary_is_peer_filtered(struct peer *peer, return false; } -/* Show BGP peer's summary information. */ +/* Show BGP peer's summary information. + * + * Peer's description is stripped according to if `wide` option is given + * or not. + * + * When adding new columns to `show bgp summary` output, please make + * sure `Desc` is the lastest column to show because it can contain + * whitespaces and the whole output will be tricky. + */ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, struct peer *fpeer, int as_type, as_t as, uint16_t show_flags) @@ -10685,6 +10684,9 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, vty_out(vty, " %8u", 0); } + /* Make sure `Desc` column is the lastest in + * the output. + */ if (peer->desc) vty_out(vty, " %s", bgp_peer_description_stripped( diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 5ef49e5108..b0d0c844f3 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -472,8 +472,9 @@ static int bgp_interface_vrf_update(ZAPI_CALLBACK_ARGS) static int zebra_read_route(ZAPI_CALLBACK_ARGS) { enum nexthop_types_t nhtype; + enum blackhole_type bhtype = BLACKHOLE_UNSPEC; struct zapi_route api; - union g_addr nexthop; + union g_addr nexthop = {}; ifindex_t ifindex; int add, i; struct bgp *bgp; @@ -494,10 +495,16 @@ static int zebra_read_route(ZAPI_CALLBACK_ARGS) && IN6_IS_ADDR_LINKLOCAL(&api.prefix.u.prefix6)) return 0; - nexthop = api.nexthops[0].gate; ifindex = api.nexthops[0].ifindex; nhtype = api.nexthops[0].type; + /* api_nh structure has union of gate and bh_type */ + if (nhtype == NEXTHOP_TYPE_BLACKHOLE) { + /* bh_type is only applicable if NEXTHOP_TYPE_BLACKHOLE*/ + bhtype = api.nexthops[0].bh_type; + } else + nexthop = api.nexthops[0].gate; + add = (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD); if (add) { /* @@ -517,8 +524,8 @@ static int zebra_read_route(ZAPI_CALLBACK_ARGS) /* Now perform the add/update. */ bgp_redistribute_add(bgp, &api.prefix, &nexthop, ifindex, - nhtype, api.distance, api.metric, api.type, - api.instance, api.tag); + nhtype, bhtype, api.distance, api.metric, + api.type, api.instance, api.tag); } else { bgp_redistribute_delete(bgp, &api.prefix, api.type, api.instance); @@ -1076,8 +1083,10 @@ static bool update_ipv4nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, * a VRF (which are programmed as onlink on l3-vni SVI) as well as * connected routes leaked into a VRF. */ - if (is_evpn) { - + if (attr->nh_type == NEXTHOP_TYPE_BLACKHOLE) { + api_nh->type = attr->nh_type; + api_nh->bh_type = attr->bh_type; + } else if (is_evpn) { /* * If the nexthop is EVPN overlay index gateway IP, * treat the nexthop as NEXTHOP_TYPE_IPV4 @@ -1090,8 +1099,7 @@ static bool update_ipv4nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; } - } else if (nh_othervrf && - api_nh->gate.ipv4.s_addr == INADDR_ANY) { + } else if (nh_othervrf && api_nh->gate.ipv4.s_addr == INADDR_ANY) { api_nh->type = NEXTHOP_TYPE_IFINDEX; api_nh->ifindex = attr->nh_ifindex; } else @@ -1113,8 +1121,10 @@ static bool update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, attr = pi->attr; api_nh->vrf_id = nh_bgp->vrf_id; - if (is_evpn) { - + if (attr->nh_type == NEXTHOP_TYPE_BLACKHOLE) { + api_nh->type = attr->nh_type; + api_nh->bh_type = attr->bh_type; + } else if (is_evpn) { /* * If the nexthop is EVPN overlay index gateway IP, * treat the nexthop as NEXTHOP_TYPE_IPV4 @@ -1169,7 +1179,8 @@ static bool update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, api_nh->ifindex = 0; } } - if (nexthop) + /* api_nh structure has union of gate and bh_type */ + if (nexthop && api_nh->type != NEXTHOP_TYPE_BLACKHOLE) api_nh->gate.ipv6 = *nexthop; return true; diff --git a/doc/developer/cli.rst b/doc/developer/cli.rst index edabe61d92..9254eb4739 100644 --- a/doc/developer/cli.rst +++ b/doc/developer/cli.rst @@ -139,6 +139,7 @@ by the parser. selector: "<" `selector_seq_seq` ">" `varname_token` : "{" `selector_seq_seq` "}" `varname_token` : "[" `selector_seq_seq` "]" `varname_token` + : "![" `selector_seq_seq` "]" `varname_token` selector_seq_seq: `selector_seq_seq` "|" `selector_token_seq` : `selector_token_seq` selector_token_seq: `selector_token_seq` `selector_token` @@ -218,6 +219,10 @@ one-or-more selection and repetition. provide mutual exclusion. User input matches at most one option. - ``[square brackets]`` -- Contains sequences of tokens that can be omitted. ``[<a|b>]`` can be shortened to ``[a|b]``. +- ``![exclamation square brackets]`` -- same as ``[square brackets]``, but + only allow skipping the contents if the command input starts with ``no``. + (For cases where the positive command needs a parameter, but the parameter + is optional for the negative case.) - ``{curly|braces}`` -- similar to angle brackets, but instead of mutual exclusion, curly braces indicate that one or more of the pipe-separated sequences may be provided in any order. @@ -767,6 +772,172 @@ User input: ``ip`` partially matches ``ipv6`` but exactly matches ``ip``, so ``ip`` will win. +Adding a CLI Node +----------------- + +To add a new CLI node, you should: + +- define a new numerical node constant +- define a node structure in the relevant daemon +- call ``install_node()`` in the relevant daemon +- define and install the new node in vtysh +- define corresponding node entry commands in daemon and vtysh +- add a new entry to the ``ctx_keywords`` dictionary in ``tools/frr-reload.py`` + +Defining the numerical node constant +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Add your new node value to the enum before ``NODE_TYPE_MAX`` in +``lib/command.h``: + +.. code-block:: c + + enum node_type { + AUTH_NODE, // Authentication mode of vty interface. + VIEW_NODE, // View node. Default mode of vty interface. + [...] + MY_NEW_NODE, + NODE_TYPE_MAX, // maximum + }; + +Defining a node structure +^^^^^^^^^^^^^^^^^^^^^^^^^ +In your daemon-specific code where you define your new commands that +attach to the new node, add a node definition: + +.. code-block:: c + + static struct cmd_node my_new_node = { + .name = "my new node name", + .node = MY_NEW_NODE, // enum node_type lib/command.h + .parent_node = CONFIG_NODE, + .prompt = "%s(my-new-node-prompt)# ", + .config_write = my_new_node_config_write, + }; + +You will need to define ``my_new_node_config_write(struct vty \*vty)`` +(or omit this field if you have no relevant configuration to save). + +Calling ``install_node()`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ +In the daemon's initialization function, before installing your new commands +with ``install_element()``, add a call ``install_node(&my_new_node)``. + +Defining and installing the new node in vtysh +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The build tools automatically collect command definitions for vtysh. +However, new nodes must be coded in vtysh specifically. + +In ``vtysh/vtysh.c``, define a stripped-down node structure and +call ``install_node()``: + +.. code-block:: c + + static struct cmd_node my_new_node = { + .name = "my new node name", + .node = MY_NEW_NODE, /* enum node_type lib/command.h */ + .parent_node = CONFIG_NODE, + .prompt = "%s(my-new-node-prompt)# ", + }; + [...] + void vtysh_init_vty(void) + { + [...] + install_node(&my_new_node) + [...] + } + +Defining corresponding node entry commands in daemon and vtysh +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The command that descends into the new node is typically programmed +with ``VTY_PUSH_CONTEXT`` or equivalent in the daemon's CLI handler function. +(If the CLI has been updated to use the new northbound architecture, +``VTY_PUSH_XPATH`` is used instead.) + +In vtysh, you must implement a corresponding node change so that vtysh +tracks the daemon's movement through the node tree. + +Although the build tools typically scan daemon code for CLI definitions +to replicate their parsing in vtysh, the node-descent function in the +daemon must be blocked from this replication so that a hand-coded +skeleton can be written in ``vtysh.c``. + +Accordingly, use one of the ``*_NOSH`` macros such as ``DEFUN_NOSH``, +``DEFPY_NOSH``, or ``DEFUN_YANG_NOSH`` for the daemon's node-descent +CLI definition, and use ``DEFUNSH`` in ``vtysh.c`` for the vtysh equivalent. + +.. seealso:: :ref:`vtysh-special-defuns` + +Examples: + +``zebra_whatever.c`` + +.. code-block:: c + + DEFPY_NOSH(my_new_node, + my_new_node_cmd, + "my-new-node foo", + "New Thing\n" + "A foo\n") + { + [...] + VTY_PUSH_CONTEXT(MY_NEW_NODE, bar); + [...] + } + + +``ripd_whatever.c`` + +.. code-block:: c + + DEFPY_YANG_NOSH(my_new_node, + my_new_node_cmd, + "my-new-node foo", + "New Thing\n" + "A foo\n") + { + [...] + VTY_PUSH_XPATH(MY_NEW_NODE, xbar); + [...] + } + + +``vtysh.c`` + +.. code-block:: c + + DEFUNSH(VTYSH_ZEBRA, my_new_node, + my_new_node_cmd, + "my-new-node foo", + "New Thing\n" + "A foo\n") + { + vty->node = MY_NEW_NODE; + return CMD_SUCCESS; + } + [...] + install_element(CONFIG_NODE, &my_new_node_cmd); + + +Adding a new entry to the ``ctx_keywords`` dictionary +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In file ``tools/frr-reload.py``, the ``ctx_keywords`` dictionary +describes the various node relationships. +Add a new node entry at the appropriate level in this dictionary. + +.. code-block:: python + + ctx_keywords = { + [...] + "key chain ": { + "key ": {} + }, + [...] + "my-new-node": {}, + [...] + } + + + Inspection & Debugging ---------------------- diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index fa6a1ba660..c52d210ee5 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -983,22 +983,20 @@ Writing Tests """"""""""""" Test topologies should always be bootstrapped from -:file:`tests/topotests/example-test/test_template.py` because it contains +:file:`tests/topotests/example_test/test_template.py` because it contains important boilerplate code that can't be avoided, like: Example: .. code:: py - # For all registered routers, load the zebra configuration file - CWD = os.path.dirname(os.path.realpath(__file__)) - for rname, router in router_list.items(): - router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)) - ) - # os.path.join() joins the CWD string with arguments adding the necessary - # slashes ('/'). Arguments must not begin with '/'. + # For all routers arrange for: + # - starting zebra using config file from <rtrname>/zebra.conf + # - starting ospfd using an empty config file. + for rname, router in router_list.items(): + router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + router.load_config(TopoRouter.RD_OSPF) + - The topology definition or build function @@ -1013,27 +1011,31 @@ Example: # topology build code ... -- pytest ``setup_module()`` and ``teardown_module()`` to start the topology +- pytest setup/teardown fixture to start the topology and supply `tgen` argument + to tests. .. code:: py - def setup_module(module): + + @pytest.fixture(scope="module") + def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + tgen = Topogen(topodef, module.__name__) # or tgen = Topogen(build_topo, module.__name__) - tgen.start_topology('debug') + ... - def teardown_module(_m): - tgen = get_topogen() - tgen.stop_topology() + # Start and configure the router daemons + tgen.start_router() -- ``__main__`` initialization code (to support running the script directly) + # Provide tgen as argument to each test function + yield tgen -.. code:: py + # Teardown after last test runs + tgen.stop_topology() - if __name__ == '__main__': - sys.exit(pytest.main(["-s"])) Requirements: diff --git a/eigrpd/eigrp_northbound.c b/eigrpd/eigrp_northbound.c index 482667f633..3ad711164b 100644 --- a/eigrpd/eigrp_northbound.c +++ b/eigrpd/eigrp_northbound.c @@ -79,6 +79,7 @@ static int eigrpd_instance_create(struct nb_cb_create_args *args) { struct eigrp *eigrp; const char *vrf; + struct vrf *pVrf; vrf_id_t vrfid; switch (args->event) { @@ -87,7 +88,12 @@ static int eigrpd_instance_create(struct nb_cb_create_args *args) break; case NB_EV_PREPARE: vrf = yang_dnode_get_string(args->dnode, "./vrf"); - vrfid = vrf_name_to_id(vrf); + + pVrf = vrf_lookup_by_name(vrf); + if (pVrf) + vrfid = pVrf->vrf_id; + else + vrfid = VRF_DEFAULT; eigrp = eigrp_get(yang_dnode_get_uint16(args->dnode, "./asn"), vrfid); @@ -719,12 +725,19 @@ static int eigrpd_instance_redistribute_create(struct nb_cb_create_args *args) struct eigrp *eigrp; uint32_t proto; vrf_id_t vrfid; + struct vrf *pVrf; switch (args->event) { case NB_EV_VALIDATE: proto = yang_dnode_get_enum(args->dnode, "./protocol"); vrfname = yang_dnode_get_string(args->dnode, "../vrf"); - vrfid = vrf_name_to_id(vrfname); + + pVrf = vrf_lookup_by_name(vrfname); + if (pVrf) + vrfid = pVrf->vrf_id; + else + vrfid = VRF_DEFAULT; + if (vrf_bitmap_check(zclient->redist[AFI_IP][proto], vrfid)) return NB_ERR_INCONSISTENCY; break; diff --git a/isisd/isis_snmp.c b/isisd/isis_snmp.c index d530faa151..c530eb9169 100644 --- a/isisd/isis_snmp.c +++ b/isisd/isis_snmp.c @@ -283,13 +283,6 @@ SNMP_LOCAL_VARIABLES * * 2. I could be replaced in unit test environment */ -#ifndef ISIS_SNMP_HAVE_TIME_FUNC -static uint32_t isis_snmp_time(void) -{ - return (uint32_t)time(NULL); -} - -#endif /* ISIS-MIB instances. */ static oid isis_oid[] = {ISIS_MIB}; @@ -2083,7 +2076,7 @@ static uint8_t *isis_snmp_find_circ(struct variable *v, oid *name, struct isis_circuit *circuit; uint32_t up_ticks; uint32_t delta_ticks; - uint32_t now_time; + time_t now_time; int res; *write_method = NULL; @@ -2191,7 +2184,7 @@ static uint8_t *isis_snmp_find_circ(struct variable *v, oid *name, return SNMP_INTEGER(0); up_ticks = (uint32_t)netsnmp_get_agent_uptime(); - now_time = isis_snmp_time(); + now_time = time(NULL); if (circuit->last_uptime >= now_time) return SNMP_INTEGER(up_ticks); @@ -2501,11 +2494,11 @@ static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name, oid *oid_idx; size_t oid_idx_len; int res; - uint32_t val; + time_t val; struct isis_adjacency *adj; uint32_t up_ticks; uint32_t delta_ticks; - uint32_t now_time; + time_t now_time; *write_method = NULL; @@ -2577,7 +2570,7 @@ static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name, * It seems that we want remaining timer */ if (adj->last_upd != 0) { - val = isis_snmp_time(); + val = time(NULL); if (val < (adj->last_upd + adj->hold_time)) return SNMP_INTEGER(adj->last_upd + adj->hold_time - val); @@ -2594,7 +2587,7 @@ static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name, up_ticks = (uint32_t)netsnmp_get_agent_uptime(); - now_time = isis_snmp_time(); + now_time = time(NULL); if (adj->last_flap >= now_time) return SNMP_INTEGER(up_ticks); @@ -2853,7 +2846,7 @@ static int isis_snmp_trap_throttle(oid trap_id) if (isis == NULL || !isis->snmp_notifications || !smux_enabled()) return 0; - time_now = isis_snmp_time(); + time_now = time(NULL); if ((isis_snmp_trap_timestamp[trap_id] + 5) > time_now) /* Throttle trap rate at 1 in 5 secs */ diff --git a/lib/command.c b/lib/command.c index fcaf466c65..53aa064705 100644 --- a/lib/command.c +++ b/lib/command.c @@ -74,6 +74,7 @@ const struct message tokennames[] = { item(JOIN_TKN), item(START_TKN), item(END_TKN), + item(NEG_ONLY_TKN), {0}, }; /* clang-format on */ diff --git a/lib/command_graph.c b/lib/command_graph.c index c6c3840455..15c8302e63 100644 --- a/lib/command_graph.c +++ b/lib/command_graph.c @@ -388,6 +388,7 @@ static void cmd_node_names(struct graph_node *gn, struct graph_node *join, case START_TKN: case JOIN_TKN: + case NEG_ONLY_TKN: /* "<foo|bar> WORD" -> word is not "bar" or "foo" */ prevname = NULL; break; @@ -511,6 +512,9 @@ void cmd_graph_node_print_cb(struct graph_node *gn, struct buffer *buf) case JOIN_TKN: color = "#ddaaff"; break; + case NEG_ONLY_TKN: + color = "#ffddaa"; + break; case WORD_TKN: color = "#ffffff"; break; diff --git a/lib/command_graph.h b/lib/command_graph.h index 2754dca67d..c20c9874c2 100644 --- a/lib/command_graph.h +++ b/lib/command_graph.h @@ -64,6 +64,7 @@ enum cmd_token_type { JOIN_TKN, // marks subgraph end START_TKN, // first token in line END_TKN, // last token in line + NEG_ONLY_TKN, // filter token, match if "no ..." command SPECIAL_TKN = FORK_TKN, }; diff --git a/lib/command_lex.l b/lib/command_lex.l index 9c096995f5..ec366ce7e1 100644 --- a/lib/command_lex.l +++ b/lib/command_lex.l @@ -82,6 +82,7 @@ RANGE \({NUMBER}[ ]?\-[ ]?{NUMBER}\) {VARIABLE} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return VARIABLE;} {WORD} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return WORD;} {RANGE} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return RANGE;} +!\[ {yylval->string = NULL; return EXCL_BRACKET;} . {return yytext[0];} %% diff --git a/lib/command_match.c b/lib/command_match.c index 5703510148..56a7ae422b 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -42,7 +42,7 @@ DEFINE_MTYPE_STATIC(LIB, CMD_MATCHSTACK, "Command Match Stack"); /* matcher helper prototypes */ static int add_nexthops(struct list *, struct graph_node *, - struct graph_node **, size_t); + struct graph_node **, size_t, bool); static enum matcher_rv command_match_r(struct graph_node *, vector, unsigned int, struct graph_node **, @@ -79,6 +79,13 @@ static enum match_type match_variable(struct cmd_token *, const char *); static enum match_type match_mac(const char *, bool); +static bool is_neg(vector vline, size_t idx) +{ + if (idx >= vector_active(vline)) + return false; + return !strcmp(vector_slot(vline, idx), "no"); +} + enum matcher_rv command_match(struct graph *cmdgraph, vector vline, struct list **argv, const struct cmd_element **el) { @@ -248,7 +255,7 @@ static enum matcher_rv command_match_r(struct graph_node *start, vector vline, // get all possible nexthops struct list *next = list_new(); - add_nexthops(next, start, NULL, 0); + add_nexthops(next, start, NULL, 0, is_neg(vline, 1)); // determine the best match for (ALL_LIST_ELEMENTS_RO(next, ln, gn)) { @@ -349,6 +356,7 @@ enum matcher_rv command_complete(struct graph *graph, vector vline, { // pointer to next input token to match char *input_token; + bool neg = is_neg(vline, 0); struct list * current = @@ -363,7 +371,7 @@ enum matcher_rv command_complete(struct graph *graph, vector vline, // add all children of start node to list struct graph_node *start = vector_slot(graph->nodes, 0); - add_nexthops(next, start, &start, 0); + add_nexthops(next, start, &start, 0, neg); unsigned int idx; for (idx = 0; idx < vector_active(vline) && next->count > 0; idx++) { @@ -428,7 +436,7 @@ enum matcher_rv command_complete(struct graph *graph, vector vline, listnode_add(next, newstack); } else if (matchtype >= minmatch) add_nexthops(next, gstack[0], gstack, - idx + 1); + idx + 1, neg); break; default: trace_matcher("no_match\n"); @@ -478,7 +486,7 @@ enum matcher_rv command_complete(struct graph *graph, vector vline, * output, instead of direct node pointers! */ static int add_nexthops(struct list *list, struct graph_node *node, - struct graph_node **stack, size_t stackpos) + struct graph_node **stack, size_t stackpos, bool neg) { int added = 0; struct graph_node *child; @@ -494,8 +502,13 @@ static int add_nexthops(struct list *list, struct graph_node *node, if (j != stackpos) continue; } + + if (token->type == NEG_ONLY_TKN && !neg) + continue; + if (token->type >= SPECIAL_TKN && token->type != END_TKN) { - added += add_nexthops(list, child, stack, stackpos); + added += + add_nexthops(list, child, stack, stackpos, neg); } else { if (stack) { nextstack = XMALLOC( diff --git a/lib/command_parse.y b/lib/command_parse.y index f5e42cc304..3e2cdc79af 100644 --- a/lib/command_parse.y +++ b/lib/command_parse.y @@ -105,6 +105,9 @@ %token <string> MAC %token <string> MAC_PREFIX +/* special syntax, value is irrelevant */ +%token <string> EXCL_BRACKET + /* union types for parsed rules */ %type <node> start %type <node> literal_token @@ -372,6 +375,19 @@ selector: '[' selector_seq_seq ']' varname_token } ; +/* ![option] productions */ +selector: EXCL_BRACKET selector_seq_seq ']' varname_token +{ + struct graph_node *neg_only = new_token_node (ctx, NEG_ONLY_TKN, NULL, NULL); + + $$ = $2; + graph_add_edge ($$.start, neg_only); + graph_add_edge (neg_only, $$.end); + cmd_token_varname_set ($2.end->data, $4); + XFREE (MTYPE_LEX, $4); +} +; + %% #undef scanner diff --git a/lib/command_py.c b/lib/command_py.c index 7f19008fbf..90344ae1e5 100644 --- a/lib/command_py.c +++ b/lib/command_py.c @@ -197,21 +197,30 @@ static PyObject *graph_to_pyobj(struct wrap_graph *wgraph, if (gn->data) { struct cmd_token *tok = gn->data; switch (tok->type) { -#define item(x) case x: wrap->type = #x; break; - item(WORD_TKN) // words - item(VARIABLE_TKN) // almost anything - item(RANGE_TKN) // integer range - item(IPV4_TKN) // IPV4 addresses - item(IPV4_PREFIX_TKN) // IPV4 network prefixes - item(IPV6_TKN) // IPV6 prefixes - item(IPV6_PREFIX_TKN) // IPV6 network prefixes - item(MAC_TKN) // MAC address - item(MAC_PREFIX_TKN) // MAC address with mask - - /* plumbing types */ - item(FORK_TKN) item(JOIN_TKN) item(START_TKN) - item(END_TKN) default - : wrap->type = "???"; +#define item(x) \ + case x: \ + wrap->type = #x; \ + break /* no semicolon */ + + item(WORD_TKN); // words + item(VARIABLE_TKN); // almost anything + item(RANGE_TKN); // integer range + item(IPV4_TKN); // IPV4 addresses + item(IPV4_PREFIX_TKN); // IPV4 network prefixes + item(IPV6_TKN); // IPV6 prefixes + item(IPV6_PREFIX_TKN); // IPV6 network prefixes + item(MAC_TKN); // MAC address + item(MAC_PREFIX_TKN); // MAC address with mask + + /* plumbing types */ + item(FORK_TKN); + item(JOIN_TKN); + item(START_TKN); + item(END_TKN); + item(NEG_ONLY_TKN); +#undef item + default: + wrap->type = "???"; } wrap->deprecated = (tok->attr == CMD_ATTR_DEPRECATED); diff --git a/lib/nexthop.c b/lib/nexthop.c index 23e3a2b733..98409c76c5 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -519,12 +519,13 @@ struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6, return nexthop; } -struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type) +struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type, + vrf_id_t nh_vrf_id) { struct nexthop *nexthop; nexthop = nexthop_new(); - nexthop->vrf_id = VRF_DEFAULT; + nexthop->vrf_id = nh_vrf_id; nexthop->type = NEXTHOP_TYPE_BLACKHOLE; nexthop->bh_type = bh_type; diff --git a/lib/nexthop.h b/lib/nexthop.h index dd65509aec..320b46315e 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -182,7 +182,8 @@ struct nexthop *nexthop_from_ipv6(const struct in6_addr *ipv6, vrf_id_t vrf_id); struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6, ifindex_t ifindex, vrf_id_t vrf_id); -struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type); +struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type, + vrf_id_t nh_vrf_id); /* * Hash a nexthop. Suitable for use with hash tables. diff --git a/lib/routemap.c b/lib/routemap.c index 594dcf97cb..5c60b7d1c6 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -2488,8 +2488,9 @@ void route_map_notify_pentry_dependencies(const char *affected_name, We need to make sure our route-map processing matches the above */ -route_map_result_t route_map_apply(struct route_map *map, - const struct prefix *prefix, void *object) +route_map_result_t route_map_apply_ext(struct route_map *map, + const struct prefix *prefix, + void *match_object, void *set_object) { static int recursion = 0; enum route_map_cmd_result_t match_ret = RMAP_NOMATCH; @@ -2516,7 +2517,7 @@ route_map_result_t route_map_apply(struct route_map *map, if ((!map->optimization_disabled) && (map->ipv4_prefix_table || map->ipv6_prefix_table)) { - index = route_map_get_index(map, prefix, object, + index = route_map_get_index(map, prefix, match_object, (uint8_t *)&match_ret); if (index) { index->applied++; @@ -2551,7 +2552,7 @@ route_map_result_t route_map_apply(struct route_map *map, index->applied++; /* Apply this index. */ match_ret = route_map_apply_match(&index->match_list, - prefix, object); + prefix, match_object); if (rmap_debug) { zlog_debug( "Route-map: %s, sequence: %d, prefix: %pFX, result: %s", @@ -2610,7 +2611,7 @@ route_map_result_t route_map_apply(struct route_map *map, * return code. */ (void)(*set->cmd->func_apply)( - set->value, prefix, object); + set->value, prefix, set_object); /* Call another route-map if available */ if (index->nextrm) { @@ -2622,8 +2623,10 @@ route_map_result_t route_map_apply(struct route_map *map, jump to it */ { recursion++; - ret = route_map_apply( - nextrm, prefix, object); + ret = route_map_apply_ext( + nextrm, prefix, + match_object, + set_object); recursion--; } diff --git a/lib/routemap.h b/lib/routemap.h index b356dbf52e..2c8eb24537 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -443,9 +443,12 @@ extern struct route_map *route_map_lookup_by_name(const char *name); struct route_map *route_map_lookup_warn_noexist(struct vty *vty, const char *name); /* Apply route map to the object. */ -extern route_map_result_t route_map_apply(struct route_map *map, - const struct prefix *prefix, - void *object); +extern route_map_result_t route_map_apply_ext(struct route_map *map, + const struct prefix *prefix, + void *match_object, + void *set_object); +#define route_map_apply(map, prefix, object) \ + route_map_apply_ext(map, prefix, object, object) extern void route_map_add_hook(void (*func)(const char *)); extern void route_map_delete_hook(void (*func)(const char *)); @@ -384,21 +384,6 @@ const char *vrf_id_to_name(vrf_id_t vrf_id) return VRF_LOGNAME(vrf); } -vrf_id_t vrf_name_to_id(const char *name) -{ - struct vrf *vrf; - vrf_id_t vrf_id = VRF_DEFAULT; // Pending: need a way to return invalid - // id/ routine not used. - - if (!name) - return vrf_id; - vrf = vrf_lookup_by_name(name); - if (vrf) - vrf_id = vrf->vrf_id; - - return vrf_id; -} - /* Get the data pointer of the specified VRF. If not found, create one. */ void *vrf_info_get(vrf_id_t vrf_id) { @@ -119,7 +119,6 @@ extern struct vrf *vrf_lookup_by_name(const char *); extern struct vrf *vrf_get(vrf_id_t, const char *); extern struct vrf *vrf_update(vrf_id_t new_vrf_id, const char *name); extern const char *vrf_id_to_name(vrf_id_t vrf_id); -extern vrf_id_t vrf_name_to_id(const char *); #define VRF_LOGNAME(V) V ? V->name : "Unknown" diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index aa85475678..fe1845907f 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -1201,9 +1201,16 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) __func__, &prefix, listcount(old->paths)); } for (old_route = old; old_route; old_route = old_route->next) { - if (!ospf6_route_is_same(old_route, route) || - (old_route->type != route->type) || - (old_route->path.type != route->path.type)) + + /* The route linked-list is grouped in batches of prefix. + * If the new prefix is not the same as the one of interest + * then we have walked over the end of the batch and so we + * should break rather than continuing unnecessarily. + */ + if (!ospf6_route_is_same(old_route, route)) + break; + if ((old_route->type != route->type) + || (old_route->path.type != route->path.type)) continue; if ((ospf6_route_cmp(route, old_route) != 0)) { diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h index b2a275d745..aed9589bfb 100644 --- a/ospf6d/ospf6_area.h +++ b/ospf6d/ospf6_area.h @@ -149,16 +149,18 @@ extern void area_id2str(char *buf, int len, uint32_t area_id, int area_id_fmt); extern int ospf6_area_cmp(void *va, void *vb); -extern struct ospf6_area *ospf6_area_create(uint32_t, struct ospf6 *, int); -extern void ospf6_area_delete(struct ospf6_area *); -extern struct ospf6_area *ospf6_area_lookup(uint32_t, struct ospf6 *); +extern struct ospf6_area *ospf6_area_create(uint32_t area_id, + struct ospf6 *ospf6, int df); +extern void ospf6_area_delete(struct ospf6_area *oa); +extern struct ospf6_area *ospf6_area_lookup(uint32_t area_id, + struct ospf6 *ospf6); extern struct ospf6_area *ospf6_area_lookup_by_area_id(uint32_t area_id); extern void ospf6_area_stub_unset(struct ospf6 *ospf6, struct ospf6_area *area); -extern void ospf6_area_enable(struct ospf6_area *); -extern void ospf6_area_disable(struct ospf6_area *); +extern void ospf6_area_enable(struct ospf6_area *oa); +extern void ospf6_area_disable(struct ospf6_area *oa); -extern void ospf6_area_show(struct vty *, struct ospf6_area *, +extern void ospf6_area_show(struct vty *vty, struct ospf6_area *oa, json_object *json_areas, bool use_json); extern void ospf6_area_plist_update(struct prefix_list *plist, int add); diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index b5bf81c213..90c9fdf23a 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -262,8 +262,14 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, next_route = old_route->next; - if (!ospf6_route_is_same(old_route, route) - || (old_route->path.type != route->path.type)) + /* The route linked-list is grouped in batches of prefix. + * If the new prefix is not the same as the one of interest + * then we have walked over the end of the batch and so we + * should break rather than continuing unnecessarily. + */ + if (!ospf6_route_is_same(old_route, route)) + break; + if (old_route->path.type != route->path.type) continue; /* Current and New route has same origin, @@ -367,11 +373,14 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, /* Add new route */ for (old_route = old; old_route; old_route = old_route->next) { - /* Current and New Route prefix or route type - * is not same skip this current node. + /* The route linked-list is grouped in batches of prefix. + * If the new prefix is not the same as the one of interest + * then we have walked over the end of the batch and so we + * should break rather than continuing unnecessarily. */ - if (!ospf6_route_is_same(old_route, route) - || (old_route->path.type != route->path.type)) + if (!ospf6_route_is_same(old_route, route)) + break; + if (old_route->path.type != route->path.type) continue; /* Old Route and New Route have Equal Cost, Merge NHs */ diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index ccdf8b1c8f..ee24b989bd 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -193,23 +193,23 @@ extern void ospf6_interface_stop(struct ospf6_interface *oi); extern struct ospf6_interface * ospf6_interface_lookup_by_ifindex(ifindex_t, vrf_id_t vrf_id); -extern struct ospf6_interface *ospf6_interface_create(struct interface *); -extern void ospf6_interface_delete(struct ospf6_interface *); +extern struct ospf6_interface *ospf6_interface_create(struct interface *ifp); +extern void ospf6_interface_delete(struct ospf6_interface *oi); -extern void ospf6_interface_enable(struct ospf6_interface *); -extern void ospf6_interface_disable(struct ospf6_interface *); +extern void ospf6_interface_enable(struct ospf6_interface *oi); +extern void ospf6_interface_disable(struct ospf6_interface *oi); -extern void ospf6_interface_state_update(struct interface *); -extern void ospf6_interface_connected_route_update(struct interface *); +extern void ospf6_interface_state_update(struct interface *ifp); +extern void ospf6_interface_connected_route_update(struct interface *ifp); extern struct in6_addr * ospf6_interface_get_global_address(struct interface *ifp); /* interface event */ -extern int interface_up(struct thread *); -extern int interface_down(struct thread *); -extern int wait_timer(struct thread *); -extern int backup_seen(struct thread *); -extern int neighbor_change(struct thread *); +extern int interface_up(struct thread *thread); +extern int interface_down(struct thread *thread); +extern int wait_timer(struct thread *thread); +extern int backup_seen(struct thread *thread); +extern int neighbor_change(struct thread *thread); extern void ospf6_interface_init(void); extern void ospf6_interface_clear(struct interface *ifp); diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index e4db8f3a02..06a950156b 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -1470,8 +1470,14 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, for (old_route = old; old_route; old_route = old_route->next) { bool route_updated = false; - if (!ospf6_route_is_same(old_route, route) || - (old_route->path.type != route->path.type)) + /* The route linked-list is grouped in batches of prefix. + * If the new prefix is not the same as the one of interest + * then we have walked over the end of the batch and so we + * should break rather than continuing unnecessarily. + */ + if (!ospf6_route_is_same(old_route, route)) + break; + if (old_route->path.type != route->path.type) continue; /* Current and New route has same origin, @@ -1569,8 +1575,14 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, for (old_route = old; old_route; old_route = old_route->next) { - if (!ospf6_route_is_same(old_route, route) || - (old_route->path.type != route->path.type)) + /* The route linked-list is grouped in batches of prefix. + * If the new prefix is not the same as the one of interest + * then we have walked over the end of the batch and so we + * should break rather than continuing unnecessarily. + */ + if (!ospf6_route_is_same(old_route, route)) + break; + if (old_route->path.type != route->path.type) continue; /* Old Route and New Route have Equal Cost, Merge NHs */ diff --git a/ospf6d/ospf6_intra.h b/ospf6d/ospf6_intra.h index 9c29681dee..4031ffe689 100644 --- a/ospf6d/ospf6_intra.h +++ b/ospf6d/ospf6_intra.h @@ -221,11 +221,11 @@ extern char *ospf6_network_lsdesc_lookup(uint32_t router_id, struct ospf6_lsa *lsa); extern int ospf6_router_is_stub_router(struct ospf6_lsa *lsa); -extern int ospf6_router_lsa_originate(struct thread *); -extern int ospf6_network_lsa_originate(struct thread *); -extern int ospf6_link_lsa_originate(struct thread *); -extern int ospf6_intra_prefix_lsa_originate_transit(struct thread *); -extern int ospf6_intra_prefix_lsa_originate_stub(struct thread *); +extern int ospf6_router_lsa_originate(struct thread *thread); +extern int ospf6_network_lsa_originate(struct thread *thread); +extern int ospf6_link_lsa_originate(struct thread *thread); +extern int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread); +extern int ospf6_intra_prefix_lsa_originate_stub(struct thread *thread); extern void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa); extern void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa); extern int ospf6_orig_as_external_lsa(struct thread *thread); diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h index a8ed9132dd..c271965fe7 100644 --- a/ospf6d/ospf6_lsa.h +++ b/ospf6d/ospf6_lsa.h @@ -231,10 +231,11 @@ extern int metric_type(struct ospf6 *ospf6, int type, uint8_t instance); extern int metric_value(struct ospf6 *ospf6, int type, uint8_t instance); extern int ospf6_lsa_is_differ(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2); extern int ospf6_lsa_is_changed(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2); -extern uint16_t ospf6_lsa_age_current(struct ospf6_lsa *); -extern void ospf6_lsa_age_update_to_send(struct ospf6_lsa *, uint32_t); -extern void ospf6_lsa_premature_aging(struct ospf6_lsa *); -extern int ospf6_lsa_compare(struct ospf6_lsa *, struct ospf6_lsa *); +extern uint16_t ospf6_lsa_age_current(struct ospf6_lsa *lsa); +extern void ospf6_lsa_age_update_to_send(struct ospf6_lsa *lsa, + uint32_t transdelay); +extern void ospf6_lsa_premature_aging(struct ospf6_lsa *lsa); +extern int ospf6_lsa_compare(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2); extern char *ospf6_lsa_printbuf(struct ospf6_lsa *lsa, char *buf, int size); extern void ospf6_lsa_header_print_raw(struct ospf6_lsa_header *header); @@ -254,16 +255,16 @@ extern struct ospf6_lsa *ospf6_lsa_create(struct ospf6_lsa_header *header); extern struct ospf6_lsa * ospf6_lsa_create_headeronly(struct ospf6_lsa_header *header); extern void ospf6_lsa_delete(struct ospf6_lsa *lsa); -extern struct ospf6_lsa *ospf6_lsa_copy(struct ospf6_lsa *); +extern struct ospf6_lsa *ospf6_lsa_copy(struct ospf6_lsa *lsa); extern struct ospf6_lsa *ospf6_lsa_lock(struct ospf6_lsa *lsa); extern struct ospf6_lsa *ospf6_lsa_unlock(struct ospf6_lsa *lsa); -extern int ospf6_lsa_expire(struct thread *); -extern int ospf6_lsa_refresh(struct thread *); +extern int ospf6_lsa_expire(struct thread *thread); +extern int ospf6_lsa_refresh(struct thread *thread); -extern unsigned short ospf6_lsa_checksum(struct ospf6_lsa_header *); -extern int ospf6_lsa_checksum_valid(struct ospf6_lsa_header *); +extern unsigned short ospf6_lsa_checksum(struct ospf6_lsa_header *lsah); +extern int ospf6_lsa_checksum_valid(struct ospf6_lsa_header *lsah); extern int ospf6_lsa_prohibited_duration(uint16_t type, uint32_t id, uint32_t adv_router, void *scope); diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index a229897226..30e044da4b 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -183,24 +183,24 @@ extern const char *const ospf6_neighbor_state_str[]; int ospf6_neighbor_cmp(void *va, void *vb); void ospf6_neighbor_dbex_init(struct ospf6_neighbor *on); -struct ospf6_neighbor *ospf6_neighbor_lookup(uint32_t, - struct ospf6_interface *); -struct ospf6_neighbor *ospf6_neighbor_create(uint32_t, - struct ospf6_interface *); -void ospf6_neighbor_delete(struct ospf6_neighbor *); +struct ospf6_neighbor *ospf6_neighbor_lookup(uint32_t router_id, + struct ospf6_interface *oi); +struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id, + struct ospf6_interface *oi); +void ospf6_neighbor_delete(struct ospf6_neighbor *on); /* Neighbor event */ -extern int hello_received(struct thread *); -extern int twoway_received(struct thread *); -extern int negotiation_done(struct thread *); -extern int exchange_done(struct thread *); -extern int loading_done(struct thread *); -extern int adj_ok(struct thread *); -extern int seqnumber_mismatch(struct thread *); -extern int bad_lsreq(struct thread *); -extern int oneway_received(struct thread *); -extern int inactivity_timer(struct thread *); -extern void ospf6_check_nbr_loading(struct ospf6_neighbor *); +extern int hello_received(struct thread *thread); +extern int twoway_received(struct thread *thread); +extern int negotiation_done(struct thread *thread); +extern int exchange_done(struct thread *thread); +extern int loading_done(struct thread *thread); +extern int adj_ok(struct thread *thread); +extern int seqnumber_mismatch(struct thread *thread); +extern int bad_lsreq(struct thread *thread); +extern int oneway_received(struct thread *thread); +extern int inactivity_timer(struct thread *thread); +extern void ospf6_check_nbr_loading(struct ospf6_neighbor *on); extern void ospf6_neighbor_init(void); extern int config_write_ospf6_debug_neighbor(struct vty *vty); diff --git a/ospf6d/ospf6_nssa.h b/ospf6d/ospf6_nssa.h index 454bdd7fe2..631503edc6 100644 --- a/ospf6d/ospf6_nssa.h +++ b/ospf6d/ospf6_nssa.h @@ -52,11 +52,11 @@ int ospf6_area_nssa_unset(struct ospf6 *ospf6, struct ospf6_area *area); int ospf6_area_nssa_set(struct ospf6 *ospf6, struct ospf6_area *area); extern void ospf6_nssa_lsa_flush(struct ospf6 *ospf6, struct prefix_ipv6 *p); -extern struct ospf6_lsa *ospf6_translated_nssa_refresh(struct ospf6_area *, - struct ospf6_lsa *, - struct ospf6_lsa *); -extern struct ospf6_lsa *ospf6_translated_nssa_originate(struct ospf6_area *, - struct ospf6_lsa *); +extern struct ospf6_lsa *ospf6_translated_nssa_refresh(struct ospf6_area *oa, + struct ospf6_lsa *type7, + struct ospf6_lsa *type5); +extern struct ospf6_lsa * +ospf6_translated_nssa_originate(struct ospf6_area *oa, struct ospf6_lsa *type7); extern void ospf6_asbr_nssa_redist_task(struct ospf6 *ospf6); diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h index 991720ec2e..54baaee638 100644 --- a/ospf6d/ospf6_route.h +++ b/ospf6d/ospf6_route.h @@ -343,7 +343,7 @@ extern int ospf6_route_get_first_nh_index(struct ospf6_route *route); ospf6_add_nexthop(route->nh_list, ifindex, addr) extern struct ospf6_route *ospf6_route_create(struct ospf6 *ospf6); -extern void ospf6_route_delete(struct ospf6_route *); +extern void ospf6_route_delete(struct ospf6_route *route); extern struct ospf6_route *ospf6_route_copy(struct ospf6_route *route); extern int ospf6_route_cmp(struct ospf6_route *ra, struct ospf6_route *rb); @@ -384,8 +384,10 @@ extern void ospf6_route_show_detail(struct vty *vty, struct ospf6_route *route, json_object *json, bool use_json); -extern int ospf6_route_table_show(struct vty *, int, int, struct cmd_token **, - struct ospf6_route_table *, bool use_json); +extern int ospf6_route_table_show(struct vty *vty, int argc_start, int argc, + struct cmd_token **argv, + struct ospf6_route_table *table, + bool use_json); extern int ospf6_linkstate_table_show(struct vty *vty, int idx_ipv4, int argc, struct cmd_token **argv, struct ospf6_route_table *table); diff --git a/ospf6d/ospf6_zebra.h b/ospf6d/ospf6_zebra.h index a3ccc3d38d..e25f6bf80d 100644 --- a/ospf6d/ospf6_zebra.h +++ b/ospf6d/ospf6_zebra.h @@ -54,20 +54,23 @@ extern void ospf6_zebra_redistribute(int, vrf_id_t vrf_id); extern void ospf6_zebra_no_redistribute(int, vrf_id_t vrf_id); #define ospf6_zebra_is_redistribute(type, vrf_id) \ vrf_bitmap_check(zclient->redist[AFI_IP6][type], vrf_id) -extern void ospf6_zebra_init(struct thread_master *); +extern void ospf6_zebra_init(struct thread_master *tm); extern void ospf6_zebra_add_discard(struct ospf6_route *request, struct ospf6 *ospf6); extern void ospf6_zebra_delete_discard(struct ospf6_route *request, struct ospf6 *ospf6); -extern void ospf6_distance_reset(struct ospf6 *); -extern uint8_t ospf6_distance_apply(struct prefix_ipv6 *, struct ospf6_route *, - struct ospf6 *); +extern void ospf6_distance_reset(struct ospf6 *ospf6); +extern uint8_t ospf6_distance_apply(struct prefix_ipv6 *p, + struct ospf6_route * or, + struct ospf6 *ospf6); -extern int ospf6_distance_set(struct vty *, struct ospf6 *, const char *, - const char *, const char *); -extern int ospf6_distance_unset(struct vty *, struct ospf6 *, const char *, - const char *, const char *); +extern int ospf6_distance_set(struct vty *vty, struct ospf6 *ospf6, + const char *distance_str, const char *ip_str, + const char *access_list_str); +extern int ospf6_distance_unset(struct vty *vty, struct ospf6 *ospf6, + const char *distance_str, const char *ip_str, + const char *access_list_str); extern int config_write_ospf6_debug_zebra(struct vty *vty); extern void install_element_ospf6_debug_zebra(void); diff --git a/pathd/path_cli.c b/pathd/path_cli.c index bd629a2b70..46242fd05a 100644 --- a/pathd/path_cli.c +++ b/pathd/path_cli.c @@ -352,7 +352,16 @@ static int segment_list_has_src_dst( nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "ipv6_adjacency"); node_src_id = adj_src_ipv6_str; + } else { + /* + * This is just to make the compiler happy about + * node_src_id not being initialized. This + * should never happen unless we change the cli + * function. + */ + assert(!"We must have a adj_src_ipv4_str or a adj_src_ipv6_str"); } + /* addresses */ snprintf(xpath, XPATH_MAXLEN, "./segment[index='%s']/nai/local-address", index_str); diff --git a/tests/lib/cli/test_cli.c b/tests/lib/cli/test_cli.c index 8dba1e29f0..f8d74018dd 100644 --- a/tests/lib/cli/test_cli.c +++ b/tests/lib/cli/test_cli.c @@ -40,6 +40,8 @@ DUMMY_DEFUN(cmd12, "alt a A.B.C.D"); DUMMY_DEFUN(cmd13, "alt a X:X::X:X"); DUMMY_DEFUN(cmd14, "pat g { foo A.B.C.D$foo|foo|bar X:X::X:X$bar| baz } [final]"); +DUMMY_DEFUN(cmd15, "no pat g ![ WORD ]"); +DUMMY_DEFUN(cmd16, "[no] pat h {foo ![A.B.C.D$foo]|bar X:X::X:X$bar} final"); #include "tests/lib/cli/test_cli_clippy.c" @@ -81,5 +83,7 @@ void test_init(int argc, char **argv) install_element(ENABLE_NODE, &cmd13_cmd); } install_element(ENABLE_NODE, &cmd14_cmd); + install_element(ENABLE_NODE, &cmd15_cmd); + install_element(ENABLE_NODE, &cmd16_cmd); install_element(ENABLE_NODE, &magic_test_cmd); } diff --git a/tests/lib/cli/test_cli.in b/tests/lib/cli/test_cli.in index 5c146ef984..bd685a6231 100644 --- a/tests/lib/cli/test_cli.in +++ b/tests/lib/cli/test_cli.in @@ -74,6 +74,23 @@ pat f pat f foo pat f key +no pat g +no pat g test +no pat g test more + +pat h foo ?1.2.3.4 final +no pat h foo ?1.2.3.4 final +pat h foo final +no pat h foo final +pat h bar final +no pat h bar final +pat h bar 1::2 final +no pat h bar 1::2 final +pat h bar 1::2 foo final +no pat h bar 1::2 foo final +pat h bar 1::2 foo 1.2.3.4 final +no pat h bar 1::2 foo 1.2.3.4 final + alt a a?b alt a 1 .2?.3.4 alt a 1 :2? ::?3 diff --git a/tests/lib/cli/test_cli.refout.in b/tests/lib/cli/test_cli.refout.in index 1f38e08b20..84365810d5 100644 --- a/tests/lib/cli/test_cli.refout.in +++ b/tests/lib/cli/test_cli.refout.in @@ -147,7 +147,7 @@ test# papat % Command incomplete.
test# pat
a b c d e f
-g
+g h
test# pat
% Command incomplete.
test#
@@ -263,6 +263,100 @@ cmd10 with 3 args. [01] f@(null): f
[02] key@(null): key
test#
+test# no pat g
+cmd15 with 3 args.
+[00] no@(null): no
+[01] pat@(null): pat
+[02] g@(null): g
+test# no pat g test
+cmd15 with 4 args.
+[00] no@(null): no
+[01] pat@(null): pat
+[02] g@(null): g
+[03] WORD@g: test
+test# no pat g test more
+% [NONE] Unknown command: no pat g test more
+test#
+test# pat h foo
+ A.B.C.D 04
+test# pat h foo 1.2.3.4 final
+cmd16 with 5 args.
+[00] pat@(null): pat
+[01] h@(null): h
+[02] foo@(null): foo
+[03] A.B.C.D@foo: 1.2.3.4
+[04] final@(null): final
+test# no pat h foo
+ A.B.C.D 04
+ bar 05
+ final 07
+test# no pat h foo 1.2.3.4 final
+cmd16 with 6 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] foo@(null): foo
+[04] A.B.C.D@foo: 1.2.3.4
+[05] final@(null): final
+test# pat h foo final
+% [NONE] Unknown command: pat h foo final
+test# no pat h foo final
+cmd16 with 5 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] foo@(null): foo
+[04] final@(null): final
+test# pat h bar final
+% [NONE] Unknown command: pat h bar final
+test# no pat h bar final
+% [NONE] Unknown command: no pat h bar final
+test# pat h bar 1::2 final
+cmd16 with 5 args.
+[00] pat@(null): pat
+[01] h@(null): h
+[02] bar@(null): bar
+[03] X:X::X:X@bar: 1::2
+[04] final@(null): final
+test# no pat h bar 1::2 final
+cmd16 with 6 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] bar@(null): bar
+[04] X:X::X:X@bar: 1::2
+[05] final@(null): final
+test# pat h bar 1::2 foo final
+% [NONE] Unknown command: pat h bar 1::2 foo final
+test# no pat h bar 1::2 foo final
+cmd16 with 7 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] bar@(null): bar
+[04] X:X::X:X@bar: 1::2
+[05] foo@(null): foo
+[06] final@(null): final
+test# pat h bar 1::2 foo 1.2.3.4 final
+cmd16 with 7 args.
+[00] pat@(null): pat
+[01] h@(null): h
+[02] bar@(null): bar
+[03] X:X::X:X@bar: 1::2
+[04] foo@(null): foo
+[05] A.B.C.D@foo: 1.2.3.4
+[06] final@(null): final
+test# no pat h bar 1::2 foo 1.2.3.4 final
+cmd16 with 8 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] bar@(null): bar
+[04] X:X::X:X@bar: 1::2
+[05] foo@(null): foo
+[06] A.B.C.D@foo: 1.2.3.4
+[07] final@(null): final
+test#
test# alt a
test# alt a a
WORD 02
diff --git a/tests/lib/test_nexthop.c b/tests/lib/test_nexthop.c index 659d207b4e..7cf687dffe 100644 --- a/tests/lib/test_nexthop.c +++ b/tests/lib/test_nexthop.c @@ -112,15 +112,15 @@ static void test_run_first(void) nexthop_free(nh2); /* Blackhole */ - nh1 = nexthop_from_blackhole(BLACKHOLE_REJECT); - nh2 = nexthop_from_blackhole(BLACKHOLE_REJECT); + nh1 = nexthop_from_blackhole(BLACKHOLE_REJECT, 0); + nh2 = nexthop_from_blackhole(BLACKHOLE_REJECT, 0); ret = nexthop_cmp_basic(nh1, nh2); assert(ret == 0); nexthop_free(nh2); - nh2 = nexthop_from_blackhole(BLACKHOLE_NULL); + nh2 = nexthop_from_blackhole(BLACKHOLE_NULL, 0); ret = nexthop_cmp_basic(nh1, nh2); assert(ret != 0); diff --git a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py index b1203570a1..1b99fcea1f 100644 --- a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py +++ b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py @@ -932,6 +932,9 @@ def test_bgp_summary(): # Remove Unknown Summary (all of it) actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual) actual = re.sub(r"No Unknown neighbor is configured", "", actual) + # Make Connect/Active/Idle the same (change them all to Active) + actual = re.sub(r" Connect ", " Active ", actual) + actual = re.sub(r" Idle ", " Active ", actual) actual = re.sub( r"IPv4 labeled-unicast Summary \(VRF default\):", "", actual @@ -1089,6 +1092,9 @@ def test_bgp_ipv6_summary(): # Remove Unknown Summary (all of it) actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual) actual = re.sub(r"No Unknown neighbor is configured", "", actual) + # Make Connect/Active/Idle the same (change them all to Active) + actual = re.sub(r" Connect ", " Active ", actual) + actual = re.sub(r" Idle ", " Active ", actual) # Remove Labeled Unicast Summary (all of it) actual = re.sub( diff --git a/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py b/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py index 27451ec7b3..c890b0d7dc 100644 --- a/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py +++ b/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py @@ -92,7 +92,13 @@ def test_bgp_default_originate_route_map(): def _bgp_default_route_has_metric(router): output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json")) expected = { - "paths": [{"aspath": {"string": "65000 65000 65000 65000"}, "metric": 123}] + "paths": [ + { + "aspath": {"string": "65000 65000 65000 65000"}, + "metric": 123, + "community": None, + } + ] } return topotest.json_cmp(output, expected) diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/host2/zebra.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/host2/zebra.conf index 9135545c58..b9f80f112d 100644 --- a/tests/topotests/bgp_evpn_overlay_index_gateway/host2/zebra.conf +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/host2/zebra.conf @@ -1,4 +1,4 @@ ! -int host1-eth0 +int host2-eth0 ip address 50.0.1.21/24 ipv6 address 50:0:1::21/48 diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/test_bgp_snmp_mplsvpn.py b/tests/topotests/bgp_snmp_mplsl3vpn/test_bgp_snmp_mplsvpn.py index 3e6e417211..0d27474cbd 100755 --- a/tests/topotests/bgp_snmp_mplsl3vpn/test_bgp_snmp_mplsvpn.py +++ b/tests/topotests/bgp_snmp_mplsl3vpn/test_bgp_snmp_mplsvpn.py @@ -151,13 +151,6 @@ def setup_module(mod): r1.run("sysctl -w net.mpls.conf.r1-eth0.input=1") r1.run("sysctl -w net.mpls.conf.r1-eth1.input=1") r1.run("sysctl -w net.mpls.conf.r1-eth2.input=1") - r2.run("sysctl -w net.mpls.conf.r1-eth0.input=1") - r2.run("sysctl -w net.mpls.conf.r1-eth1.input=1") - r3.run("sysctl -w net.mpls.conf.r1-eth0.input=1") - r3.run("sysctl -w net.mpls.conf.r1-eth1.input=1") - r3.run("sysctl -w net.mpls.conf.r1-eth2.input=1") - r4.run("sysctl -w net.mpls.conf.r1-eth0.input=1") - r4.run("sysctl -w net.mpls.conf.r1-eth1.input=1") router_list = tgen.routers() diff --git a/tests/topotests/example_test/r1/zebra.conf b/tests/topotests/example_test/r1/zebra.conf new file mode 100644 index 0000000000..b733b7b03c --- /dev/null +++ b/tests/topotests/example_test/r1/zebra.conf @@ -0,0 +1,8 @@ +interface r1-eth0 + ip address 192.168.1.1/24 + +interface r1-eth1 + ip address 192.168.2.1/24 + +interface r1-eth2 + ip address 192.168.3.1/24
\ No newline at end of file diff --git a/tests/topotests/example_test/r2/zebra.conf b/tests/topotests/example_test/r2/zebra.conf new file mode 100644 index 0000000000..c0921f54c9 --- /dev/null +++ b/tests/topotests/example_test/r2/zebra.conf @@ -0,0 +1,4 @@ +interface r2-eth0 + ip address 192.168.1.2/24 +interface r2-eth1 + ip address 192.168.3.2/24 diff --git a/tests/topotests/example_test/test_template.py b/tests/topotests/example_test/test_template.py index e94bb905a5..4c073f259c 100644 --- a/tests/topotests/example_test/test_template.py +++ b/tests/topotests/example_test/test_template.py @@ -1,5 +1,5 @@ #!/usr/bin/env python - +# -*- coding: utf-8 eval: (blacken-mode 1) -*- # # <template>.py # Part of NetDEF Topology Tests @@ -29,54 +29,69 @@ import sys import pytest -# Import topogen and topotest helpers -from lib.topogen import Topogen, TopoRouter, get_topogen - +from lib.topogen import Topogen, TopoRouter +from lib.topolog import logger # TODO: select markers based on daemons used during test # pytest module level markers -""" -pytestmark = pytest.mark.bfdd # single marker pytestmark = [ - pytest.mark.bgpd, - pytest.mark.ospfd, - pytest.mark.ospf6d -] # multiple markers -""" - - + # pytest.mark.babeld, + # pytest.mark.bfdd, + # pytest.mark.bgpd, + # pytest.mark.eigrpd, + # pytest.mark.isisd, + # pytest.mark.ldpd, + # pytest.mark.nhrpd, + # pytest.mark.ospf6d, + pytest.mark.ospfd, + # pytest.mark.pathd, + # pytest.mark.pbrd, + # pytest.mark.pimd, + # pytest.mark.ripd, + # pytest.mark.ripngd, + # pytest.mark.sharpd, + # pytest.mark.staticd, + # pytest.mark.vrrpd, +] + +# Function we pass to Topogen to create the topology def build_topo(tgen): "Build function" # Create 2 routers - for routern in range(1, 3): - tgen.add_router("r{}".format(routern)) + r1 = tgen.add_router("r1") + r2 = tgen.add_router("r2") - # Create a switch with just one router connected to it to simulate a - # empty network. + # Create a p2p connection between r1 and r2 + tgen.add_link(r1, r2) + + # Create a switch with one router connected to it to simulate a empty network. switch = tgen.add_switch("s1") - switch.add_link(tgen.gears["r1"]) + switch.add_link(r1) - # Create a connection between r1 and r2 + # Create a p2p connection between r1 and r2 switch = tgen.add_switch("s2") - switch.add_link(tgen.gears["r1"]) - switch.add_link(tgen.gears["r2"]) + switch.add_link(r1) + switch.add_link(r2) -def setup_module(mod): - "Sets up the pytest environment" +# New form of setup/teardown using pytest fixture +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" # This function initiates the topology build with Topogen... - tgen = Topogen(build_topo, mod.__name__) + tgen = Topogen(build_topo, request.module.__name__) - # The basic topology above could also have be more easily specified using a - # dictionary, remove the build_topo function and use the following instead: + # A basic topology similar to the above could also have be more easily specified + # using a # dictionary, remove the build_topo function and use the following + # instead: # # topodef = { # "s1": "r1" # "s2": ("r1", "r2") # } - # tgen = Topogen(topodef, mod.__name__) + # tgen = Topogen(topodef, request.module.__name__) # ... and here it calls initialization functions. tgen.start_topology() @@ -84,42 +99,69 @@ def setup_module(mod): # This is a sample of configuration loading. router_list = tgen.routers() - # For all registred routers, load the zebra configuration file - # CWD = os.path.dirname(os.path.realpath(__file__)) + # For all routers arrange for: + # - starting zebra using config file from <rtrname>/zebra.conf + # - starting ospfd using an empty config file. for rname, router in router_list.items(): - router.load_config( - TopoRouter.RD_ZEBRA, - # Uncomment next line to load configuration from ./router/zebra.conf - # os.path.join(CWD, '{}/zebra.conf'.format(rname)) - ) + router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + router.load_config(TopoRouter.RD_OSPF) - # After loading the configurations, this function loads configured daemons. + # Start and configure the router daemons tgen.start_router() + # Provide tgen as argument to each test function + yield tgen -def teardown_module(mod): - "Teardown the pytest environment" - tgen = get_topogen() - - # This function tears down the whole topology. + # Teardown after last test runs tgen.stop_topology() -def test_call_cli(): - "Dummy test that just calls tgen.cli() so we can interact with the build." - tgen = get_topogen() - # Don't run this test if we have any failure. +# Fixture that executes before each test +@pytest.fixture(autouse=True) +def skip_on_failure(tgen): if tgen.routers_have_failure(): - pytest.skip(tgen.errors) + pytest.skip("skipped because of previous test failure") + + +# =================== +# The tests functions +# =================== + - # logger.info("calling CLI") - # tgen.cli() +def test_get_version(tgen): + "Test the logs the FRR version" + + r1 = tgen.gears["r1"] + version = r1.vtysh_cmd("show version") + logger.info("FRR version is: " + version) + + +def test_connectivity(tgen): + "Test the logs the FRR version" + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + output = r1.cmd_raises("ping -c1 192.168.1.2") + output = r2.cmd_raises("ping -c1 192.168.3.1") + + +@pytest.mark.xfail +def test_expect_failure(tgen): + "A test that is current expected to fail but should be fixed" + + assert False, "Example of temporary expected failure that will eventually be fixed" + + +@pytest.mark.skip +def test_will_be_skipped(tgen): + "A test that will be skipped" + assert False # Memory leak test template -def test_memory_leak(): +def test_memory_leak(tgen): "Run the memory leak test and report results." - tgen = get_topogen() + if not tgen.is_memleak_enabled(): pytest.skip("Memory leak test/report is disabled") diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index 54020d2ff9..1bce3c6bb2 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -44,6 +44,7 @@ from lib.micronet import comm_error from lib.topogen import TopoRouter, get_topogen from lib.topolog import get_logger, logger from lib.topotest import frr_unicode, interface_set_status, version_cmp +from lib import topotest FRRCFG_FILE = "frr_json.conf" FRRCFG_BKUP_FILE = "frr_json_initial.conf" diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index 1b26ddc1b5..b98698185c 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -439,6 +439,19 @@ def run_and_expect_type(func, etype, count=20, wait=3, avalue=None): return (False, result) +def router_json_cmp_retry(router, cmd, data, exact=False, retry_timeout=10.0): + """ + Runs `cmd` that returns JSON data (normally the command ends with 'json') + and compare with `data` contents. Retry by default for 10 seconds + """ + + def test_func(): + return router_json_cmp(router, cmd, data, exact) + + ok, _ = run_and_expect(test_func, None, int(retry_timeout), 1) + return ok + + def int2dpid(dpid): "Converting Integer to DPID" diff --git a/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py b/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py index 3e14ab7164..dc14bc6468 100755 --- a/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py +++ b/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py @@ -90,7 +90,6 @@ from lib.pim import ( clear_ip_mroute, clear_ip_pim_interface_traffic, verify_igmp_config, - clear_ip_mroute_verify, McastTesterHelper, ) from lib.topolog import logger @@ -549,17 +548,11 @@ def test_clear_pim_neighbors_and_mroute_p0(request): result = app_helper.run_traffic("i2", IGMP_JOIN_RANGE_1, "f1") assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) - step("Clear the mroute on l1, wait for 5 sec") - result = clear_ip_mroute_verify(tgen, "l1") - assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) - step( - "After clear ip mroute (*,g) entries are re-populated again" - " with same OIL and IIF, verify using 'show ip mroute' and " - " 'show ip pim upstream' " + "Verify clear ip mroute (*,g) entries are populated by using " + "'show ip mroute' cli" ) - source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] input_dict = [ {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"} ] @@ -570,6 +563,21 @@ def test_clear_pim_neighbors_and_mroute_p0(request): ) assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result) + step("Clear mroutes on l1") + clear_ip_mroute(tgen, "l1") + + step( + "After clear ip mroute (*,g) entries are re-populated again" + " with same OIL and IIF, verify using 'show ip mroute' and " + " 'show ip pim upstream' " + ) + + for data in input_dict: + result = verify_ip_mroutes( + tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"] + ) + assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result) + step( "Verify 'show ip pim upstream' showing correct OIL and IIF" " on all the nodes" ) diff --git a/tests/topotests/route_scale/test_route_scale.py b/tests/topotests/route_scale/scale_test_common.py index fefeccd5e7..3557cb4413 100644 --- a/tests/topotests/route_scale/test_route_scale.py +++ b/tests/topotests/route_scale/scale_test_common.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -# test_route_scale.py +# scale_test_common.py # # Copyright (c) 2020 by # Cumulus Networks, Inc. @@ -23,7 +23,7 @@ # """ -test_route_scale.py: Testing route scale +scale_test_common.py: Common routines for testing route scale """ @@ -45,9 +45,6 @@ from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger -pytestmark = [pytest.mark.sharpd] - - ##################################################### ## ## Network Topology Definition @@ -55,7 +52,7 @@ pytestmark = [pytest.mark.sharpd] ##################################################### -def build(tgen): +def scale_build_common(tgen): "Build function" # Populate routers @@ -68,16 +65,9 @@ def build(tgen): switch.add_link(tgen.gears["r1"]) -##################################################### -## -## Tests starting -## -##################################################### - - -def setup_module(module): +def scale_setup_module(module): "Setup topology" - tgen = Topogen(build, module.__name__) + tgen = Topogen(scale_build_common, module.__name__) tgen.start_topology() router_list = tgen.routers() @@ -93,7 +83,7 @@ def setup_module(module): # tgen.mininet_cli() -def teardown_module(_mod): +def scale_teardown_module(_mod): "Teardown the pytest environment" tgen = get_topogen() @@ -101,7 +91,7 @@ def teardown_module(_mod): tgen.stop_topology() -def test_converge_protocols(): +def scale_converge_protocols(): "Wait for protocol convergence" tgen = get_topogen() @@ -156,7 +146,7 @@ def run_one_setup(r1, s): logger.info(output) -def test_route_install(): +def route_install_helper(iter): "Test route install for a variety of ecmp" tgen = get_topogen() @@ -166,6 +156,16 @@ def test_route_install(): r1 = tgen.gears["r1"] + # Avoid top ecmp case for runs with < 4G memory + output = tgen.net.cmd_raises("free") + m = re.search("Mem:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)", output) + total_mem = int(m.group(2)) + if total_mem < 4000000 and iter == 5: + logger.info( + "Limited memory available: {}, skipping x32 testcase".format(total_mem) + ) + return; + installed_file = "{}/r1/installed.routes.json".format(CWD) expected_installed = json.loads(open(installed_file).read()) @@ -196,38 +196,20 @@ def test_route_install(): # Build up a list of dicts with params for each step of the test; # use defaults where the step doesn't supply a value scale_setups = [] - for s in scale_steps: - d = dict(zip(scale_keys, s)) - for k in scale_keys: - if k not in d: - d[k] = scale_defaults[k] + s = scale_steps[iter] - scale_setups.append(d) + d = dict(zip(scale_keys, s)) + for k in scale_keys: + if k not in d: + d[k] = scale_defaults[k] - # Avoid top ecmp case for runs with < 4G memory - output = tgen.net.cmd_raises("free") - m = re.search("Mem:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)", output) - total_mem = int(m.group(2)) - if total_mem < 4000000: - logger.info( - "Limited memory available: {}, skipping x32 testcase".format(total_mem) - ) - scale_setups = scale_setups[0:-1] - - # Run each step using the dicts we've built - for s in scale_setups: - run_one_setup(r1, s) + run_one_setup(r1, d) # Mem leak testcase -def test_memory_leak(): +def scale_test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() if not tgen.is_memleak_enabled(): pytest.skip("Memory leak test/report is disabled") tgen.report_memory_leaks() - - -if __name__ == "__main__": - args = ["-s"] + sys.argv[1:] - sys.exit(pytest.main(args)) diff --git a/tests/topotests/route_scale/test_route_scale1.py b/tests/topotests/route_scale/test_route_scale1.py new file mode 100644 index 0000000000..b563883b45 --- /dev/null +++ b/tests/topotests/route_scale/test_route_scale1.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +# +# test_route_scale1.py +# +# Copyright (c) 2021 by +# Nvidia, Inc. +# Donald Sharp +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_route_scale1.py: Testing route scale + +""" +import os +import re +import sys +import pytest +import json +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +from scale_test_common import scale_build_common, scale_setup_module, route_install_helper, scale_test_memory_leak, scale_converge_protocols, scale_teardown_module + + +pytestmark = [pytest.mark.sharpd] + +def build(tgen): + scale_build_common(tgen) + +def setup_module(module): + scale_setup_module(module) + +def teardown_module(_mod): + scale_teardown_module(_mod) + +def test_converge_protocols(): + scale_converge_protocols() + +def test_route_install_2nh(): + route_install_helper(1) + +def test_route_install_4nh(): + route_install_helper(2) + +def test_route_install_16nh(): + route_install_helper(4) + +def test_memory_leak(): + scale_test_memory_leak() + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/route_scale/test_route_scale2.py b/tests/topotests/route_scale/test_route_scale2.py new file mode 100644 index 0000000000..7045995f26 --- /dev/null +++ b/tests/topotests/route_scale/test_route_scale2.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +# +# test_route_scale2.py +# +# Copyright (c) 2022 by +# Nvidia, Inc. +# Donald Sharp +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NVIDIA DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NVIDIA BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_route_scale2.py: Testing route scale + +""" +import os +import re +import sys +import pytest +import json +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +from scale_test_common import scale_build_common, scale_setup_module, route_install_helper, scale_test_memory_leak, scale_converge_protocols, scale_teardown_module + + +pytestmark = [pytest.mark.sharpd] + +def build(tgen): + scale_build_common(tgen) + +def setup_module(module): + scale_setup_module(module) + +def teardown_module(_mod): + scale_teardown_module(_mod) + +def test_converge_protocols(): + scale_converge_protocols() + +def test_route_install_1nh(): + route_install_helper(0) + +def test_route_install_8nh(): + route_install_helper(3) + +def test_route_install_32nh(): + route_install_helper(5) + +def test_memory_leak(): + scale_test_memory_leak() + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/zebra_netlink/r1/sharpd.conf b/tests/topotests/zebra_netlink/r1/sharpd.conf deleted file mode 100644 index e69de29bb2..0000000000 --- a/tests/topotests/zebra_netlink/r1/sharpd.conf +++ /dev/null diff --git a/tests/topotests/zebra_netlink/r1/v4_route.json b/tests/topotests/zebra_netlink/r1/v4_route.json deleted file mode 100644 index 39041ebc95..0000000000 --- a/tests/topotests/zebra_netlink/r1/v4_route.json +++ /dev/null @@ -1,2302 +0,0 @@ -{ - "2.1.3.7\/32":[ - { - "prefix":"2.1.3.7\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.8\/32":[ - { - "prefix":"2.1.3.8\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.9\/32":[ - { - "prefix":"2.1.3.9\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.10\/32":[ - { - "prefix":"2.1.3.10\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.11\/32":[ - { - "prefix":"2.1.3.11\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.12\/32":[ - { - "prefix":"2.1.3.12\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.13\/32":[ - { - "prefix":"2.1.3.13\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.14\/32":[ - { - "prefix":"2.1.3.14\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.15\/32":[ - { - "prefix":"2.1.3.15\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.16\/32":[ - { - "prefix":"2.1.3.16\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.17\/32":[ - { - "prefix":"2.1.3.17\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.18\/32":[ - { - "prefix":"2.1.3.18\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.19\/32":[ - { - "prefix":"2.1.3.19\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.20\/32":[ - { - "prefix":"2.1.3.20\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.21\/32":[ - { - "prefix":"2.1.3.21\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.22\/32":[ - { - "prefix":"2.1.3.22\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.23\/32":[ - { - "prefix":"2.1.3.23\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.24\/32":[ - { - "prefix":"2.1.3.24\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.25\/32":[ - { - "prefix":"2.1.3.25\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.26\/32":[ - { - "prefix":"2.1.3.26\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.27\/32":[ - { - "prefix":"2.1.3.27\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.28\/32":[ - { - "prefix":"2.1.3.28\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.29\/32":[ - { - "prefix":"2.1.3.29\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.30\/32":[ - { - "prefix":"2.1.3.30\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.31\/32":[ - { - "prefix":"2.1.3.31\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.32\/32":[ - { - "prefix":"2.1.3.32\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.33\/32":[ - { - "prefix":"2.1.3.33\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.34\/32":[ - { - "prefix":"2.1.3.34\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.35\/32":[ - { - "prefix":"2.1.3.35\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.36\/32":[ - { - "prefix":"2.1.3.36\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.37\/32":[ - { - "prefix":"2.1.3.37\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.38\/32":[ - { - "prefix":"2.1.3.38\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.39\/32":[ - { - "prefix":"2.1.3.39\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.40\/32":[ - { - "prefix":"2.1.3.40\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.41\/32":[ - { - "prefix":"2.1.3.41\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.42\/32":[ - { - "prefix":"2.1.3.42\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.43\/32":[ - { - "prefix":"2.1.3.43\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.44\/32":[ - { - "prefix":"2.1.3.44\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.45\/32":[ - { - "prefix":"2.1.3.45\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.46\/32":[ - { - "prefix":"2.1.3.46\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.47\/32":[ - { - "prefix":"2.1.3.47\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.48\/32":[ - { - "prefix":"2.1.3.48\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.49\/32":[ - { - "prefix":"2.1.3.49\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.50\/32":[ - { - "prefix":"2.1.3.50\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.51\/32":[ - { - "prefix":"2.1.3.51\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.52\/32":[ - { - "prefix":"2.1.3.52\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.53\/32":[ - { - "prefix":"2.1.3.53\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.54\/32":[ - { - "prefix":"2.1.3.54\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.55\/32":[ - { - "prefix":"2.1.3.55\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.56\/32":[ - { - "prefix":"2.1.3.56\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.57\/32":[ - { - "prefix":"2.1.3.57\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.58\/32":[ - { - "prefix":"2.1.3.58\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.59\/32":[ - { - "prefix":"2.1.3.59\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.60\/32":[ - { - "prefix":"2.1.3.60\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.61\/32":[ - { - "prefix":"2.1.3.61\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.62\/32":[ - { - "prefix":"2.1.3.62\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.63\/32":[ - { - "prefix":"2.1.3.63\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.64\/32":[ - { - "prefix":"2.1.3.64\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.65\/32":[ - { - "prefix":"2.1.3.65\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.66\/32":[ - { - "prefix":"2.1.3.66\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.67\/32":[ - { - "prefix":"2.1.3.67\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.68\/32":[ - { - "prefix":"2.1.3.68\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.69\/32":[ - { - "prefix":"2.1.3.69\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.70\/32":[ - { - "prefix":"2.1.3.70\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.71\/32":[ - { - "prefix":"2.1.3.71\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.72\/32":[ - { - "prefix":"2.1.3.72\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.73\/32":[ - { - "prefix":"2.1.3.73\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.74\/32":[ - { - "prefix":"2.1.3.74\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.75\/32":[ - { - "prefix":"2.1.3.75\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.76\/32":[ - { - "prefix":"2.1.3.76\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.77\/32":[ - { - "prefix":"2.1.3.77\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.78\/32":[ - { - "prefix":"2.1.3.78\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.79\/32":[ - { - "prefix":"2.1.3.79\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.80\/32":[ - { - "prefix":"2.1.3.80\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.81\/32":[ - { - "prefix":"2.1.3.81\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.82\/32":[ - { - "prefix":"2.1.3.82\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.83\/32":[ - { - "prefix":"2.1.3.83\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.84\/32":[ - { - "prefix":"2.1.3.84\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.85\/32":[ - { - "prefix":"2.1.3.85\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.86\/32":[ - { - "prefix":"2.1.3.86\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.87\/32":[ - { - "prefix":"2.1.3.87\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.88\/32":[ - { - "prefix":"2.1.3.88\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.89\/32":[ - { - "prefix":"2.1.3.89\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.90\/32":[ - { - "prefix":"2.1.3.90\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.91\/32":[ - { - "prefix":"2.1.3.91\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.92\/32":[ - { - "prefix":"2.1.3.92\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.93\/32":[ - { - "prefix":"2.1.3.93\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.94\/32":[ - { - "prefix":"2.1.3.94\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.95\/32":[ - { - "prefix":"2.1.3.95\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.96\/32":[ - { - "prefix":"2.1.3.96\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.97\/32":[ - { - "prefix":"2.1.3.97\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.98\/32":[ - { - "prefix":"2.1.3.98\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.99\/32":[ - { - "prefix":"2.1.3.99\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.100\/32":[ - { - "prefix":"2.1.3.100\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.101\/32":[ - { - "prefix":"2.1.3.101\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.102\/32":[ - { - "prefix":"2.1.3.102\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.103\/32":[ - { - "prefix":"2.1.3.103\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.104\/32":[ - { - "prefix":"2.1.3.104\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.105\/32":[ - { - "prefix":"2.1.3.105\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.106\/32":[ - { - "prefix":"2.1.3.106\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ] -} diff --git a/tests/topotests/zebra_netlink/test_zebra_netlink.py b/tests/topotests/zebra_netlink/test_zebra_netlink.py index 05cc0ae4a1..ca90c5cb15 100644 --- a/tests/topotests/zebra_netlink/test_zebra_netlink.py +++ b/tests/topotests/zebra_netlink/test_zebra_netlink.py @@ -24,21 +24,15 @@ test_zebra_netlink.py: Test some basic interactions with kernel using Netlink """ - -import os -import sys -import pytest +# pylint: disable=C0413 +import ipaddress import json +import sys from functools import partial -# Save the Current Working Directory to find configuration files. -CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, "../")) - -# pylint: disable=C0413 -# Import topogen and topotest helpers +import pytest from lib import topotest -from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topogen import Topogen, TopoRouter from lib.topolog import logger @@ -52,62 +46,73 @@ pytestmark = [pytest.mark.sharpd] ##################################################### -def setup_module(mod): +@pytest.fixture(scope="module") +def tgen(request): "Sets up the pytest environment" topodef = {"s1": ("r1")} - tgen = Topogen(topodef, mod.__name__) + tgen = Topogen(topodef, request.module.__name__) tgen.start_topology() + # Initialize all routers. router_list = tgen.routers() for rname, router in router_list.items(): - router.load_config( - TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) - ) - - router.load_config( - TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname)) - ) + router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + router.load_config(TopoRouter.RD_SHARP) - # Initialize all routers. tgen.start_router() + yield tgen + tgen.stop_topology() -def teardown_module(_mod): - "Teardown the pytest environment" - tgen = get_topogen() - - # This function tears down the whole topology. - tgen.stop_topology() +@pytest.fixture(autouse=True) +def skip_on_failure(tgen): + if tgen.routers_have_failure(): + pytest.skip("skipped because of previous test failure") -def test_zebra_netlink_batching(): +def test_zebra_netlink_batching(tgen): "Test the situation where dataplane fills netlink send buffer entirely." logger.info( "Test the situation where dataplane fills netlink send buffer entirely." ) - tgen = get_topogen() - if tgen.routers_have_failure(): - pytest.skip("skipped because of previous test failure") r1 = tgen.gears["r1"] # Reduce the size of the buffer to hit the limit. r1.vtysh_cmd("conf t\nzebra kernel netlink batch-tx-buf 256 256") - r1.vtysh_cmd("sharp install routes 2.1.3.7 nexthop 192.168.1.1 100") - json_file = "{}/r1/v4_route.json".format(CWD) - expected = json.loads(open(json_file).read()) - test_func = partial( - topotest.router_json_cmp, - r1, - "show ip route json", - expected, - ) - _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5) - assertmsg = '"r1" JSON output mismatches' - assert result is None, assertmsg - - r1.vtysh_cmd("sharp remove routes 2.1.3.7 100") + count = 100 + r1.vtysh_cmd("sharp install routes 2.1.3.7 nexthop 192.168.1.1 " + str(count)) + + # Generate expected results + entry = { + "protocol": "sharp", + "distance": 150, + "metric": 0, + "installed": True, + "table": 254, + "nexthops": [ + { + "fib": True, + "ip": "192.168.1.1", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": True, + "weight": 1, + } + ], + } + + match = {} + base = int(ipaddress.ip_address(u"2.1.3.7")) + for i in range(base, base + count): + pfx = str(ipaddress.ip_network((i, 32))) + match[pfx] = [dict(entry, prefix=pfx)] + + ok = topotest.router_json_cmp_retry(r1, "show ip route json", match) + assert ok, '"r1" JSON output mismatches' + + r1.vtysh_cmd("sharp remove routes 2.1.3.7 " + str(count)) if __name__ == "__main__": diff --git a/tests/zebra/test_lm_plugin.c b/tests/zebra/test_lm_plugin.c index 4a9344fee4..ecfb085793 100644 --- a/tests/zebra/test_lm_plugin.c +++ b/tests/zebra/test_lm_plugin.c @@ -77,7 +77,7 @@ static int lm_release_chunk_pi(struct zserv *client, uint32_t start, /* use external allocations */ -static void lp_plugin_init() +static void lp_plugin_init(void) { /* register our own hooks */ hook_register(lm_client_connect, test_client_connect); @@ -86,7 +86,7 @@ static void lp_plugin_init() hook_register(lm_release_chunk, lm_release_chunk_pi); } -static void lp_plugin_cleanup() +static void lp_plugin_cleanup(void) { /* register our own hooks */ hook_unregister(lm_client_connect, test_client_connect); @@ -98,7 +98,7 @@ static void lp_plugin_cleanup() /* tests */ -static void test_lp_plugin() +static void test_lp_plugin(void) { struct label_manager_chunk *lmc; diff --git a/tools/permutations.c b/tools/permutations.c index f51d4a4ec9..b280cc15b1 100644 --- a/tools/permutations.c +++ b/tools/permutations.c @@ -61,9 +61,22 @@ void permute(struct graph_node *start) struct cmd_token *stok = start->data; struct graph_node *gnn; struct listnode *ln; + bool is_neg = false; // recursive dfs listnode_add(position, start); + + for (ALL_LIST_ELEMENTS_RO(position, ln, gnn)) { + struct cmd_token *tok = gnn->data; + + if (tok->type == WORD_TKN && !strcmp(tok->text, "no")) { + is_neg = true; + break; + } + if (tok->type < SPECIAL_TKN) + break; + } + for (unsigned int i = 0; i < vector_active(start->to); i++) { struct graph_node *gn = vector_slot(start->to, i); struct cmd_token *tok = gn->data; @@ -82,6 +95,9 @@ void permute(struct graph_node *start) fprintf(stdout, "\n"); } else { bool skip = false; + + if (tok->type == NEG_ONLY_TKN && !is_neg) + continue; if (stok->type == FORK_TKN && tok->type != FORK_TKN) for (ALL_LIST_ELEMENTS_RO(position, ln, gnn)) if (gnn == gn) { diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 72c7071502..9ffbcc9223 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1611,7 +1611,8 @@ static struct nexthop *nexthop_from_zapi(const struct zapi_nexthop *api_nh, zlog_debug("%s: nh blackhole %d", __func__, api_nh->bh_type); - nexthop = nexthop_from_blackhole(api_nh->bh_type); + nexthop = + nexthop_from_blackhole(api_nh->bh_type, api_nh->vrf_id); break; } diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c index 08ff7011e2..af46ea6d7a 100644 --- a/zebra/zebra_evpn_neigh.c +++ b/zebra/zebra_evpn_neigh.c @@ -2113,12 +2113,12 @@ void zebra_evpn_neigh_remote_macip_add(struct zebra_evpn *zevpn, "sync->remote neigh vni %u ip %pIA mac %pEA seq %d f0x%x", n->zevpn->vni, &n->ip, &n->emac, seq, n->flags); - zebra_evpn_neigh_clear_sync_info(n); if (IS_ZEBRA_NEIGH_ACTIVE(n)) zebra_evpn_neigh_send_del_to_client( zevpn->vni, &n->ip, &n->emac, n->flags, n->state, false /*force*/); + zebra_evpn_neigh_clear_sync_info(n); } if (memcmp(&n->emac, &mac->macaddr, sizeof(struct ethaddr)) diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c index 64366d6f4f..8caabf19e7 100644 --- a/zebra/zebra_fpm.c +++ b/zebra/zebra_fpm.c @@ -292,6 +292,9 @@ static void zfpm_start_connect_timer(const char *reason); static void zfpm_start_stats_timer(void); static void zfpm_mac_info_del(struct fpm_mac_info_t *fpm_mac); +static const char ipv4_ll_buf[16] = "169.254.0.1"; +union g_addr ipv4ll_gateway; + /* * zfpm_thread_should_yield */ @@ -1993,6 +1996,9 @@ static int zfpm_init(struct thread_master *master) zfpm_stats_init(&zfpm_g->last_ivl_stats); zfpm_stats_init(&zfpm_g->cumulative_stats); + memset(&ipv4ll_gateway, 0, sizeof(ipv4ll_gateway)); + inet_pton(AF_INET, ipv4_ll_buf, &ipv4ll_gateway.ipv4); + install_node(&zebra_node); install_element(ENABLE_NODE, &show_zebra_fpm_stats_cmd); install_element(ENABLE_NODE, &clear_zebra_fpm_stats_cmd); diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index efbd078a52..68fb044353 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -189,7 +189,12 @@ static int netlink_route_info_add_nh(struct netlink_route_info *ri, if (nexthop->type == NEXTHOP_TYPE_IPV6 || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { - nhi.gateway = &nexthop->gate; + /* Special handling for IPv4 route with IPv6 Link Local next hop + */ + if (ri->af == AF_INET) + nhi.gateway = &ipv4ll_gateway; + else + nhi.gateway = &nexthop->gate; } if (nexthop->type == NEXTHOP_TYPE_IFINDEX) { diff --git a/zebra/zebra_fpm_private.h b/zebra/zebra_fpm_private.h index c169ee8c22..13415c7e1d 100644 --- a/zebra/zebra_fpm_private.h +++ b/zebra/zebra_fpm_private.h @@ -97,6 +97,8 @@ extern int zfpm_netlink_encode_mac(struct fpm_mac_info_t *mac, char *in_buf, extern struct route_entry *zfpm_route_for_update(rib_dest_t *dest); +extern union g_addr ipv4ll_gateway; + #ifdef __cplusplus } #endif diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index 7e9382518f..e17465b112 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -520,7 +520,13 @@ static int zebra_ptm_handle_bfd_msg(void *arg, void *in_ctxt, if (!strcmp(ZEBRA_PTM_INVALID_VRF, vrf_str) && ifp) { vrf_id = ifp->vrf_id; } else { - vrf_id = vrf_name_to_id(vrf_str); + struct vrf *pVrf; + + pVrf = vrf_lookup_by_name(vrf_str); + if (pVrf) + vrf_id = pVrf->vrf_id; + else + vrf_id = VRF_DEFAULT; } if (!strcmp(bfdst_str, ZEBRA_PTM_BFDSTATUS_DOWN_STR)) { diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index b204b30ca7..1c8a1ad09a 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -357,9 +357,11 @@ static void show_nexthop_detail_helper(struct vty *vty, break; } - if ((re->vrf_id != nexthop->vrf_id) - && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) - vty_out(vty, "(vrf %s)", vrf_id_to_name(nexthop->vrf_id)); + if (re->vrf_id != nexthop->vrf_id) { + struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id); + + vty_out(vty, "(vrf %s)", VRF_LOGNAME(vrf)); + } if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) vty_out(vty, " (duplicate nexthop removed)"); @@ -607,8 +609,7 @@ static void show_route_nexthop_helper(struct vty *vty, break; } - if ((re == NULL || (nexthop->vrf_id != re->vrf_id)) - && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) + if ((re == NULL || (nexthop->vrf_id != re->vrf_id))) vty_out(vty, " (vrf %s)", vrf_id_to_name(nexthop->vrf_id)); if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) @@ -780,8 +781,7 @@ static void show_nexthop_json_helper(json_object *json_nexthop, break; } - if ((nexthop->vrf_id != re->vrf_id) - && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) + if (nexthop->vrf_id != re->vrf_id) json_object_string_add(json_nexthop, "vrf", vrf_id_to_name(nexthop->vrf_id)); |
