summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--configure.ac3
-rw-r--r--doc/user/zebra.rst5
-rw-r--r--isisd/isis_bfd.c1
-rw-r--r--isisd/isis_circuit.c4
-rw-r--r--isisd/isis_cli.c4
-rw-r--r--isisd/isis_nb_config.c2
-rw-r--r--lib/compiler.h17
-rw-r--r--lib/frr_zmq.c32
-rw-r--r--lib/prefix.h9
-rw-r--r--ospf6d/ospf6_abr.c6
-rw-r--r--ospf6d/ospf6_bfd.c1
-rw-r--r--ospf6d/ospf6_route.c6
-rw-r--r--ospf6d/ospf6_top.c7
-rw-r--r--pceplib/pcep_session_logic_loop.c4
-rw-r--r--pimd/pim_bfd.c1
-rw-r--r--staticd/static_vty.c5
-rw-r--r--tests/topotests/bgp_gshut_topo1/ebgp_gshut_topo1.json221
-rw-r--r--tests/topotests/bgp_gshut_topo1/ibgp_gshut_topo1.json221
-rw-r--r--tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py630
-rw-r--r--tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py676
-rwxr-xr-xtests/topotests/multicast_pim_sm_topo2/test_multicast_pim_sm_topo2.py38
-rw-r--r--tools/.gitignore1
-rw-r--r--tools/etc/frr/support_bundle_commands.conf2
-rw-r--r--tools/frr-llvm-cg.c347
-rw-r--r--tools/frr-llvm-debuginfo.cpp112
-rw-r--r--tools/frr-llvm-debuginfo.h47
-rw-r--r--tools/subdir.am6
-rw-r--r--zebra/zebra_vty.c205
29 files changed, 2547 insertions, 68 deletions
diff --git a/.gitignore b/.gitignore
index 4e120dae85..33d03296db 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,7 @@
/libtool.orig
/changelog-auto
/test-driver
+/test-suite.log
/Makefile
/Makefile.in
@@ -57,6 +58,7 @@
*.pb.cc
*_clippy.c
*.bc
+*.ll
*.cg.json
*.cg.dot
*.cg.svg
diff --git a/configure.ac b/configure.ac
index 560cd73860..96bacc9ed4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1924,7 +1924,7 @@ dnl ---------------
dnl gRPC
dnl ---------------
if test "$enable_grpc" = "yes"; then
- AC_LANG([C++])
+ AC_LANG_PUSH([C++])
AX_CXX_COMPILE_STDCXX([11], [ext])
PKG_CHECK_MODULES([GRPC], [grpc >= 6.0.0 grpc++ >= 1.16.1 protobuf >= 3.6.1 ], [
AC_CHECK_PROGS([PROTOC], [protoc], [/bin/false])
@@ -1938,6 +1938,7 @@ if test "$enable_grpc" = "yes"; then
GRPC=false
AC_MSG_ERROR([grpc/grpc++ were not found on your system.])
])
+ AC_LANG_POP([C++])
fi
dnl -----
diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst
index ac1686dd27..1539f9a9d1 100644
--- a/doc/user/zebra.rst
+++ b/doc/user/zebra.rst
@@ -1058,6 +1058,11 @@ zebra Terminal Mode Commands
nexthop groups that do have an afi. [type] allows you to filter those
only coming from a specific NHG type (protocol).
+.. clicmd:: show <ip|ipv6> zebra route dump [<vrf> VRFNAME]
+
+ It dumps all the routes from RIB with detailed information including
+ internal flags, status etc. This is defined as a hidden command.
+
Router-id
=========
diff --git a/isisd/isis_bfd.c b/isisd/isis_bfd.c
index ed4d2c6539..7510e310f7 100644
--- a/isisd/isis_bfd.c
+++ b/isisd/isis_bfd.c
@@ -132,6 +132,7 @@ static void bfd_handle_adj_up(struct isis_adjacency *adj)
bfd_sess_set_ipv6_addrs(adj->bfd_session, &src_ip.ipv6,
&dst_ip.ipv6);
bfd_sess_set_interface(adj->bfd_session, adj->circuit->interface->name);
+ bfd_sess_set_vrf(adj->bfd_session, adj->circuit->interface->vrf_id);
bfd_sess_set_profile(adj->bfd_session, circuit->bfd_config.profile);
bfd_sess_install(adj->bfd_session);
return;
diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c
index 2a197ab2b7..4fa28a4ad9 100644
--- a/isisd/isis_circuit.c
+++ b/isisd/isis_circuit.c
@@ -497,7 +497,7 @@ void isis_circuit_if_add(struct isis_circuit *circuit, struct interface *ifp)
circuit->circ_type = CIRCUIT_T_BROADCAST;
} else if (if_is_pointopoint(ifp)) {
circuit->circ_type = CIRCUIT_T_P2P;
- } else if (if_is_loopback(ifp)) {
+ } else if (if_is_loopback_or_vrf(ifp)) {
circuit->circ_type = CIRCUIT_T_LOOPBACK;
circuit->is_passive = 1;
} else {
@@ -1350,7 +1350,7 @@ ferr_r isis_circuit_passive_set(struct isis_circuit *circuit, bool passive)
if (circuit->is_passive == passive)
return ferr_ok();
- if (if_is_loopback(circuit->interface) && !passive)
+ if (if_is_loopback_or_vrf(circuit->interface) && !passive)
return ferr_cfg_invalid("loopback is always passive");
if (circuit->state != C_STATE_UP) {
diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c
index 5aea9f25d9..415c25fbf9 100644
--- a/isisd/isis_cli.c
+++ b/isisd/isis_cli.c
@@ -195,7 +195,7 @@ DEFPY_YANG(ip_router_isis, ip_router_isis_cmd,
/* check if the interface is a loopback and if so set it as passive */
ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
- if (ifp && if_is_loopback(ifp))
+ if (ifp && if_is_loopback_or_vrf(ifp))
nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive",
NB_OP_MODIFY, "true");
@@ -252,7 +252,7 @@ DEFPY_YANG(ip6_router_isis, ip6_router_isis_cmd,
/* check if the interface is a loopback and if so set it as passive */
ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
- if (ifp && if_is_loopback(ifp))
+ if (ifp && if_is_loopback_or_vrf(ifp))
nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive",
NB_OP_MODIFY, "true");
diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c
index a00c86eeaf..2622c5b51b 100644
--- a/isisd/isis_nb_config.c
+++ b/isisd/isis_nb_config.c
@@ -2966,7 +2966,7 @@ int lib_interface_isis_passive_modify(struct nb_cb_modify_args *args)
ifp = circuit->interface;
if (!ifp)
return NB_OK;
- if (if_is_loopback(ifp)) {
+ if (if_is_loopback_or_vrf(ifp)) {
snprintf(args->errmsg, args->errmsg_len,
"Loopback is always passive");
return NB_ERR_VALIDATION;
diff --git a/lib/compiler.h b/lib/compiler.h
index 86cf347e01..bbfe01b569 100644
--- a/lib/compiler.h
+++ b/lib/compiler.h
@@ -140,21 +140,6 @@ extern "C" {
#define MACRO_REQUIRE_SEMICOLON() \
_Static_assert(1, "please add a semicolon after this macro")
-#if CONFDATE < 20210601
-#ifdef ENABLE_BGP_VNC
-/* temporarily disabled for transition for LabN CI
- * NB: it's not possible to generate a deprecation warning for this, hence
- * the shortened transition period (since otherwise new uses of the old syntax
- * may creep in without errors)
- */
-#undef MACRO_REQUIRE_SEMICOLON
-#define MACRO_REQUIRE_SEMICOLON() \
- /* nothing */
-#endif /* ENABLE_BGP_VNC */
-#else /* CONFDATE >= 20210601 */
-CPP_NOTICE("time to remove this CONFDATE block")
-#endif
-
/* variadic macros, use like:
* #define V_0() ...
* #define V_1(x) ...
@@ -331,7 +316,7 @@ CPP_NOTICE("time to remove this CONFDATE block")
19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \
9, 8, 7, 6, 5, 4, 3, 2, 1, 0
-#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
+#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
#define PP_NARG(...) PP_NARG_(_, ##__VA_ARGS__, PP_RSEQ_N())
diff --git a/lib/frr_zmq.c b/lib/frr_zmq.c
index 05f0fce5fc..ce52848a25 100644
--- a/lib/frr_zmq.c
+++ b/lib/frr_zmq.c
@@ -135,8 +135,8 @@ static int frrzmq_read_msg(struct thread *t)
if (read)
frrzmq_check_events(cbp, &cb->write, ZMQ_POLLOUT);
- _thread_add_read_write(t->xref, t->master, frrzmq_read_msg, cbp,
- cb->fd, &cb->read.thread);
+ thread_add_read(t->master, frrzmq_read_msg, cbp,
+ cb->fd, &cb->read.thread);
return 0;
out_err:
@@ -191,11 +191,11 @@ int _frrzmq_thread_add_read(const struct xref_threadsched *xref,
if (events & ZMQ_POLLIN) {
thread_cancel(&cb->read.thread);
- _thread_add_event(xref, master, frrzmq_read_msg, cbp, fd,
+ thread_add_event(master, frrzmq_read_msg, cbp, fd,
&cb->read.thread);
} else
- _thread_add_read_write(xref, master, frrzmq_read_msg, cbp, fd,
- &cb->read.thread);
+ thread_add_read(master, frrzmq_read_msg, cbp, fd,
+ &cb->read.thread);
return 0;
}
@@ -241,8 +241,8 @@ static int frrzmq_write_msg(struct thread *t)
if (written)
frrzmq_check_events(cbp, &cb->read, ZMQ_POLLIN);
- _thread_add_read_write(t->xref, t->master, frrzmq_write_msg, cbp,
- cb->fd, &cb->write.thread);
+ thread_add_write(t->master, frrzmq_write_msg, cbp,
+ cb->fd, &cb->write.thread);
return 0;
out_err:
@@ -297,8 +297,8 @@ int _frrzmq_thread_add_write(const struct xref_threadsched *xref,
_thread_add_event(xref, master, frrzmq_write_msg, cbp, fd,
&cb->write.thread);
} else
- _thread_add_read_write(xref, master, frrzmq_write_msg, cbp, fd,
- &cb->write.thread);
+ thread_add_write(master, frrzmq_write_msg, cbp, fd,
+ &cb->write.thread);
return 0;
}
@@ -310,7 +310,7 @@ void frrzmq_thread_cancel(struct frrzmq_cb **cb, struct cb_core *core)
thread_cancel(&core->thread);
if ((*cb)->read.cancelled && !(*cb)->read.thread
- && (*cb)->write.cancelled && (*cb)->write.thread)
+ && (*cb)->write.cancelled && !(*cb)->write.thread)
XFREE(MTYPE_ZEROMQ_CB, *cb);
}
@@ -330,12 +330,16 @@ void frrzmq_check_events(struct frrzmq_cb **cbp, struct cb_core *core,
len = sizeof(events);
if (zmq_getsockopt(cb->zmqsock, ZMQ_EVENTS, &events, &len))
return;
- if (events & event && core->thread && !core->cancelled) {
+ if ((events & event) && core->thread && !core->cancelled) {
struct thread_master *tm = core->thread->master;
+
thread_cancel(&core->thread);
- thread_add_event(tm, (event == ZMQ_POLLIN ? frrzmq_read_msg
- : frrzmq_write_msg),
- cbp, cb->fd, &core->thread);
+ if (event == ZMQ_POLLIN)
+ thread_add_event(tm, frrzmq_read_msg,
+ cbp, cb->fd, &core->thread);
+ else
+ thread_add_event(tm, frrzmq_write_msg,
+ cbp, cb->fd, &core->thread);
}
}
diff --git a/lib/prefix.h b/lib/prefix.h
index d7ee1b8e4c..217a23d561 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -315,10 +315,12 @@ struct prefix_sg {
#ifndef __cplusplus
#define prefixtype(uname, typename, fieldname) \
typename *fieldname;
+#define TRANSPARENT_UNION __attribute__((transparent_union))
#else
#define prefixtype(uname, typename, fieldname) \
typename *fieldname; \
uname(typename *x) { this->fieldname = x; }
+#define TRANSPARENT_UNION
#endif
union prefixptr {
@@ -328,7 +330,7 @@ union prefixptr {
prefixtype(prefixptr, struct prefix_evpn, evp)
prefixtype(prefixptr, struct prefix_fs, fs)
prefixtype(prefixptr, struct prefix_rd, rd)
-} __attribute__((transparent_union));
+} TRANSPARENT_UNION;
union prefixconstptr {
prefixtype(prefixconstptr, const struct prefix, p)
@@ -337,7 +339,10 @@ union prefixconstptr {
prefixtype(prefixconstptr, const struct prefix_evpn, evp)
prefixtype(prefixconstptr, const struct prefix_fs, fs)
prefixtype(prefixconstptr, const struct prefix_rd, rd)
-} __attribute__((transparent_union));
+} TRANSPARENT_UNION;
+
+#undef prefixtype
+#undef TRANSPARENT_UNION
#ifndef INET_ADDRSTRLEN
#define INET_ADDRSTRLEN 16
diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c
index 286e642781..1af8aed1a9 100644
--- a/ospf6d/ospf6_abr.c
+++ b/ospf6d/ospf6_abr.c
@@ -1224,8 +1224,6 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
if (table->hook_add)
(*table->hook_add)(old_route);
- /* Delete new route */
- ospf6_route_delete(route);
break;
}
@@ -1253,7 +1251,9 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
listnode_add_sort(route->paths, path);
/* ospf6_ia_add_nw_route (table, &prefix, route); */
ospf6_route_add(route, table);
- }
+ } else
+ /* if we did not add the route remove it */
+ ospf6_route_delete(route);
}
void ospf6_abr_examin_brouter(uint32_t router_id, struct ospf6_route *route,
diff --git a/ospf6d/ospf6_bfd.c b/ospf6d/ospf6_bfd.c
index ba8c398dee..fc1e718540 100644
--- a/ospf6d/ospf6_bfd.c
+++ b/ospf6d/ospf6_bfd.c
@@ -143,6 +143,7 @@ void ospf6_bfd_info_nbr_create(struct ospf6_interface *oi,
bfd_sess_set_ipv6_addrs(on->bfd_session, on->ospf6_if->linklocal_addr,
&on->linklocal_addr);
bfd_sess_set_interface(on->bfd_session, oi->interface->name);
+ bfd_sess_set_vrf(on->bfd_session, oi->interface->vrf_id);
bfd_sess_set_profile(on->bfd_session, oi->bfd_config.profile);
}
diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c
index 2daf119c52..908011c946 100644
--- a/ospf6d/ospf6_route.c
+++ b/ospf6d/ospf6_route.c
@@ -39,6 +39,7 @@
#include "ospf6_zebra.h"
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_ROUTE, "OSPF6 route");
+DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_ROUTE_TABLE, "OSPF6 route table");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_NEXTHOP, "OSPF6 nexthop");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PATH, "OSPF6 Path");
@@ -1021,7 +1022,8 @@ void ospf6_route_remove_all(struct ospf6_route_table *table)
struct ospf6_route_table *ospf6_route_table_create(int s, int t)
{
struct ospf6_route_table *new;
- new = XCALLOC(MTYPE_OSPF6_ROUTE, sizeof(struct ospf6_route_table));
+ new = XCALLOC(MTYPE_OSPF6_ROUTE_TABLE,
+ sizeof(struct ospf6_route_table));
new->table = route_table_init();
new->scope_type = s;
new->table_type = t;
@@ -1033,7 +1035,7 @@ void ospf6_route_table_delete(struct ospf6_route_table *table)
ospf6_route_remove_all(table);
bf_free(table->idspace);
route_table_finish(table->table);
- XFREE(MTYPE_OSPF6_ROUTE, table);
+ XFREE(MTYPE_OSPF6_ROUTE_TABLE, table);
}
diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c
index 2376409ccd..42405ca35e 100644
--- a/ospf6d/ospf6_top.c
+++ b/ospf6d/ospf6_top.c
@@ -413,6 +413,7 @@ void ospf6_delete(struct ospf6 *o)
{
struct listnode *node, *nnode;
struct ospf6_area *oa;
+ struct vrf *vrf;
QOBJ_UNREG(o);
@@ -442,6 +443,12 @@ void ospf6_delete(struct ospf6 *o)
ospf6_distance_reset(o);
route_table_finish(o->distance_table);
+ if (o->vrf_id != VRF_UNKNOWN) {
+ vrf = vrf_lookup_by_id(o->vrf_id);
+ if (vrf)
+ ospf6_vrf_unlink(o, vrf);
+ }
+
XFREE(MTYPE_OSPF6_TOP, o->name);
XFREE(MTYPE_OSPF6_TOP, o);
}
diff --git a/pceplib/pcep_session_logic_loop.c b/pceplib/pcep_session_logic_loop.c
index 4b855c06cd..77392fb6a9 100644
--- a/pceplib/pcep_session_logic_loop.c
+++ b/pceplib/pcep_session_logic_loop.c
@@ -332,7 +332,7 @@ void *session_logic_loop(void *data)
session_logic_handle
->session_event_queue);
pthread_mutex_unlock(
- &(session_logic_handle_
+ &(session_logic_handle
->session_list_mutex));
continue;
}
@@ -350,7 +350,7 @@ void *session_logic_loop(void *data)
session_logic_handle->session_event_queue);
pthread_mutex_unlock(
- &(session_logic_handle_->session_list_mutex));
+ &(session_logic_handle->session_list_mutex));
}
session_logic_handle->session_logic_condition = false;
diff --git a/pimd/pim_bfd.c b/pimd/pim_bfd.c
index bc518391a5..dfe2d5f2fa 100644
--- a/pimd/pim_bfd.c
+++ b/pimd/pim_bfd.c
@@ -96,6 +96,7 @@ void pim_bfd_info_nbr_create(struct pim_interface *pim_ifp,
pim_ifp->bfd_config.min_rx, pim_ifp->bfd_config.min_tx);
bfd_sess_set_ipv4_addrs(neigh->bfd_session, NULL, &neigh->source_addr);
bfd_sess_set_interface(neigh->bfd_session, neigh->interface->name);
+ bfd_sess_set_vrf(neigh->bfd_session, neigh->interface->vrf_id);
bfd_sess_set_profile(neigh->bfd_session, pim_ifp->bfd_config.profile);
bfd_sess_install(neigh->bfd_session);
}
diff --git a/staticd/static_vty.c b/staticd/static_vty.c
index 3bdd4a673c..ea09054a23 100644
--- a/staticd/static_vty.c
+++ b/staticd/static_vty.c
@@ -325,9 +325,8 @@ static int static_route_leak(struct vty *vty, const char *svrf,
dnode = yang_dnode_get(vty->candidate_config->dnode, ab_xpath);
if (!dnode) {
- vty_out(vty,
- "%% Refusing to remove a non-existent route\n");
- return ret;
+ /* Silently return */
+ return CMD_SUCCESS;
}
dnode = yang_get_subtree_with_no_sibling(dnode);
diff --git a/tests/topotests/bgp_gshut_topo1/ebgp_gshut_topo1.json b/tests/topotests/bgp_gshut_topo1/ebgp_gshut_topo1.json
new file mode 100644
index 0000000000..dcd5af83f6
--- /dev/null
+++ b/tests/topotests/bgp_gshut_topo1/ebgp_gshut_topo1.json
@@ -0,0 +1,221 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.1.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:DB8:F::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "r2": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "static_routes":[
+ {
+ "network":"100.0.10.1/32",
+ "no_of_ip":5,
+ "next_hop":"Null0"
+ },
+ {
+ "network":"1::1/128",
+ "no_of_ip":5,
+ "next_hop":"Null0"
+ }]
+ },
+ "r2": {
+ "links": {
+ "r1": {"ipv4": "auto", "ipv6": "auto"},
+ "r4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "r1": {"ipv4": "auto", "ipv6": "auto"},
+ "r4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "r2": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_gshut_topo1/ibgp_gshut_topo1.json b/tests/topotests/bgp_gshut_topo1/ibgp_gshut_topo1.json
new file mode 100644
index 0000000000..464e362465
--- /dev/null
+++ b/tests/topotests/bgp_gshut_topo1/ibgp_gshut_topo1.json
@@ -0,0 +1,221 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.1.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:DB8:F::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "r2": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "static_routes":[
+ {
+ "network":"100.0.10.1/32",
+ "no_of_ip":5,
+ "next_hop":"Null0"
+ },
+ {
+ "network":"1::1/128",
+ "no_of_ip":5,
+ "next_hop":"Null0"
+ }]
+ },
+ "r2": {
+ "links": {
+ "r1": {"ipv4": "auto", "ipv6": "auto"},
+ "r4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "r1": {"ipv4": "auto", "ipv6": "auto"},
+ "r4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "r2": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py b/tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py
new file mode 100644
index 0000000000..868aec9f3e
--- /dev/null
+++ b/tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py
@@ -0,0 +1,630 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# 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 VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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.
+#
+
+
+"""
+Following tests are covered to test ecmp functionality on BGP GSHUT.
+1. Verify graceful-shutdown functionality with eBGP peers
+2. Verify graceful-shutdown functionality when daemons
+ bgpd/zebra/staticd and frr services are restarted with eBGP peers
+"""
+
+import os
+import sys
+import time
+import json
+import pytest
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from mininet.topo import Topo
+from time import sleep
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ interface_status,
+ reset_config_on_routers,
+ step,
+ get_frr_ipv6_linklocal,
+ kill_router_daemons,
+ start_router_daemons,
+ stop_router,
+ start_router,
+ create_route_maps,
+ create_bgp_community_lists,
+ delete_route_maps,
+ required_linux_kernel_version,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ clear_bgp,
+ verify_bgp_rib,
+ verify_bgp_attributes,
+)
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology and configuration creation
+jsonFile = "{}/ebgp_gshut_topo1.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ logger.info("Could not read file:", jsonFile)
+
+# Global variables
+NETWORK = {"ipv4": "100.0.10.1/32", "ipv6": "1::1/128"}
+NEXT_HOP_IP_1 = {"ipv4": "10.0.2.1", "ipv6": "fd00:0:0:1::1"}
+NEXT_HOP_IP_2 = {"ipv4": "10.0.4.2", "ipv6": "fd00:0:0:3::2"}
+PREFERRED_NEXT_HOP = "link_local"
+BGP_CONVERGENCE = False
+
+
+class GenerateTopo(Topo):
+ """
+ Test topology builder
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ # This function only purpose is to create topology
+ # as defined in input json file.
+ #
+ # Create topology (setup module)
+ # Creating 2 routers topology, r1, r2in IBGP
+ # Bring up topology
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ global ADDR_TYPES
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.16")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(GenerateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ ADDR_TYPES = check_address_types()
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+
+###########################
+# Local APIs
+###########################
+
+
+def next_hop_per_address_family(
+ tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP
+):
+ """
+ This function returns link_local or global next_hop per address-family
+ """
+
+ intferface = topo["routers"][peer]["links"]["{}".format(dut)]["interface"]
+ if addr_type == "ipv6" and "link_local" in preferred_next_hop:
+ next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface)
+ else:
+ next_hop = next_hop_dict[addr_type]
+
+ return next_hop
+
+
+###########################
+# TESTCASES
+###########################
+
+
+def test_verify_graceful_shutdown_functionality_with_eBGP_peers_p0(request):
+ """
+ Verify graceful-shutdown functionality with eBGP peers
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ reset_config_on_routers(tgen)
+
+ step("Done in base config: Configure base config as per the topology")
+ step("Base config should be up, verify using BGP convergence")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Done in base config: Advertise prefixes from R1")
+ step("Verify BGP routes are received at R3 with best path from R3 to R1")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r3", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r3", "r4", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("On R1 configure:")
+ step("Create standard bgp community-list to permit graceful-shutdown:")
+ input_dict_1 = {
+ "r1": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "GSHUT",
+ "value": "graceful-shutdown",
+ }
+ ]
+ }
+ }
+
+ result = create_bgp_community_lists(tgen, input_dict_1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create route-map to set community GSHUT in OUT direction")
+
+ input_dict_2 = {
+ "r1": {
+ "route_maps": {
+ "GSHUT-OUT": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {"community": {"num": "graceful-shutdown"}},
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "GSHUT-OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "GSHUT-OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "FRR is setting local-pref to 0 by-default on receiver GSHUT community, "
+ "below step is not needed, but keeping for reference"
+ )
+ step(
+ "On R3, apply route-map IN direction to match GSHUT community "
+ "and set local-preference to 0."
+ )
+
+ step(
+ "Verify BGP convergence on R3 and ensure all the neighbours state "
+ "is established"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP routes on R3:")
+ step("local pref for routes coming from R1 is set to 0.")
+
+ for addr_type in ADDR_TYPES:
+ rmap_dict = {"r1": {"route_maps": {"GSHUT-OUT": [{"set": {"locPrf": 0}}],}}}
+
+ static_routes = [NETWORK[addr_type]]
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, static_routes, "GSHUT-OUT", rmap_dict
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Ensure that best path is selected from R4 to R3.")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r3", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r3", "r4", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_restarting_zebra_bgpd_staticd_frr_with_eBGP_peers_p0(request):
+ """
+ Verify graceful-shutdown functionality when daemons bgpd/zebra/staticd and
+ frr services are restarted with eBGP peers
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ reset_config_on_routers(tgen)
+
+ step("Done in base config: Configure base config as per the topology")
+ step("Base config should be up, verify using BGP convergence")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Done in base config: Advertise prefixes from R1")
+ step("Verify BGP routes are received at R3 with best path from R3 to R1")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r3", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r3", "r4", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("On R1 configure:")
+ step("Create standard bgp community-list to permit graceful-shutdown:")
+ input_dict_1 = {
+ "r1": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "GSHUT",
+ "value": "graceful-shutdown",
+ }
+ ]
+ }
+ }
+
+ result = create_bgp_community_lists(tgen, input_dict_1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create route-map to set community GSHUT in OUT direction")
+
+ input_dict_2 = {
+ "r1": {
+ "route_maps": {
+ "GSHUT-OUT": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {"community": {"num": "graceful-shutdown"}},
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "GSHUT-OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "GSHUT-OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "FRR is setting local-pref to 0 by-default on receiver GSHUT community, "
+ "below step is not needed, but keeping for reference"
+ )
+ step(
+ "On R3, apply route-map IN direction to match GSHUT community "
+ "and set local-preference to 0."
+ )
+
+ step(
+ "Verify BGP convergence on R3 and ensure all the neighbours state "
+ "is established"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP routes on R3:")
+ step("local pref for routes coming from R1 is set to 0.")
+
+ for addr_type in ADDR_TYPES:
+ rmap_dict = {"r1": {"route_maps": {"GSHUT-OUT": [{"set": {"locPrf": 0}}]}}}
+
+ static_routes = [NETWORK[addr_type]]
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, static_routes, "GSHUT-OUT", rmap_dict
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Ensure that best path is selected from R4 to R3.")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r3", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r3", "r4", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Restart daemons and frr services")
+
+ for daemon in ["bgpd", "zebra", "staticd", "frr"]:
+ if daemon != "frr":
+ kill_router_daemons(tgen, "r3", ["staticd"])
+ start_router_daemons(tgen, "r3", ["staticd"])
+ else:
+ stop_router(tgen, "r3")
+ start_router(tgen, "r3")
+
+ step(
+ "Verify BGP convergence on R3 and ensure all the neighbours state "
+ "is established"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify BGP routes on R3:")
+ step("local pref for routes coming from R1 is set to 0.")
+
+ for addr_type in ADDR_TYPES:
+ rmap_dict = {"r1": {"route_maps": {"GSHUT-OUT": [{"set": {"locPrf": 0}}]}}}
+
+ static_routes = [NETWORK[addr_type]]
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, static_routes, "GSHUT-OUT", rmap_dict
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Ensure that best path is selected from R4 to R3.")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r3", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r3", "r4", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py b/tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py
new file mode 100644
index 0000000000..69f4916374
--- /dev/null
+++ b/tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py
@@ -0,0 +1,676 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# 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 VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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.
+#
+
+
+"""
+Following tests are covered to test ecmp functionality on BGP GSHUT.
+1. Verify graceful-shutdown functionality with iBGP peers
+2. Verify graceful-shutdown functionality after
+ deleting/re-adding route-map with iBGP peers
+"""
+
+import os
+import sys
+import time
+import json
+import pytest
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from mininet.topo import Topo
+from time import sleep
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ interface_status,
+ reset_config_on_routers,
+ step,
+ get_frr_ipv6_linklocal,
+ kill_router_daemons,
+ start_router_daemons,
+ stop_router,
+ start_router,
+ create_route_maps,
+ create_bgp_community_lists,
+ delete_route_maps,
+ required_linux_kernel_version,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ clear_bgp,
+ verify_bgp_rib,
+ verify_bgp_attributes,
+)
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology and configuration creation
+jsonFile = "{}/ibgp_gshut_topo1.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ logger.info("Could not read file:", jsonFile)
+
+# Global variables
+NETWORK = {"ipv4": "100.0.10.1/32", "ipv6": "1::1/128"}
+NEXT_HOP_IP_1 = {"ipv4": "10.0.3.1", "ipv6": "fd00:0:0:3::1"}
+NEXT_HOP_IP_2 = {"ipv4": "10.0.4.1", "ipv6": "fd00:0:0:2::1"}
+PREFERRED_NEXT_HOP = "link_local"
+BGP_CONVERGENCE = False
+
+
+class GenerateTopo(Topo):
+ """
+ Test topology builder
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ # This function only purpose is to create topology
+ # as defined in input json file.
+ #
+ # Create topology (setup module)
+ # Creating 2 routers topology, r1, r2in IBGP
+ # Bring up topology
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ global ADDR_TYPES
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.16")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(GenerateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ ADDR_TYPES = check_address_types()
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+
+###########################
+# Local APIs
+###########################
+
+
+def next_hop_per_address_family(
+ tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP
+):
+ """
+ This function returns link_local or global next_hop per address-family
+ """
+
+ intferface = topo["routers"][peer]["links"]["{}".format(dut)]["interface"]
+ if addr_type == "ipv6" and "link_local" in preferred_next_hop:
+ next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface)
+ else:
+ next_hop = next_hop_dict[addr_type]
+
+ return next_hop
+
+
+###########################
+# TESTCASES
+###########################
+
+
+def test_verify_graceful_shutdown_functionality_with_iBGP_peers_p0(request):
+ """
+ Verify graceful-shutdown functionality with iBGP peers
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ reset_config_on_routers(tgen)
+
+ step("Done in base config: Configure base config as per the topology")
+ step("Base config should be up, verify using BGP convergence")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Done in base config: Advertise prefixes from R1")
+ step("Verify BGP routes are received at R4 with best path from R3 to R1")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r4"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r4", "r2", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r4", "r3", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("On R1 configure:")
+ step("Create standard bgp community-list to permit graceful-shutdown:")
+ input_dict_1 = {
+ "r1": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "GSHUT",
+ "value": "graceful-shutdown",
+ }
+ ]
+ }
+ }
+
+ result = create_bgp_community_lists(tgen, input_dict_1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create route-map to set community GSHUT in OUT direction")
+
+ input_dict_2 = {
+ "r1": {
+ "route_maps": {
+ "GSHUT-OUT": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {"community": {"num": "graceful-shutdown"}},
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "GSHUT-OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "GSHUT-OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "FRR is setting local-pref to 0 by-default on receiver GSHUT community, "
+ "below step is not needed, but keeping for reference"
+ )
+ step(
+ "On R3, apply route-map IN direction to match GSHUT community "
+ "and set local-preference to 0."
+ )
+
+ step(
+ "Verify BGP convergence on R4 and ensure all the neighbours state "
+ "is established"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP routes on R4:")
+ step("local pref for routes coming from R1 is set to 0.")
+
+ for addr_type in ADDR_TYPES:
+ rmap_dict = {"r1": {"route_maps": {"GSHUT-OUT": [{"set": {"locPrf": 0}}],}}}
+
+ static_routes = [NETWORK[addr_type]]
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, static_routes, "GSHUT-OUT", rmap_dict
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Ensure that best path is selected from R4 to R3.")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r4"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r4", "r2", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r4", "r3", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_deleting_re_adding_route_map_with_iBGP_peers_p0(request):
+ """
+ Verify graceful-shutdown functionality after deleting/re-adding route-map
+ with iBGP peers
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ reset_config_on_routers(tgen)
+
+ step("Done in base config: Configure base config as per the topology")
+ step("Base config should be up, verify using BGP convergence")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Done in base config: Advertise prefixes from R1")
+ step("Verify BGP routes are received at R4 with best path from R3 to R1")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r4"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r4", "r2", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r4", "r3", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("On R1 configure:")
+ step("Create standard bgp community-list to permit graceful-shutdown:")
+ input_dict_1 = {
+ "r1": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "GSHUT",
+ "value": "graceful-shutdown",
+ }
+ ]
+ }
+ }
+
+ result = create_bgp_community_lists(tgen, input_dict_1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create route-map to set community GSHUT in OUT direction")
+
+ input_dict_2 = {
+ "r1": {
+ "route_maps": {
+ "GSHUT-OUT": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {"community": {"num": "graceful-shutdown"}},
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "GSHUT-OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "GSHUT-OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "FRR is setting local-pref to 0 by-default on receiver GSHUT community, "
+ "below step is not needed, but keeping for reference"
+ )
+ step(
+ "On R3, apply route-map IN direction to match GSHUT community "
+ "and set local-preference to 0."
+ )
+
+ step(
+ "Verify BGP convergence on R4 and ensure all the neighbours state "
+ "is established"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP routes on R4:")
+ step("local pref for routes coming from R1 is set to 0.")
+
+ for addr_type in ADDR_TYPES:
+ rmap_dict = {"r1": {"route_maps": {"GSHUT-OUT": [{"set": {"locPrf": 0}}],}}}
+
+ static_routes = [NETWORK[addr_type]]
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, static_routes, "GSHUT-OUT", rmap_dict
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Ensure that best path is selected from R4 to R3.")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r4"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r4", "r2", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r4", "r3", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete route-map from R1")
+ del_rmap_dict = {
+ "r1": {
+ "route_maps": {
+ "GSHUT-OUT": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {
+ "community": {"num": "graceful-shutdown", "delete": True}
+ },
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, del_rmap_dict)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify BGP convergence on R3 and ensure that all neighbor state "
+ "is established"
+ )
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP routes on R4:")
+ step("Ensure that best path is selected from R1->R3")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r4"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r4", "r2", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r4", "r3", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop=[next_hop1])
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=[next_hop1])
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Re-add route-map in R1")
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify BGP convergence on R3 and ensure all the neighbours state "
+ "is established"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP routes on R4:")
+ step("local pref for routes coming from R1 is set to 0.")
+
+ for addr_type in ADDR_TYPES:
+ rmap_dict = {"r1": {"route_maps": {"GSHUT-OUT": [{"set": {"locPrf": 0}}]}}}
+
+ static_routes = [NETWORK[addr_type]]
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, static_routes, "GSHUT-OUT", rmap_dict
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Ensure that best path is selected from R4 to R3.")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r4"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r4", "r2", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r4", "r3", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/multicast_pim_sm_topo2/test_multicast_pim_sm_topo2.py b/tests/topotests/multicast_pim_sm_topo2/test_multicast_pim_sm_topo2.py
index f1f130826f..ad3b77b843 100755
--- a/tests/topotests/multicast_pim_sm_topo2/test_multicast_pim_sm_topo2.py
+++ b/tests/topotests/multicast_pim_sm_topo2/test_multicast_pim_sm_topo2.py
@@ -519,8 +519,11 @@ def test_verify_mroute_and_traffic_when_pimd_restarted_p2(request):
data["oil"],
expected=False,
)
- assert result is not True, "Testcase {} : Failed \n mroutes are"
- " still present \n Error: {}".format(tc_name, result)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
logger.info("Expected Behavior: {}".format(result))
write_test_footer(tc_name)
@@ -727,8 +730,11 @@ def test_verify_mroute_and_traffic_when_frr_restarted_p2(request):
data["oil"],
expected=False,
)
- assert result is not True, "Testcase {} : Failed \n mroutes are"
- " still present \n Error: {}".format(tc_name, result)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
logger.info("Expected Behavior: {}".format(result))
write_test_footer(tc_name)
@@ -802,8 +808,9 @@ def test_verify_SPT_switchover_when_RPT_and_SPT_path_is_different_p0(request):
state_before = verify_pim_interface_traffic(tgen, state_dict)
assert isinstance(
state_before, dict
- ), "Testcase {} : Failed \n state_before is not dictionary \n "
- "Error: {}".format(tc_name, result)
+ ), "Testcase {} : Failed \n state_before is not dictionary \nError: {}".format(
+ tc_name, result
+ )
result = iperfSendTraffic(tgen, "i2", _IGMP_JOIN_RANGE, 32, 2500)
assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
@@ -885,8 +892,9 @@ def test_verify_SPT_switchover_when_RPT_and_SPT_path_is_different_p0(request):
state_after = verify_pim_interface_traffic(tgen, state_dict)
assert isinstance(
state_after, dict
- ), "Testcase {} : Failed \n state_before is not dictionary \n "
- "Error: {}".format(tc_name, result)
+ ), "Testcase {} : Failed \n state_before is not dictionary \nError: {}".format(
+ tc_name, result
+ )
result = verify_state_incremented(state_before, state_after)
assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
@@ -1167,8 +1175,11 @@ def test_verify_mroute_after_shut_noshut_of_upstream_interface_p1(request):
data["oil"],
expected=False,
)
- assert result is not True, "Testcase {} : Failed \n mroutes are "
- "still present \n Error: {}".format(tc_name, result)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
logger.info("Expected Behavior: {}".format(result))
write_test_footer(tc_name)
@@ -1718,8 +1729,11 @@ def test_verify_mroute_when_5_different_receiver_joining_same_sources_p0(request
data["oil"],
expected=False,
)
- assert result is not True, "Testcase {} : Failed \n mroutes are"
- " still present \n Error: {}".format(tc_name, result)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
logger.info("Expected Behavior: {}".format(result))
step(
diff --git a/tools/.gitignore b/tools/.gitignore
index 63a5b61c35..1cc343a11a 100644
--- a/tools/.gitignore
+++ b/tools/.gitignore
@@ -8,3 +8,4 @@
/frrcommon.sh
/frr.service
/frr@.service
+/frr-llvm-cg
diff --git a/tools/etc/frr/support_bundle_commands.conf b/tools/etc/frr/support_bundle_commands.conf
index f902d3dd21..0d64320488 100644
--- a/tools/etc/frr/support_bundle_commands.conf
+++ b/tools/etc/frr/support_bundle_commands.conf
@@ -40,6 +40,8 @@ CMD_LIST_START
show zebra
show zebra client summary
show zebra router table summary
+show ip zebra route dump
+show ipv6 zebra route dump
show ip nht vrf all
show ipv6 nht vrf all
show ip route vrf all
diff --git a/tools/frr-llvm-cg.c b/tools/frr-llvm-cg.c
index 84a756a376..fbcf9222d2 100644
--- a/tools/frr-llvm-cg.c
+++ b/tools/frr-llvm-cg.c
@@ -50,11 +50,15 @@
#include <json-c/json.h>
+#include "frr-llvm-debuginfo.h"
+
/* if you want to use this without the special FRRouting defines,
* remove the following #define
*/
#define FRR_SPECIFIC
+static struct dbginfo *dbginfo;
+
static void dbgloc_add(struct json_object *jsobj, LLVMValueRef obj)
{
unsigned file_len = 0;
@@ -85,6 +89,70 @@ static struct json_object *js_get_or_make(struct json_object *parent,
return ret;
}
+static bool try_struct_fptr(struct json_object *js_call, LLVMValueRef gep,
+ const char *prefix)
+{
+ unsigned long long val = 0;
+ bool ret = false;
+ LLVMTypeRef ptrtype = LLVMTypeOf(LLVMGetOperand(gep, 0));
+ LLVMValueRef idx;
+
+ /* middle steps like struct a -> struct b a_member; -> fptr */
+ for (int i = 1; ptrtype && i < LLVMGetNumOperands(gep) - 1; i++) {
+ if (LLVMGetTypeKind(ptrtype) == LLVMPointerTypeKind
+ || LLVMGetTypeKind(ptrtype) == LLVMArrayTypeKind
+ || LLVMGetTypeKind(ptrtype) == LLVMVectorTypeKind) {
+ ptrtype = LLVMGetElementType(ptrtype);
+ continue;
+ }
+
+ if (LLVMGetTypeKind(ptrtype) != LLVMStructTypeKind)
+ return false;
+
+ idx = LLVMGetOperand(gep, i);
+ if (!LLVMIsConstant(idx))
+ return false;
+ val = LLVMConstIntGetZExtValue(idx);
+
+ unsigned n = LLVMGetNumContainedTypes(ptrtype);
+ LLVMTypeRef arr[n];
+
+ if (val > n)
+ return false;
+
+ LLVMGetSubtypes(ptrtype, arr);
+ ptrtype = arr[val];
+ }
+
+ if (!ptrtype)
+ return false;
+
+ idx = LLVMGetOperand(gep, LLVMGetNumOperands(gep) - 1);
+ if (!LLVMIsConstant(idx))
+ return false;
+
+ val = LLVMConstIntGetZExtValue(idx);
+
+ char *sname = NULL, *mname = NULL;
+
+ if (dbginfo_struct_member(dbginfo, ptrtype, val, &sname, &mname)) {
+ fprintf(stderr, "%s: call to struct %s->%s\n", prefix, sname,
+ mname);
+
+ json_object_object_add(js_call, "type",
+ json_object_new_string("struct_memb"));
+ json_object_object_add(js_call, "struct",
+ json_object_new_string(sname));
+ json_object_object_add(js_call, "member",
+ json_object_new_string(mname));
+ ret = true;
+ }
+ free(sname);
+ free(mname);
+
+ return ret;
+}
+
static bool details_fptr_vars = false;
static bool details_fptr_consts = true;
@@ -175,6 +243,34 @@ static void walk_const_fptrs(struct json_object *js_call, LLVMValueRef value,
prefix, hdr_written);
return;
+ case LLVMConstantExprValueKind:
+ switch (LLVMGetConstOpcode(value)) {
+ case LLVMGetElementPtr:
+ if (try_struct_fptr(js_call, value, prefix)) {
+ *hdr_written = true;
+ return;
+ }
+
+ fprintf(stderr,
+ "%s: calls function pointer from unhandled const GEP\n",
+ prefix);
+ *hdr_written = true;
+ /* fallthru */
+ default:
+ /* to help the user / development */
+ if (!*hdr_written) {
+ fprintf(stderr,
+ "%s: calls function pointer from constexpr\n",
+ prefix);
+ *hdr_written = true;
+ }
+ dump = LLVMPrintValueToString(value);
+ fprintf(stderr, "%s- [opcode=%d] %s\n", prefix,
+ LLVMGetConstOpcode(value), dump);
+ LLVMDisposeMessage(dump);
+ }
+ return;
+
default:
/* to help the user / development */
if (!*hdr_written) {
@@ -197,7 +293,7 @@ static void walk_const_fptrs(struct json_object *js_call, LLVMValueRef value,
#ifdef FRR_SPECIFIC
static bool is_thread_sched(const char *name, size_t len)
{
-#define thread_prefix "funcname_"
+#define thread_prefix "_"
static const char *const names[] = {
thread_prefix "thread_add_read_write",
thread_prefix "thread_add_timer",
@@ -218,6 +314,225 @@ static bool is_thread_sched(const char *name, size_t len)
}
#endif
+static bool _check_val(bool cond, const char *text, LLVMValueRef dumpval)
+{
+ if (cond)
+ return true;
+
+ char *dump = LLVMPrintValueToString(dumpval);
+ fprintf(stderr, "check failed: %s\ndump:\n\t%s\n", text, dump);
+ LLVMDisposeMessage(dump);
+ return false;
+}
+
+#define check_val(cond, dump) \
+ if (!_check_val(cond, #cond, dump)) \
+ return;
+
+static char *get_string(LLVMValueRef value)
+{
+ if (!LLVMIsAConstant(value))
+ return strdup("!NOT-A-CONST");
+
+ if (LLVMGetValueKind(value) == LLVMConstantExprValueKind
+ && LLVMGetConstOpcode(value) == LLVMGetElementPtr) {
+ value = LLVMGetOperand(value, 0);
+
+ if (!LLVMIsAConstant(value))
+ return strdup("!NOT-A-CONST-2");
+ }
+
+ if (LLVMIsAGlobalVariable(value))
+ value = LLVMGetInitializer(value);
+
+ size_t len = 0;
+ const char *sval = LLVMGetAsString(value, &len);
+
+ return strndup(sval, len);
+}
+
+static void handle_yang_module(struct json_object *js_special,
+ LLVMValueRef yang_mod)
+{
+ check_val(LLVMIsAGlobalVariable(yang_mod), yang_mod);
+
+ LLVMValueRef value;
+
+ value = LLVMGetInitializer(yang_mod);
+ LLVMValueKind kind = LLVMGetValueKind(value);
+
+ check_val(kind == LLVMConstantStructValueKind, value);
+
+ size_t var_len = 0;
+ const char *var_name = LLVMGetValueName2(yang_mod, &var_len);
+ char buf_name[var_len + 1];
+
+ memcpy(buf_name, var_name, var_len);
+ buf_name[var_len] = '\0';
+
+ struct json_object *js_yang, *js_yangmod, *js_items;
+
+ js_yang = js_get_or_make(js_special, "yang", json_object_new_object);
+ js_yangmod = js_get_or_make(js_yang, buf_name, json_object_new_object);
+ js_items = js_get_or_make(js_yangmod, "items", json_object_new_array);
+
+ char *mod_name = get_string(LLVMGetOperand(value, 0));
+ json_object_object_add(js_yangmod, "name",
+ json_object_new_string(mod_name));
+ free(mod_name);
+
+ value = LLVMGetOperand(value, 1);
+ kind = LLVMGetValueKind(value);
+ check_val(kind == LLVMConstantArrayValueKind, value);
+
+ unsigned len = LLVMGetArrayLength(LLVMTypeOf(value));
+
+ for (unsigned i = 0; i < len - 1; i++) {
+ struct json_object *js_item, *js_cbs;
+ LLVMValueRef item = LLVMGetOperand(value, i);
+ char *xpath = get_string(LLVMGetOperand(item, 0));
+
+ js_item = json_object_new_object();
+ json_object_array_add(js_items, js_item);
+
+ json_object_object_add(js_item, "xpath",
+ json_object_new_string(xpath));
+ js_cbs = js_get_or_make(js_item, "cbs", json_object_new_object);
+
+ free(xpath);
+
+ LLVMValueRef cbs = LLVMGetOperand(item, 1);
+
+ check_val(LLVMGetValueKind(cbs) == LLVMConstantStructValueKind,
+ value);
+
+ LLVMTypeRef cbs_type = LLVMTypeOf(cbs);
+ unsigned cblen = LLVMCountStructElementTypes(cbs_type);
+
+ for (unsigned i = 0; i < cblen; i++) {
+ LLVMValueRef cb = LLVMGetOperand(cbs, i);
+
+ char *sname = NULL;
+ char *mname = NULL;
+
+ if (dbginfo_struct_member(dbginfo, cbs_type, i, &sname,
+ &mname)) {
+ (void)0;
+ }
+
+ if (LLVMIsAFunction(cb)) {
+ size_t fn_len;
+ const char *fn_name;
+
+ fn_name = LLVMGetValueName2(cb, &fn_len);
+
+ json_object_object_add(
+ js_cbs, mname,
+ json_object_new_string_len(fn_name,
+ fn_len));
+ }
+
+ free(sname);
+ free(mname);
+ }
+ }
+}
+
+static void handle_daemoninfo(struct json_object *js_special,
+ LLVMValueRef daemoninfo)
+{
+ check_val(LLVMIsAGlobalVariable(daemoninfo), daemoninfo);
+
+ LLVMTypeRef type;
+ LLVMValueRef value;
+ unsigned len;
+
+ type = LLVMGlobalGetValueType(daemoninfo);
+ value = LLVMGetInitializer(daemoninfo);
+ LLVMValueKind kind = LLVMGetValueKind(value);
+
+ check_val(kind == LLVMConstantStructValueKind, value);
+
+ int yang_idx = -1;
+
+ len = LLVMCountStructElementTypes(type);
+
+ LLVMTypeRef fieldtypes[len];
+ LLVMGetSubtypes(type, fieldtypes);
+
+ for (unsigned i = 0; i < len; i++) {
+ LLVMTypeRef t = fieldtypes[i];
+
+ if (LLVMGetTypeKind(t) != LLVMPointerTypeKind)
+ continue;
+ t = LLVMGetElementType(t);
+ if (LLVMGetTypeKind(t) != LLVMPointerTypeKind)
+ continue;
+ t = LLVMGetElementType(t);
+ if (LLVMGetTypeKind(t) != LLVMStructTypeKind)
+ continue;
+
+ const char *name = LLVMGetStructName(t);
+ if (!strcmp(name, "struct.frr_yang_module_info"))
+ yang_idx = i;
+ }
+
+ if (yang_idx == -1)
+ return;
+
+ LLVMValueRef yang_mods = LLVMGetOperand(value, yang_idx);
+ LLVMValueRef yang_size = LLVMGetOperand(value, yang_idx + 1);
+
+ check_val(LLVMIsConstant(yang_size), yang_size);
+
+ unsigned long long ival = LLVMConstIntGetZExtValue(yang_size);
+
+ check_val(LLVMGetValueKind(yang_mods) == LLVMConstantExprValueKind
+ && LLVMGetConstOpcode(yang_mods) == LLVMGetElementPtr,
+ yang_mods);
+
+ yang_mods = LLVMGetOperand(yang_mods, 0);
+
+ check_val(LLVMIsAGlobalVariable(yang_mods), yang_mods);
+
+ yang_mods = LLVMGetInitializer(yang_mods);
+
+ check_val(LLVMGetValueKind(yang_mods) == LLVMConstantArrayValueKind,
+ yang_mods);
+
+ len = LLVMGetArrayLength(LLVMTypeOf(yang_mods));
+
+ if (len != ival)
+ fprintf(stderr, "length mismatch - %llu vs. %u\n", ival, len);
+
+ for (unsigned i = 0; i < len; i++) {
+ char *dump;
+
+ LLVMValueRef item = LLVMGetOperand(yang_mods, i);
+ LLVMValueKind kind = LLVMGetValueKind(item);
+
+ check_val(kind == LLVMGlobalVariableValueKind
+ || kind == LLVMConstantExprValueKind,
+ item);
+
+ if (kind == LLVMGlobalVariableValueKind)
+ continue;
+
+ LLVMOpcode opcode = LLVMGetConstOpcode(item);
+ switch (opcode) {
+ case LLVMBitCast:
+ item = LLVMGetOperand(item, 0);
+ handle_yang_module(js_special, item);
+ break;
+
+ default:
+ dump = LLVMPrintValueToString(item);
+ printf("[%u] = [opcode=%u] %s\n", i, opcode, dump);
+ LLVMDisposeMessage(dump);
+ }
+ }
+}
+
static void process_call(struct json_object *js_calls,
struct json_object *js_special,
LLVMValueRef instr,
@@ -227,6 +542,9 @@ static void process_call(struct json_object *js_calls,
LLVMValueRef called = LLVMGetCalledValue(instr);
+ if (LLVMIsAInlineAsm(called))
+ return;
+
if (LLVMIsAConstantExpr(called)) {
LLVMOpcode opcode = LLVMGetConstOpcode(called);
@@ -277,6 +595,12 @@ static void process_call(struct json_object *js_calls,
snprintf(prefix, sizeof(prefix), "%.*s:%d:%.*s()",
(int)file_len, file, line, (int)name_len, name_c);
+ if (LLVMIsALoadInst(called)
+ && LLVMIsAGetElementPtrInst(LLVMGetOperand(called, 0))
+ && try_struct_fptr(js_call, LLVMGetOperand(called, 0),
+ prefix))
+ goto out_struct_fptr;
+
while (LLVMIsALoadInst(last) || LLVMIsAGetElementPtrInst(last))
/* skipping over details for GEP here, but meh. */
last = LLVMGetOperand(last, 0);
@@ -324,12 +648,11 @@ static void process_call(struct json_object *js_calls,
prefix);
} else {
char *dump = LLVMPrintValueToString(called);
- printf("\t%s\n", dump);
+ fprintf(stderr, "%s: ??? %s\n", prefix, dump);
LLVMDisposeMessage(dump);
}
- return;
#ifdef FRR_SPECIFIC
- } else if (!strcmp(called_name, "install_element")) {
+ } else if (!strcmp(called_name, "_install_element")) {
called_type = FN_INSTALL_ELEMENT;
LLVMValueRef param0 = LLVMGetOperand(instr, 0);
@@ -380,10 +703,7 @@ static void process_call(struct json_object *js_calls,
json_object_new_string_len(called_name, called_len));
LLVMValueRef fparam;
- if (strstr(called_name, "_read_"))
- fparam = LLVMGetOperand(instr, 2);
- else
- fparam = LLVMGetOperand(instr, 1);
+ fparam = LLVMGetOperand(instr, 2);
assert(fparam);
size_t target_len = 0;
@@ -434,12 +754,21 @@ static void process_call(struct json_object *js_calls,
* - zclient->* ?
*/
#endif /* FRR_SPECIFIC */
+ } else if (!strcmp(called_name, "frr_preinit")) {
+ LLVMValueRef daemoninfo = LLVMGetOperand(instr, 0);
+
+ handle_daemoninfo(js_special, daemoninfo);
+
+ json_object_object_add(
+ js_call, "target",
+ json_object_new_string_len(called_name, called_len));
} else {
json_object_object_add(
js_call, "target",
json_object_new_string_len(called_name, called_len));
}
+out_struct_fptr:
for (unsigned argno = 0; argno < n_args; argno++) {
LLVMValueRef param = LLVMGetOperand(instr, argno);
size_t target_len;
@@ -597,6 +926,8 @@ int main(int argc, char **argv)
// done with the memory buffer now, so dispose of it
LLVMDisposeMemoryBuffer(memoryBuffer);
+ dbginfo = dbginfo_load(module);
+
struct json_object *js_root, *js_funcs, *js_special;
js_root = json_object_new_object();
diff --git a/tools/frr-llvm-debuginfo.cpp b/tools/frr-llvm-debuginfo.cpp
new file mode 100644
index 0000000000..ed3ad956b8
--- /dev/null
+++ b/tools/frr-llvm-debuginfo.cpp
@@ -0,0 +1,112 @@
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+//
+// In jurisdictions that recognize copyright laws, the author or authors
+// of this software dedicate any and all copyright interest in the
+// software to the public domain. We make this dedication for the benefit
+// of the public at large and to the detriment of our heirs and
+// successors. We intend this dedication to be an overt act of
+// relinquishment in perpetuity of all present and future rights to this
+// software under copyright law.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// For more information, please refer to <http://unlicense.org/>
+
+#include <llvm-c/BitReader.h>
+#include <llvm-c/BitWriter.h>
+#include <llvm-c/Core.h>
+#include <llvm-c/DebugInfo.h>
+
+#include <llvm/IR/Module.h>
+#include <llvm/IR/Value.h>
+#include <llvm/IR/Type.h>
+#include <llvm/IR/DebugInfo.h>
+#include <llvm/IR/DebugInfoMetadata.h>
+#include <llvm/Support/raw_ostream.h>
+
+#include <map>
+
+#include "frr-llvm-debuginfo.h"
+
+/* llvm::DebugInfoFinder is unfortunately not exposed in the llvm-c API... */
+
+struct dbginfo {
+ llvm::DebugInfoFinder finder;
+ std::map<std::string, llvm::DICompositeType *> tab;
+};
+
+struct dbginfo *dbginfo_load(LLVMModuleRef _mod)
+{
+ llvm::Module *mod = llvm::unwrap(_mod);
+ struct dbginfo *info = new dbginfo();
+
+ info->finder.processModule(*mod);
+
+ for (auto ty : info->finder.types()) {
+ if (ty->getMetadataID() != llvm::Metadata::DICompositeTypeKind)
+ continue;
+
+ llvm::DICompositeType *cty = (llvm::DICompositeType *)ty;
+ /* empty forward declarations aka "struct foobar;" */
+ if (cty->getElements().size() == 0)
+ continue;
+
+ info->tab.emplace(std::move(ty->getName().str()), cty);
+ }
+
+ return info;
+}
+
+bool dbginfo_struct_member(struct dbginfo *info, LLVMTypeRef _typ,
+ unsigned long long idx, char **struct_name,
+ char **member_name)
+{
+ *struct_name = NULL;
+ *member_name = NULL;
+
+ llvm::Type *typ = llvm::unwrap(_typ);
+
+ if (!typ->isStructTy())
+ return false;
+
+ llvm::StructType *styp = (llvm::StructType *)typ;
+ auto sname = styp->getStructName();
+
+ if (!sname.startswith("struct."))
+ return false;
+ sname = sname.drop_front(7);
+
+ size_t dot = sname.find_last_of(".");
+ if (dot != sname.npos)
+ sname = sname.take_front(dot);
+
+ auto item = info->tab.find(sname.str());
+ if (item == info->tab.end())
+ return false;
+
+ auto elements = item->second->getElements();
+ if (idx >= elements.size())
+ return false;
+
+ auto elem = elements[idx];
+
+ if (elem->getMetadataID() != llvm::Metadata::DIDerivedTypeKind)
+ return false;
+
+ llvm::DIDerivedType *dtyp = (llvm::DIDerivedType *)elem;
+
+ *struct_name = strdup(sname.str().c_str());
+ *member_name = strdup(dtyp->getName().str().c_str());
+ return true;
+}
diff --git a/tools/frr-llvm-debuginfo.h b/tools/frr-llvm-debuginfo.h
new file mode 100644
index 0000000000..fca4bc1f97
--- /dev/null
+++ b/tools/frr-llvm-debuginfo.h
@@ -0,0 +1,47 @@
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+//
+// In jurisdictions that recognize copyright laws, the author or authors
+// of this software dedicate any and all copyright interest in the
+// software to the public domain. We make this dedication for the benefit
+// of the public at large and to the detriment of our heirs and
+// successors. We intend this dedication to be an overt act of
+// relinquishment in perpetuity of all present and future rights to this
+// software under copyright law.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// For more information, please refer to <http://unlicense.org/>
+
+#ifndef _FRR_LLVM_DEBUGINFO_H
+#define _FRR_LLVM_DEBUGINFO_H
+
+#include <stdbool.h>
+#include <llvm-c/Core.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct dbginfo;
+
+extern struct dbginfo *dbginfo_load(LLVMModuleRef mod);
+extern bool dbginfo_struct_member(struct dbginfo *di, LLVMTypeRef typ,
+ unsigned long long idx, char **struct_name,
+ char **member_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FRR_LLVM_DEBUGINFO_H */
diff --git a/tools/subdir.am b/tools/subdir.am
index 6a03a23baa..e4b9ecd84f 100644
--- a/tools/subdir.am
+++ b/tools/subdir.am
@@ -40,9 +40,15 @@ tools_ssd_CPPFLAGS =
llvm_version = $(shell echo __clang_major__ | $(CC) -xc -P -E -)
tools_frr_llvm_cg_CPPFLAGS = $(CPPFLAGS_BASE)
tools_frr_llvm_cg_CFLAGS = $(AM_CFLAGS) `llvm-config-$(llvm_version) --cflags`
+tools_frr_llvm_cg_CXXFLAGS = $(AM_CXXFLAGS) -O0 -ggdb3 `llvm-config-$(llvm_version) --cxxflags`
tools_frr_llvm_cg_LDFLAGS = `llvm-config-$(llvm_version) --ldflags --libs`
tools_frr_llvm_cg_SOURCES = \
tools/frr-llvm-cg.c \
+ tools/frr-llvm-debuginfo.cpp \
+ # end
+
+noinst_HEADERS += \
+ tools/frr-llvm-debuginfo.h \
# end
EXTRA_DIST += \
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index 8567edbf96..2c2c75c419 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -88,6 +88,9 @@ static void show_nexthop_detail_helper(struct vty *vty,
const struct nexthop *nexthop,
bool is_backup);
+static void show_ip_route_dump_vty(struct vty *vty, struct route_table *table);
+static void show_ip_route_nht_dump(struct vty *vty, struct nexthop *nexthop,
+ struct route_entry *re, unsigned int num);
DEFUN (ip_multicast_mode,
ip_multicast_mode_cmd,
@@ -2084,6 +2087,207 @@ DEFPY (show_route_summary,
return CMD_SUCCESS;
}
+DEFUN_HIDDEN (show_route_zebra_dump,
+ show_route_zebra_dump_cmd,
+ "show <ip|ipv6> zebra route dump [vrf VRFNAME]",
+ SHOW_STR
+ IP_STR
+ IP6_STR
+ "Zebra daemon\n"
+ "Routing table\n"
+ "All information\n"
+ VRF_CMD_HELP_STR)
+{
+ afi_t afi = AFI_IP;
+ struct route_table *table;
+ const char *vrf_name = NULL;
+ int idx = 0;
+
+ afi = strmatch(argv[1]->text, "ipv6") ? AFI_IP6 : AFI_IP;
+
+ if (argv_find(argv, argc, "vrf", &idx))
+ vrf_name = argv[++idx]->arg;
+
+ if (!vrf_name) {
+ struct vrf *vrf;
+ struct zebra_vrf *zvrf;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ zvrf = vrf->info;
+ if ((zvrf == NULL)
+ || (zvrf->table[afi][SAFI_UNICAST] == NULL))
+ continue;
+
+ table = zvrf->table[afi][SAFI_UNICAST];
+ show_ip_route_dump_vty(vty, table);
+ }
+ } else {
+ vrf_id_t vrf_id = VRF_DEFAULT;
+
+ VRF_GET_ID(vrf_id, vrf_name, true);
+
+ table = zebra_vrf_table(afi, SAFI_UNICAST, vrf_id);
+ if (!table)
+ return CMD_SUCCESS;
+
+ show_ip_route_dump_vty(vty, table);
+ }
+
+ return CMD_SUCCESS;
+}
+
+static void show_ip_route_nht_dump(struct vty *vty, struct nexthop *nexthop,
+ struct route_entry *re, unsigned int num)
+{
+
+ char buf[SRCDEST2STR_BUFFER];
+
+ vty_out(vty, " Nexthop %u:\n", num);
+ vty_out(vty, " type: %u\n", nexthop->type);
+ vty_out(vty, " flags: %u\n", nexthop->flags);
+ switch (nexthop->type) {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ vty_out(vty, " ip address: %s\n",
+ inet_ntop(AF_INET, &nexthop->gate.ipv4, buf,
+ sizeof(buf)));
+ vty_out(vty, " afi: ipv4\n");
+
+ if (nexthop->ifindex) {
+ vty_out(vty, " interface index: %d\n",
+ nexthop->ifindex);
+ vty_out(vty, " interface name: %s\n",
+ ifindex2ifname(nexthop->ifindex,
+ nexthop->vrf_id));
+ }
+
+ if (nexthop->src.ipv4.s_addr
+ && (inet_ntop(AF_INET, &nexthop->src.ipv4, buf,
+ sizeof(buf))))
+ vty_out(vty, " source: %s\n", buf);
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ vty_out(vty, " ip: %s\n",
+ inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf,
+ sizeof(buf)));
+ vty_out(vty, " afi: ipv6\n");
+
+ if (nexthop->ifindex) {
+ vty_out(vty, " interface index: %d\n",
+ nexthop->ifindex);
+ vty_out(vty, " interface name: %s\n",
+ ifindex2ifname(nexthop->ifindex,
+ nexthop->vrf_id));
+ }
+
+ if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) {
+ if (inet_ntop(AF_INET6, &nexthop->src.ipv6, buf,
+ sizeof(buf)))
+ vty_out(vty, " source: %s\n", buf);
+ }
+ break;
+ case NEXTHOP_TYPE_IFINDEX:
+ vty_out(vty,
+ " Nexthop is an interface (directly connected).\n");
+ vty_out(vty, " interface index: %d\n", nexthop->ifindex);
+ vty_out(vty, " interface name: %s\n",
+ ifindex2ifname(nexthop->ifindex, nexthop->vrf_id));
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ vty_out(vty, " Nexthop type is blackhole.\n");
+
+ switch (nexthop->bh_type) {
+ case BLACKHOLE_REJECT:
+ vty_out(vty, " Blackhole type: reject\n");
+ break;
+ case BLACKHOLE_ADMINPROHIB:
+ vty_out(vty,
+ " Blackhole type: admin-prohibited\n");
+ break;
+ case BLACKHOLE_NULL:
+ vty_out(vty, " Blackhole type: NULL0\n");
+ break;
+ case BLACKHOLE_UNSPEC:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void show_ip_route_dump_vty(struct vty *vty, struct route_table *table)
+{
+ struct route_node *rn;
+ struct route_entry *re;
+ char buf[SRCDEST2STR_BUFFER];
+ char time[20];
+ time_t uptime;
+ struct tm tm;
+ struct timeval tv;
+ struct nexthop *nexthop = NULL;
+ int nexthop_num = 0;
+
+ vty_out(vty, "\nIPv4/IPv6 Routing table dump\n");
+ vty_out(vty, "----------------------------\n");
+
+ for (rn = route_top(table); rn; rn = route_next(rn)) {
+ RNODE_FOREACH_RE (rn, re) {
+ vty_out(vty, "Route: %s\n",
+ srcdest_rnode2str(rn, buf, sizeof(buf)));
+ vty_out(vty, " protocol: %s\n",
+ zebra_route_string(re->type));
+ vty_out(vty, " instance: %u\n", re->instance);
+ vty_out(vty, " VRF ID: %u\n", re->vrf_id);
+ vty_out(vty, " VRF name: %s\n",
+ vrf_id_to_name(re->vrf_id));
+ vty_out(vty, " flags: %u\n", re->flags);
+
+ if (re->type != ZEBRA_ROUTE_CONNECT) {
+ vty_out(vty, " distance: %u\n", re->distance);
+ vty_out(vty, " metric: %u\n", re->metric);
+ }
+
+ vty_out(vty, " tag: %u\n", re->tag);
+
+ uptime = monotime(&tv);
+ uptime -= re->uptime;
+ gmtime_r(&uptime, &tm);
+
+ if (uptime < ONE_DAY_SECOND)
+ snprintf(time, sizeof(time), "%02d:%02d:%02d",
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ else if (uptime < ONE_WEEK_SECOND)
+ snprintf(time, sizeof(time), "%dd%02dh%02dm",
+ tm.tm_yday, tm.tm_hour, tm.tm_min);
+ else
+ snprintf(time, sizeof(time), "%02dw%dd%02dh",
+ tm.tm_yday / 7,
+ tm.tm_yday - ((tm.tm_yday / 7) * 7),
+ tm.tm_hour);
+
+ vty_out(vty, " status: %u\n", re->status);
+ vty_out(vty, " nexthop_num: %u\n",
+ nexthop_group_nexthop_num(&(re->nhe->nhg)));
+ vty_out(vty, " nexthop_active_num: %u\n",
+ nexthop_group_active_nexthop_num(
+ &(re->nhe->nhg)));
+ vty_out(vty, " table: %u\n", re->table);
+ vty_out(vty, " uptime: %s\n", time);
+
+ for (ALL_NEXTHOPS_PTR(&(re->nhe->nhg), nexthop)) {
+ nexthop_num++;
+ show_ip_route_nht_dump(vty, nexthop, re,
+ nexthop_num);
+ }
+
+ nexthop_num = 0;
+ vty_out(vty, "\n");
+ }
+ }
+}
+
static void vty_show_ip_route_summary(struct vty *vty,
struct route_table *table, bool use_json)
{
@@ -4191,6 +4395,7 @@ void zebra_vty_init(void)
install_element(VIEW_NODE, &show_pbr_ipset_cmd);
install_element(VIEW_NODE, &show_pbr_iptable_cmd);
+ install_element(VIEW_NODE, &show_route_zebra_dump_cmd);
install_element(CONFIG_NODE, &evpn_mh_mac_holdtime_cmd);
install_element(CONFIG_NODE, &evpn_mh_neigh_holdtime_cmd);