diff options
106 files changed, 2407 insertions, 1008 deletions
diff --git a/bfdd/bfd.c b/bfdd/bfd.c index c16912060c..57e5909f57 100644 --- a/bfdd/bfd.c +++ b/bfdd/bfd.c @@ -2158,6 +2158,7 @@ void bfd_session_update_vrf_name(struct bfd_session *bs, struct vrf *vrf) if (yang_module_find("frr-bfdd") && bs->key.vrfname[0]) { struct lyd_node *bfd_dnode; char xpath[XPATH_MAXLEN], xpath_srcaddr[XPATH_MAXLEN + 32]; + char oldpath[XPATH_MAXLEN], newpath[XPATH_MAXLEN]; char addr_buf[INET6_ADDRSTRLEN]; int slen; @@ -2178,14 +2179,19 @@ void bfd_session_update_vrf_name(struct bfd_session *bs, struct vrf *vrf) "[interface='%s']", bs->key.ifname); else slen += snprintf(xpath + slen, sizeof(xpath) - slen, - "[interface='']"); + "[interface='*']"); snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']/vrf", bs->key.vrfname); bfd_dnode = yang_dnode_get(running_config->dnode, xpath, bs->key.vrfname); if (bfd_dnode) { + yang_dnode_get_path(bfd_dnode->parent, oldpath, + sizeof(oldpath)); yang_dnode_change_leaf(bfd_dnode, vrf->name); + yang_dnode_get_path(bfd_dnode->parent, newpath, + sizeof(newpath)); + nb_running_move_tree(oldpath, newpath); running_config->version++; } } diff --git a/bfdd/bfdd.c b/bfdd/bfdd.c index 6b8f2c5d46..098e7a289e 100644 --- a/bfdd/bfdd.c +++ b/bfdd/bfdd.c @@ -21,6 +21,8 @@ #include <zebra.h> #include "filter.h" +#include "if.h" +#include "vrf.h" #include "bfd.h" #include "bfdd_nb.h" diff --git a/bfdd/bfdd_cli.c b/bfdd/bfdd_cli.c index d115684b1c..ddec83397d 100644 --- a/bfdd/bfdd_cli.c +++ b/bfdd/bfdd_cli.c @@ -127,7 +127,7 @@ DEFPY_YANG_NOSH( "[interface='%s']", ifname); else slen += snprintf(xpath + slen, sizeof(xpath) - slen, - "[interface='']"); + "[interface='*']"); if (vrf) snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", vrf); else @@ -185,7 +185,7 @@ DEFPY_YANG( "[interface='%s']", ifname); else slen += snprintf(xpath + slen, sizeof(xpath) - slen, - "[interface='']"); + "[interface='*']"); if (vrf) snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", vrf); else @@ -218,7 +218,7 @@ static void _bfd_cli_show_peer(struct vty *vty, struct lyd_node *dnode, if (strcmp(vrf, VRF_DEFAULT_NAME)) vty_out(vty, " vrf %s", vrf); - if (ifname[0]) + if (strcmp(ifname, "*")) vty_out(vty, " interface %s", ifname); vty_out(vty, "\n"); diff --git a/bfdd/bfdd_nb_config.c b/bfdd/bfdd_nb_config.c index 0046bc625b..fe6a0b7905 100644 --- a/bfdd/bfdd_nb_config.c +++ b/bfdd/bfdd_nb_config.c @@ -45,11 +45,11 @@ static void bfd_session_get_key(bool mhop, const struct lyd_node *dnode, if (yang_dnode_exists(dnode, "./source-addr")) strtosa(yang_dnode_get_string(dnode, "./source-addr"), &lsa); - /* Get optional interface and vrf names. */ - if (yang_dnode_exists(dnode, "./interface")) - ifname = yang_dnode_get_string(dnode, "./interface"); - if (yang_dnode_exists(dnode, "./vrf")) - vrfname = yang_dnode_get_string(dnode, "./vrf"); + ifname = yang_dnode_get_string(dnode, "./interface"); + vrfname = yang_dnode_get_string(dnode, "./vrf"); + + if (strcmp(ifname, "*") == 0) + ifname = NULL; /* Generate the corresponding key. */ gen_bfd_key(bk, &psa, &lsa, mhop, ifname, vrfname); @@ -72,18 +72,10 @@ static int bfd_session_create(enum nb_event event, const struct lyd_node *dnode, */ yang_dnode_get_prefix(&p, dnode, "./dest-addr"); - /* - * To support old FRR versions we must allow empty - * interface to be specified, however that should - * change in the future. - */ - if (yang_dnode_exists(dnode, "./interface")) - ifname = yang_dnode_get_string(dnode, "./interface"); - else - ifname = ""; + ifname = yang_dnode_get_string(dnode, "./interface"); if (p.family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6) - && strlen(ifname) == 0) { + && strcmp(ifname, "*") == 0) { zlog_warn( "%s: when using link-local you must specify an interface.", __func__); diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index ee3580edf1..6c077878b5 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -1319,11 +1319,18 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) else peer->v_holdtime = send_holdtime; - if ((CHECK_FLAG(peer->flags, PEER_FLAG_TIMER)) - && (peer->keepalive < peer->v_holdtime / 3)) - peer->v_keepalive = peer->keepalive; - else - peer->v_keepalive = peer->v_holdtime / 3; + /* Set effective keepalive to 1/3 the effective holdtime. + * Use configured keeplive when < effective keepalive. + */ + peer->v_keepalive = peer->v_holdtime / 3; + if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER)) { + if (peer->keepalive && peer->keepalive < peer->v_keepalive) + peer->v_keepalive = peer->keepalive; + } else { + if (peer->bgp->default_keepalive + && peer->bgp->default_keepalive < peer->v_keepalive) + peer->v_keepalive = peer->bgp->default_keepalive; + } /* Open option part parse. */ if (optlen != 0) { diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 174c11fd34..15c1df8473 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -3340,14 +3340,20 @@ bool bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi, (type == ZEBRA_ROUTE_BGP && stype == BGP_ROUTE_STATIC) ? true : false; - /* Only validated for unicast and multicast currently. */ - /* Also valid for EVPN where the nexthop is an IP address. */ - if (safi != SAFI_UNICAST && safi != SAFI_MULTICAST && safi != SAFI_EVPN) + /* + * Only validated for unicast and multicast currently. + * Also valid for EVPN where the nexthop is an IP address. + * If we are a bgp static route being checked then there is + * no need to check to see if the nexthop is martian as + * that it should be ok. + */ + if (is_bgp_static_route || + (safi != SAFI_UNICAST && safi != SAFI_MULTICAST && safi != SAFI_EVPN)) return false; /* If NEXT_HOP is present, validate it. */ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) { - if ((attr->nexthop.s_addr == INADDR_ANY && !is_bgp_static_route) + if (attr->nexthop.s_addr == INADDR_ANY || IPV4_CLASS_DE(ntohl(attr->nexthop.s_addr)) || bgp_nexthop_self(bgp, afi, type, stype, attr, dest)) return true; @@ -3366,8 +3372,7 @@ bool bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi, switch (attr->mp_nexthop_len) { case BGP_ATTR_NHLEN_IPV4: case BGP_ATTR_NHLEN_VPNV4: - ret = ((attr->mp_nexthop_global_in.s_addr == INADDR_ANY - && !is_bgp_static_route) + ret = (attr->mp_nexthop_global_in.s_addr == INADDR_ANY || IPV4_CLASS_DE( ntohl(attr->mp_nexthop_global_in.s_addr)) || bgp_nexthop_self(bgp, afi, type, stype, attr, @@ -3376,9 +3381,8 @@ bool bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi, case BGP_ATTR_NHLEN_IPV6_GLOBAL: case BGP_ATTR_NHLEN_VPNV6_GLOBAL: - ret = ((IN6_IS_ADDR_UNSPECIFIED( + ret = (IN6_IS_ADDR_UNSPECIFIED( &attr->mp_nexthop_global) - && !is_bgp_static_route) || IN6_IS_ADDR_LOOPBACK(&attr->mp_nexthop_global) || IN6_IS_ADDR_MULTICAST( &attr->mp_nexthop_global) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 84d4aeb1e6..4260970541 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -336,7 +336,15 @@ void bgp_router_id_zebra_bump(vrf_id_t vrf_id, const struct prefix *router_id) bgp->name_pretty, bgp->vrf_id, inet_ntoa(*addr)); - bgp_router_id_set(bgp, addr, false); + /* + * if old router-id was 0x0, set flag + * to use this new value + */ + bgp_router_id_set(bgp, addr, + (bgp->router_id.s_addr + == INADDR_ANY) + ? true + : false); } } } @@ -359,7 +367,15 @@ void bgp_router_id_zebra_bump(vrf_id_t vrf_id, const struct prefix *router_id) bgp->name_pretty, bgp->vrf_id, inet_ntoa(*addr)); - bgp_router_id_set(bgp, addr, false); + /* + * if old router-id was 0x0, set flag + * to use this new value + */ + bgp_router_id_set(bgp, addr, + (bgp->router_id.s_addr + == INADDR_ANY) + ? true + : false); } } diff --git a/configure.ac b/configure.ac index d75637c0a7..3cc74c4110 100755 --- a/configure.ac +++ b/configure.ac @@ -542,7 +542,7 @@ AC_ARG_ENABLE([fabricd], AC_ARG_ENABLE([vrrpd], AS_HELP_STRING([--disable-vrrpd], [do not build vrrpd])) AC_ARG_ENABLE([bgp-announce], - AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement])) + AS_HELP_STRING([--disable-bgp-announce], [turn off BGP route announcement])) AC_ARG_ENABLE([bgp-vnc], AS_HELP_STRING([--disable-bgp-vnc],[turn off BGP VNC support])) AC_ARG_ENABLE([bgp-bmp], @@ -593,7 +593,7 @@ AC_ARG_ENABLE([rusage], AC_ARG_ENABLE([gcc_ultra_verbose], AS_HELP_STRING([--enable-gcc-ultra-verbose], [enable ultra verbose GCC warnings])) AC_ARG_ENABLE([backtrace], - AS_HELP_STRING([--disable-backtrace,], [disable crash backtraces (default autodetect)])) + AS_HELP_STRING([--disable-backtrace], [disable crash backtraces (default autodetect)])) AC_ARG_ENABLE([time-check], AS_HELP_STRING([--disable-time-check], [disable slow thread warning messages])) AC_ARG_ENABLE([cpu-time], @@ -1561,10 +1561,70 @@ dnl -------------------- dnl Daemon disable check dnl -------------------- +AS_IF([test "$enable_bgpd" != "no"], [ + AC_DEFINE([HAVE_BGPD], [1], [bgpd]) +]) + +AS_IF([test "$enable_ripd" != "no"], [ + AC_DEFINE([HAVE_RIPD], [1], [ripd]) +]) + +AS_IF([test "$enable_ripngd" != "no"], [ + AC_DEFINE([HAVE_RIPNGD], [1], [ripngd]) +]) + +AS_IF([test "$enable_ospfd" != "no"], [ + AC_DEFINE([HAVE_OSPFD], [1], [ospfd]) +]) + +AS_IF([test "$enable_ospf6d" != "no"], [ + AC_DEFINE([HAVE_OSPF6D], [1], [ospf6d]) +]) + AS_IF([test "$enable_ldpd" != "no"], [ AC_DEFINE([HAVE_LDPD], [1], [ldpd]) ]) +AS_IF([test "$enable_nhrpd" != "no"], [ + AC_DEFINE([HAVE_NHRPD], [1], [nhrpd]) +]) + +AS_IF([test "$enable_eigrpd" != "no"], [ + AC_DEFINE([HAVE_EIGRPD], [1], [eigrpd]) +]) + +AS_IF([test "$enable_babeld" != "no"], [ + AC_DEFINE([HAVE_BABELD], [1], [babeld]) +]) + +AS_IF([test "$enable_isisd" != "no"], [ + AC_DEFINE([HAVE_ISISD], [1], [isisd]) +]) + +AS_IF([test "$enable_pimd" != "no"], [ + AC_DEFINE([HAVE_PIMD], [1], [pimd]) +]) + +AS_IF([test "$enable_pbrd" != "no"], [ + AC_DEFINE([HAVE_PBRD], [1], [pbrd]) +]) + +AS_IF([test "$enable_sharpd" = "yes"], [ + AC_DEFINE([HAVE_SHARPD], [1], [sharpd]) +]) + +AS_IF([test "$enable_staticd" != "no"], [ + AC_DEFINE([HAVE_STATICD], [1], [staticd]) +]) + +AS_IF([test "$enable_fabricd" != "no"], [ + AC_DEFINE([HAVE_FABRICD], [1], [fabricd]) +]) + +AS_IF([test "$enable_vrrpd" != "no"], [ + AC_DEFINE([HAVE_VRRPD], [1], [vrrpd]) +]) + if test "$enable_bfdd" = "no"; then AC_DEFINE([HAVE_BFDD], [0], [bfdd]) BFDD="" diff --git a/debian/README.Debian b/debian/README.Debian index 01b9213ae4..a23a0efcac 100644 --- a/debian/README.Debian +++ b/debian/README.Debian @@ -31,10 +31,6 @@ The following Build Profiles have been added: controls whether the RPKI module is built. Will be enabled by default at some point, adds some extra dependencies. -- pkg.frr.nosnmp (pkg.frr.snmp) - controls whether the SNMP module is built, see below for license issues. - Will remain default-off as long as the license issue persists. - - pkg.frr.nosystemd Disables both systemd unit file installation as well as watchfrr sd_notify support at startup. Removes libsystemd dependency. @@ -108,3 +104,5 @@ See message #4525 from 2005-05-09 in the quagga-users mailing list. Check /etc/pam.d/frr, it probably denies access to your user. The passwords configured in /etc/frr/frr.conf are only for telnet access. + + -- OndÅ™ej Surý <OndÅ™ej Surý <ondrej@debian.org>>, Fri, 3 Jul 2020 12:39:42 +0200 diff --git a/debian/compat b/debian/compat index ec635144f6..f599e28b8a 100644 --- a/debian/compat +++ b/debian/compat @@ -1 +1 @@ -9 +10 diff --git a/debian/control b/debian/control index fca6956760..4aaa9f21bf 100644 --- a/debian/control +++ b/debian/control @@ -2,64 +2,63 @@ Source: frr Section: net Priority: optional Maintainer: David Lamparter <equinox-debian@diac24.net> -Uploaders: FRRouting-dev <dev@lists.frrouting.org> -Build-Depends: - autotools-dev, - bison, - chrpath, - debhelper (>= 9), - debhelper (>= 9.20160709) <!pkg.frr.nosystemd> | dh-systemd <!pkg.frr.nosystemd>, - dh-autoreconf, - flex, - gawk, - install-info, - libc-ares-dev, - libcap-dev, - libjson-c-dev | libjson0-dev, - libpam0g-dev | libpam-dev, - libpcre3-dev, - libpython3-dev, - libreadline-dev, - librtr-dev <!pkg.frr.nortrlib>, - libsnmp-dev, - libssh-dev <!pkg.frr.nortrlib>, - libsystemd-dev <!pkg.frr.nosystemd>, - libyang-dev (>= 1.0.184), - pkg-config, - python3, - python3-dev, - python3-sphinx, - python3-pytest <!nocheck>, - texinfo (>= 4.7) -Standards-Version: 4.4.1 +Uploaders: FRRouting-dev <dev@lists.frrouting.org>, + OndÅ™ej Surý <ondrej@debian.org> +Build-Depends: bison, + chrpath, + debhelper (>= 10~) | dh-systemd, + debhelper (>= 9.20150101~), + flex, + gawk, + install-info, + libc-ares-dev, + libcap-dev, + libjson-c-dev | libjson0-dev, + libpam0g-dev | libpam-dev, + libpcre3-dev, + libpython3-dev, + libreadline-dev, + librtr-dev <!pkg.frr.nortrlib>, + libsnmp-dev, + libssh-dev <!pkg.frr.nortrlib>, + libsystemd-dev <!pkg.frr.nosystemd>, + libyang-dev (>= 1.0.184), + lsb-base, + pkg-config, + python3, + python3-dev, + python3-pytest <!nocheck>, + python3-sphinx, + texinfo (>= 4.7) +Standards-Version: 4.5.0.3 Homepage: https://www.frrouting.org/ Vcs-Browser: https://github.com/FRRouting/frr/tree/debian/master Vcs-Git: https://github.com/FRRouting/frr.git -b debian/master Package: frr Architecture: linux-any -Depends: - ${misc:Depends}, - ${shlibs:Depends}, - iproute2 | iproute, - logrotate (>= 3.2-11) +Depends: iproute2 | iproute, + logrotate (>= 3.2-11), + lsof, + ${misc:Depends}, + ${shlibs:Depends} Pre-Depends: adduser Recommends: frr-pythontools Suggests: frr-doc -Conflicts: - zebra, - zebra-pj, - pimd, - quagga, - quagga-bgpd, - quagga-core, - quagga-isisd, - quagga-ospf6d, - quagga-ospfd, - quagga-pimd, - quagga-ripd, - quagga-ripngd -Replaces: zebra, zebra-pj +Conflicts: pimd, + quagga, + quagga-bgpd, + quagga-core, + quagga-isisd, + quagga-ospf6d, + quagga-ospfd, + quagga-pimd, + quagga-ripd, + quagga-ripngd, + zebra, + zebra-pj +Replaces: zebra, + zebra-pj Description: FRRouting suite of internet protocols (BGP, OSPF, IS-IS, ...) FRRouting implements the routing protocols commonly used in the internet and private networks to exchange information between routers. @@ -77,10 +76,9 @@ Description: FRRouting suite of internet protocols (BGP, OSPF, IS-IS, ...) Package: frr-snmp Architecture: linux-any -Depends: - ${misc:Depends}, - ${shlibs:Depends}, - frr (= ${binary:Version}) +Depends: frr (= ${binary:Version}), + ${misc:Depends}, + ${shlibs:Depends} Recommends: snmpd Description: FRRouting suite - SNMP support Adds SNMP support to FRR's daemons by attaching to net-snmp's snmpd @@ -89,10 +87,9 @@ Description: FRRouting suite - SNMP support Package: frr-rpki-rtrlib Architecture: linux-any -Depends: - ${misc:Depends}, - ${shlibs:Depends}, - frr (= ${binary:Version}) +Depends: frr (= ${binary:Version}), + ${misc:Depends}, + ${shlibs:Depends} Description: FRRouting suite - BGP RPKI support (rtrlib) Adds RPKI support to FRR's bgpd, allowing validation of BGP routes against cryptographic information stored in WHOIS databases. This is @@ -105,10 +102,9 @@ Package: frr-doc Section: doc Architecture: all Multi-Arch: foreign -Depends: - ${misc:Depends}, - libjs-jquery, - libjs-underscore +Depends: libjs-jquery, + libjs-underscore, + ${misc:Depends} Suggests: frr Conflicts: quagga-doc Description: FRRouting suite - user manual @@ -118,11 +114,10 @@ Description: FRRouting suite - user manual Package: frr-pythontools Architecture: all -Depends: - ${misc:Depends}, - frr (<< ${source:Upstream-Version}.0-~), - frr (>= ${source:Version}~), - python3:any +Depends: frr (<< ${source:Upstream-Version}.0-~), + frr (>= ${source:Version}~), + python3:any, + ${misc:Depends} Description: FRRouting suite - Python tools The FRRouting suite uses a small Python tool to provide configuration reload functionality, particularly useful when the interactive configuration diff --git a/debian/frr-doc.install b/debian/frr-doc.install index c48dc5a8db..a666162122 100644 --- a/debian/frr-doc.install +++ b/debian/frr-doc.install @@ -1,10 +1,16 @@ # html docs include RST sources -usr/share/doc/frr/html - # info + images referenced by it -usr/share/info/ -doc/user/_build/texinfo/*.png usr/share/info - # other README.md usr/share/doc/frr doc/figures/*.png usr/share/doc/frr +doc/figures/fig-normal-processing.png usr/share/info +doc/figures/fig-rs-processing.png usr/share/info +doc/figures/fig-vnc-commercial-route-reflector.png usr/share/info +doc/figures/fig-vnc-frr-route-reflector.png usr/share/info +doc/figures/fig-vnc-gw.png usr/share/info +doc/figures/fig-vnc-mesh.png usr/share/info +doc/figures/fig-vnc-redundant-route-reflectors.png usr/share/info +doc/figures/fig_topologies_full.png usr/share/info +doc/figures/fig_topologies_rs.png usr/share/info +usr/share/doc/frr/html +usr/share/info/ diff --git a/debian/frr.dirs b/debian/frr.dirs index 4b05c8c907..9e592e370c 100644 --- a/debian/frr.dirs +++ b/debian/frr.dirs @@ -1,6 +1,6 @@ -etc/logrotate.d/ etc/frr/ etc/iproute2/rt_protos.d/ +etc/logrotate.d/ usr/share/doc/frr/ usr/share/doc/frr/examples/ usr/share/lintian/overrides/ diff --git a/debian/frr.docs b/debian/frr.docs index 34dbbd7bc7..220127caa1 100644 --- a/debian/frr.docs +++ b/debian/frr.docs @@ -1,2 +1,2 @@ -tools/zebra.el debian/README.Debian +tools/zebra.el diff --git a/debian/frr.install b/debian/frr.install index e2485fe8b8..cefc3135b2 100644 --- a/debian/frr.install +++ b/debian/frr.install @@ -1,20 +1,20 @@ +debian/frr.conf usr/lib/tmpfiles.d etc/ -usr/bin/vtysh +tools/frr-reload usr/lib/frr/ usr/bin/mtracebis +usr/bin/vtysh usr/lib/*/frr/libfrr.* usr/lib/*/frr/libfrrcares.* usr/lib/*/frr/libfrrospfapiclient.* +usr/lib/*/frr/modules/bgpd_bmp.so +usr/lib/*/frr/modules/dplane_fpm_nl.so +usr/lib/*/frr/modules/zebra_cumulus_mlag.so +usr/lib/*/frr/modules/zebra_fpm.so +usr/lib/*/frr/modules/zebra_irdp.so usr/lib/frr/*.sh usr/lib/frr/*d usr/lib/frr/watchfrr usr/lib/frr/zebra -usr/lib/*/frr/modules/zebra_cumulus_mlag.so -usr/lib/*/frr/modules/dplane_fpm_nl.so -usr/lib/*/frr/modules/zebra_irdp.so -usr/lib/*/frr/modules/zebra_fpm.so -usr/lib/*/frr/modules/bgpd_bmp.so usr/share/doc/frr/examples usr/share/man/ usr/share/yang/ -tools/frr-reload usr/lib/frr/ -debian/frr.conf usr/lib/tmpfiles.d diff --git a/debian/frr.tmpfile b/debian/frr.tmpfile new file mode 100644 index 0000000000..dee3cd849a --- /dev/null +++ b/debian/frr.tmpfile @@ -0,0 +1,2 @@ +# Create the /run/frr directory at boot or from systemd-tmpfiles on install +d /run/frr 0755 frr frr diff --git a/debian/gbp.conf b/debian/gbp.conf new file mode 100644 index 0000000000..990c4d226e --- /dev/null +++ b/debian/gbp.conf @@ -0,0 +1,4 @@ +[DEFAULT] +pristine-tar = False +debian-branch = master +upstream-tree=SLOPPY diff --git a/debian/rules b/debian/rules index c8550ecb52..6cc03c378a 100755 --- a/debian/rules +++ b/debian/rules @@ -22,17 +22,17 @@ else endif ifeq ($(filter pkg.frr.nosystemd,$(DEB_BUILD_PROFILES)),) - DH_WITH_SYSTEMD=systemd, + DH_WITHOUT_SYSTEMD= CONF_SYSTEMD=--enable-systemd=yes else - DH_WITH_SYSTEMD= + DH_WITHOUT_SYSTEMD=--without=systemd CONF_SYSTEMD=--enable-systemd=no endif export PYTHON=python3 %: - dh $@ --with=$(DH_WITH_SYSTEMD)autoreconf --parallel + dh $@ $(DH_WITHOUT_SYSTEMD) override_dh_auto_configure: $(shell dpkg-buildflags --export=sh); \ diff --git a/debian/source/format b/debian/source/format index af745b310b..163aaf8d82 100644 --- a/debian/source/format +++ b/debian/source/format @@ -1 +1 @@ -3.0 (git) +3.0 (quilt) diff --git a/debian/tests/control b/debian/tests/control index 5990a69370..6cb5b02dc1 100644 --- a/debian/tests/control +++ b/debian/tests/control @@ -3,9 +3,12 @@ Depends: frr Restrictions: needs-root, isolation-container Tests: bgpd-snmp-rpki -Depends: frr, frr-snmp, frr-rpki-rtrlib +Depends: frr, + frr-rpki-rtrlib, + frr-snmp Restrictions: needs-root, isolation-container Tests: py-frr-reload -Depends: frr, frr-pythontools +Depends: frr, + frr-pythontools Restrictions: needs-root, isolation-container diff --git a/debian/watch b/debian/watch index c286392d7e..f17eba4130 100644 --- a/debian/watch +++ b/debian/watch @@ -1,4 +1,5 @@ version=4 +opts=uversionmangle=s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha)\d*)$/$1~$2/ \ https://github.com/FRRouting/frr/releases/ \ - download/frr-(?:\d[\d.]*)/frr-(\d[\d.]*)\.tar\.xz debian uupdate + .*/frr-(\d\S+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz))) diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index 99ef258cb2..c869c6bc45 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -39,7 +39,7 @@ listing of ECMP nexthops used to forward packets for when a pbr-map is matched. sub-mode where you can specify individual nexthops. To exit this mode type exit or end as per normal conventions for leaving a sub-mode. -.. clicmd:: nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME] [label LABELS] +.. clicmd:: nexthop [A.B.C.D|X:X::X:XX] [interface [onlink]] [nexthop-vrf NAME] [label LABELS] Create a v4 or v6 nexthop. All normal rules for creating nexthops that you are used to are allowed here. The syntax was intentionally kept the same as diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index f7cdd58f72..5bfbb2cf7e 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -304,7 +304,6 @@ void isis_adj_state_change(struct isis_adjacency **padj, continue; if (new_state == ISIS_ADJ_UP) { circuit->upadjcount[level - 1]++; - hook_call(isis_adj_state_change_hook, adj); /* update counter & timers for debugging * purposes */ adj->last_flap = time(NULL); @@ -317,7 +316,6 @@ void isis_adj_state_change(struct isis_adjacency **padj, if (circuit->upadjcount[level - 1] == 0) isis_tx_queue_clean(circuit->tx_queue); - hook_call(isis_adj_state_change_hook, adj); if (new_state == ISIS_ADJ_DOWN) del = true; } @@ -342,7 +340,6 @@ void isis_adj_state_change(struct isis_adjacency **padj, continue; if (new_state == ISIS_ADJ_UP) { circuit->upadjcount[level - 1]++; - hook_call(isis_adj_state_change_hook, adj); /* update counter & timers for debugging * purposes */ @@ -365,13 +362,14 @@ void isis_adj_state_change(struct isis_adjacency **padj, if (circuit->upadjcount[level - 1] == 0) isis_tx_queue_clean(circuit->tx_queue); - hook_call(isis_adj_state_change_hook, adj); if (new_state == ISIS_ADJ_DOWN) del = true; } } } + hook_call(isis_adj_state_change_hook, adj); + if (del) { isis_delete_adj(adj); *padj = NULL; @@ -467,11 +465,15 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, vty_out(vty, "%-3u", adj->level); /* level */ vty_out(vty, "%-13s", adj_state2string(adj->adj_state)); now = time(NULL); - if (adj->last_upd) - vty_out(vty, "%-9llu", - (unsigned long long)adj->last_upd - + adj->hold_time - now); - else + if (adj->last_upd) { + if (adj->last_upd + adj->hold_time + < (unsigned long long)now) + vty_out(vty, " Expiring"); + else + vty_out(vty, " %-9llu", + (unsigned long long)adj->last_upd + + adj->hold_time - now); + } else vty_out(vty, "- "); vty_out(vty, "%-10s", snpa_print(adj->snpa)); vty_out(vty, "\n"); @@ -491,11 +493,15 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, vty_out(vty, ", Level: %u", adj->level); /* level */ vty_out(vty, ", State: %s", adj_state2string(adj->adj_state)); now = time(NULL); - if (adj->last_upd) - vty_out(vty, ", Expires in %s", - time2string(adj->last_upd + adj->hold_time - - now)); - else + if (adj->last_upd) { + if (adj->last_upd + adj->hold_time + < (unsigned long long)now) + vty_out(vty, " Expiring"); + else + vty_out(vty, ", Expires in %s", + time2string(adj->last_upd + + adj->hold_time - now)); + } else vty_out(vty, ", Expires in %s", time2string(adj->hold_time)); vty_out(vty, "\n"); diff --git a/lib/hash.h b/lib/hash.h index e7ba3187f5..00953ff3b3 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -236,7 +236,8 @@ extern void *hash_release(struct hash *hash, void *data); * Iterate over the elements in a hash table. * * It is safe to delete items passed to the iteration function from the hash - * table during iteration. Please note that adding entries to the hash + * table during iteration. More than one item cannot be deleted during each + * iteration. Please note that adding entries to the hash * during the walk will cause undefined behavior in that some new entries * will be walked and some will not. So do not do this. * @@ -452,7 +452,10 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_OPAQUE_MESSAGE), DESC_ENTRY(ZEBRA_OPAQUE_REGISTER), DESC_ENTRY(ZEBRA_OPAQUE_UNREGISTER), - DESC_ENTRY(ZEBRA_NEIGH_DISCOVER)}; + DESC_ENTRY(ZEBRA_NEIGH_DISCOVER), + DESC_ENTRY(ZEBRA_NHG_ADD), + DESC_ENTRY(ZEBRA_NHG_DEL), + DESC_ENTRY(ZEBRA_NHG_NOTIFY_OWNER)}; #undef DESC_ENTRY static const struct zebra_desc_table unknown = {0, "unknown", '?'}; diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index 687cac4062..83905abe43 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -41,6 +41,7 @@ struct nexthop_hold { char *nhvrf_name; union sockunion *addr; char *intf; + bool onlink; char *labels; uint32_t weight; char *backup_str; @@ -560,6 +561,10 @@ static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2) if (ret) return ret; + ret = ((int)nh2->onlink) - ((int)nh1->onlink); + if (ret) + return ret; + return nhgc_cmp_helper(nh1->labels, nh2->labels); } @@ -673,8 +678,8 @@ DEFPY(no_nexthop_group_backup, no_nexthop_group_backup_cmd, static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc, const char *nhvrf_name, const union sockunion *addr, - const char *intf, const char *labels, - const uint32_t weight, + const char *intf, bool onlink, + const char *labels, const uint32_t weight, const char *backup_str) { struct nexthop_hold *nh; @@ -690,6 +695,8 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc, if (labels) nh->labels = XSTRDUP(MTYPE_TMP, labels); + nh->onlink = onlink; + nh->weight = weight; if (backup_str) @@ -738,9 +745,10 @@ static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc, */ static bool nexthop_group_parse_nexthop(struct nexthop *nhop, const union sockunion *addr, - const char *intf, const char *name, - const char *labels, int *lbl_ret, - uint32_t weight, const char *backup_str) + const char *intf, bool onlink, + const char *name, const char *labels, + int *lbl_ret, uint32_t weight, + const char *backup_str) { int ret = 0; struct vrf *vrf; @@ -764,6 +772,9 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop, return false; } + if (onlink) + SET_FLAG(nhop->flags, NEXTHOP_FLAG_ONLINK); + if (addr) { if (addr->sa.sa_family == AF_INET) { nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr; @@ -820,15 +831,15 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop, static bool nexthop_group_parse_nhh(struct nexthop *nhop, const struct nexthop_hold *nhh) { - return (nexthop_group_parse_nexthop(nhop, nhh->addr, nhh->intf, - nhh->nhvrf_name, nhh->labels, NULL, - nhh->weight, nhh->backup_str)); + return (nexthop_group_parse_nexthop( + nhop, nhh->addr, nhh->intf, nhh->onlink, nhh->nhvrf_name, + nhh->labels, NULL, nhh->weight, nhh->backup_str)); } DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, "[no] nexthop\ <\ - <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\ + <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf [onlink$onlink]]\ |INTERFACE$intf\ >\ [{ \ @@ -842,6 +853,7 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, "v4 Address\n" "v6 Address\n" "Interface to use\n" + "Treat nexthop as directly attached to the interface\n" "Interface to use\n" "If the nexthop is in a different vrf tell us\n" "The nexthop-vrf Name\n" @@ -870,8 +882,9 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, } } - legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name, label, - &lbl_ret, weight, backup_idx); + legal = nexthop_group_parse_nexthop(&nhop, addr, intf, !!onlink, + vrf_name, label, &lbl_ret, weight, + backup_idx); if (nhop.type == NEXTHOP_TYPE_IPV6 && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) { @@ -933,8 +946,8 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, } /* Save config always */ - nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, label, - weight, backup_idx); + nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, !!onlink, + label, weight, backup_idx); if (legal && nhg_hooks.add_nexthop) nhg_hooks.add_nexthop(nhgc, nh); @@ -1106,6 +1119,9 @@ static void nexthop_group_write_nexthop_internal(struct vty *vty, if (nh->intf) vty_out(vty, " %s", nh->intf); + if (nh->onlink) + vty_out(vty, " onlink"); + if (nh->nhvrf_name) vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name); diff --git a/lib/route_types.txt b/lib/route_types.txt index b549c11cfc..37cc2fb590 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -84,7 +84,7 @@ ZEBRA_ROUTE_PBR, pbr, pbrd, 'F', 1, 1, 0, "PBR" ZEBRA_ROUTE_BFD, bfd, bfdd, '-', 0, 0, 0, "BFD" ZEBRA_ROUTE_OPENFABRIC, openfabric, fabricd, 'f', 1, 1, 1, "OpenFabric" ZEBRA_ROUTE_VRRP, vrrp, vrrpd, '-', 0, 0, 0, "VRRP" -ZEBRA_ROUTE_NHG, nhg, none, '-', 0, 0, 0, "Nexthop Group" +ZEBRA_ROUTE_NHG, zebra, none, '-', 0, 0, 0, "Nexthop Group" ZEBRA_ROUTE_SRTE, srte, none, '-', 0, 0, 0, "SR-TE" ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-" diff --git a/lib/zclient.c b/lib/zclient.c index c5016d22e2..b7d240b4e8 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1017,6 +1017,57 @@ done: return ret; } +int zapi_nhg_encode(struct stream *s, int cmd, struct zapi_nhg *api_nhg) +{ + int i; + + if (cmd != ZEBRA_NHG_DEL && cmd != ZEBRA_NHG_ADD) { + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: Specified zapi NHG command (%d) doesn't exist\n", + __func__, cmd); + return -1; + } + + stream_reset(s); + zclient_create_header(s, cmd, VRF_DEFAULT); + + stream_putw(s, api_nhg->proto); + stream_putl(s, api_nhg->id); + + if (cmd == ZEBRA_NHG_ADD) { + /* Nexthops */ + zapi_nexthop_group_sort(api_nhg->nexthops, + api_nhg->nexthop_num); + + stream_putw(s, api_nhg->nexthop_num); + + for (i = 0; i < api_nhg->nexthop_num; i++) + zapi_nexthop_encode(s, &api_nhg->nexthops[i], 0, 0); + + /* Backup nexthops */ + + stream_putw(s, api_nhg->backup_nexthop_num); + + for (i = 0; i < api_nhg->backup_nexthop_num; i++) + zapi_nexthop_encode(s, &api_nhg->backup_nexthops[i], 0, + 0); + } + + stream_putw_at(s, 0, stream_get_endp(s)); + + return 0; +} + +int zclient_nhg_send(struct zclient *zclient, int cmd, struct zapi_nhg *api_nhg) +{ + api_nhg->proto = zclient->redist_default; + + if (zapi_nhg_encode(zclient->obuf, cmd, api_nhg)) + return -1; + + return zclient_send_message(zclient); +} + int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) { struct zapi_nexthop *api_nh; @@ -1058,6 +1109,9 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) stream_write(s, (uint8_t *)&api->src_prefix.prefix, psize); } + if (CHECK_FLAG(api->message, ZAPI_MESSAGE_NHG)) + stream_putl(s, api->nhgid); + /* Nexthops. */ if (CHECK_FLAG(api->message, ZAPI_MESSAGE_NEXTHOP)) { /* limit the number of nexthops if necessary */ @@ -1171,8 +1225,8 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) /* * Decode a single zapi nexthop object */ -static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, - uint32_t api_flags, uint32_t api_message) +int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, + uint32_t api_flags, uint32_t api_message) { int i, ret = -1; @@ -1328,6 +1382,9 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) } } + if (CHECK_FLAG(api->message, ZAPI_MESSAGE_NHG)) + STREAM_GETL(s, api->nhgid); + /* Nexthops. */ if (CHECK_FLAG(api->message, ZAPI_MESSAGE_NEXTHOP)) { STREAM_GETW(s, api->nexthop_num); @@ -1432,6 +1489,22 @@ int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule) return 0; } +bool zapi_nhg_notify_decode(struct stream *s, uint32_t *id, + enum zapi_nhg_notify_owner *note) +{ + uint32_t read_id; + + STREAM_GET(note, s, sizeof(*note)); + STREAM_GETL(s, read_id); + + *id = read_id; + + return true; + +stream_failure: + return false; +} + bool zapi_route_notify_decode(struct stream *s, struct prefix *p, uint32_t *tableid, enum zapi_route_notify_owner *note) @@ -1582,6 +1655,9 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh, znh->ifindex = nh->ifindex; znh->gate = nh->gate; + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK)) + SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); + if (nh->nh_label && (nh->nh_label->num_labels > 0)) { /* Validate */ @@ -3733,6 +3809,11 @@ static int zclient_read(struct thread *thread) (*zclient->rule_notify_owner)(command, zclient, length, vrf_id); break; + case ZEBRA_NHG_NOTIFY_OWNER: + if (zclient->nhg_notify_owner) + (*zclient->nhg_notify_owner)(command, zclient, length, + vrf_id); + break; case ZEBRA_GET_LABEL_CHUNK: if (zclient->label_chunk) (*zclient->label_chunk)(command, zclient, length, @@ -4001,3 +4082,13 @@ int zclient_send_neigh_discovery_req(struct zclient *zclient, stream_putw_at(s, 0, stream_get_endp(s)); return zclient_send_message(zclient); } + +/* + * Get a starting nhg point for a routing protocol + */ +uint32_t zclient_get_nhg_start(uint32_t proto) +{ + assert(proto < ZEBRA_ROUTE_MAX); + + return ZEBRA_NHG_PROTO_SPACING * proto; +} diff --git a/lib/zclient.h b/lib/zclient.h index 050877f27a..959a101395 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -209,6 +209,9 @@ typedef enum { ZEBRA_MLAG_CLIENT_REGISTER, ZEBRA_MLAG_CLIENT_UNREGISTER, ZEBRA_MLAG_FORWARD_MSG, + ZEBRA_NHG_ADD, + ZEBRA_NHG_DEL, + ZEBRA_NHG_NOTIFY_OWNER, ZEBRA_ERROR, ZEBRA_CLIENT_CAPABILITIES, ZEBRA_OPAQUE_MESSAGE, @@ -354,6 +357,7 @@ struct zclient { int (*mlag_process_up)(void); int (*mlag_process_down)(void); int (*mlag_handle_msg)(struct stream *msg, int len); + int (*nhg_notify_owner)(ZAPI_CALLBACK_ARGS); int (*handle_error)(enum zebra_error_types error); int (*opaque_msg_handler)(ZAPI_CALLBACK_ARGS); int (*opaque_register_handler)(ZAPI_CALLBACK_ARGS); @@ -370,6 +374,7 @@ struct zclient { #define ZAPI_MESSAGE_SRCPFX 0x20 /* Backup nexthops are present */ #define ZAPI_MESSAGE_BACKUP_NEXTHOPS 0x40 +#define ZAPI_MESSAGE_NHG 0x80 /* * This should only be used by a DAEMON that needs to communicate @@ -434,6 +439,20 @@ struct zapi_nexthop { #define ZAPI_NEXTHOP_FLAG_HAS_BACKUP 0x08 /* Nexthop has a backup */ /* + * ZAPI Nexthop Group. For use with protocol creation of nexthop groups. + */ +struct zapi_nhg { + uint16_t proto; + uint32_t id; + + uint16_t nexthop_num; + struct zapi_nexthop nexthops[MULTIPATH_NUM]; + + uint16_t backup_nexthop_num; + struct zapi_nexthop backup_nexthops[MULTIPATH_NUM]; +}; + +/* * Some of these data structures do not map easily to * a actual data structure size giving different compilers * and systems. For those data structures we need @@ -514,6 +533,8 @@ struct zapi_route { uint16_t backup_nexthop_num; struct zapi_nexthop backup_nexthops[MULTIPATH_NUM]; + uint32_t nhgid; + uint8_t distance; uint32_t metric; @@ -592,6 +613,13 @@ enum zapi_route_notify_owner { ZAPI_ROUTE_REMOVE_FAIL, }; +enum zapi_nhg_notify_owner { + ZAPI_NHG_FAIL_INSTALL, + ZAPI_NHG_INSTALLED, + ZAPI_NHG_REMOVED, + ZAPI_NHG_REMOVE_FAIL, +}; + enum zapi_rule_notify_owner { ZAPI_RULE_FAIL_INSTALL, ZAPI_RULE_INSTALLED, @@ -671,6 +699,22 @@ struct zclient_options { extern struct zclient_options zclient_options_default; +/* + * We reserve the top 4 bits for l2-NHG, everything else + * is for zebra/proto l3-NHG. + * + * Each client is going to get it's own nexthop group space + * and we'll separate them, we'll figure out where to start based upon + * the route_types.h + */ +#define ZEBRA_NHG_PROTO_UPPER \ + ((uint32_t)250000000) /* Bottom 28 bits then rounded down */ +#define ZEBRA_NHG_PROTO_SPACING (ZEBRA_NHG_PROTO_UPPER / ZEBRA_ROUTE_MAX) +#define ZEBRA_NHG_PROTO_LOWER \ + (ZEBRA_NHG_PROTO_SPACING * (ZEBRA_ROUTE_CONNECT + 1)) + +extern uint32_t zclient_get_nhg_start(uint32_t proto); + extern struct zclient *zclient_new(struct thread_master *m, struct zclient_options *opt); @@ -853,7 +897,11 @@ extern int zclient_send_rnh(struct zclient *zclient, int command, int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, uint32_t api_flags, uint32_t api_message); extern int zapi_route_encode(uint8_t, struct stream *, struct zapi_route *); -extern int zapi_route_decode(struct stream *, struct zapi_route *); +extern int zapi_route_decode(struct stream *s, struct zapi_route *api); +extern int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, + uint32_t api_flags, uint32_t api_message); +bool zapi_nhg_notify_decode(struct stream *s, uint32_t *id, + enum zapi_nhg_notify_owner *note); bool zapi_route_notify_decode(struct stream *s, struct prefix *p, uint32_t *tableid, enum zapi_route_notify_owner *note); @@ -864,6 +912,12 @@ bool zapi_ipset_notify_decode(struct stream *s, uint32_t *unique, enum zapi_ipset_notify_owner *note); + +extern int zapi_nhg_encode(struct stream *s, int cmd, struct zapi_nhg *api_nhg); +extern int zapi_nhg_decode(struct stream *s, int cmd, struct zapi_nhg *api_nhg); +extern int zclient_nhg_send(struct zclient *zclient, int cmd, + struct zapi_nhg *api_nhg); + #define ZEBRA_IPSET_NAME_SIZE 32 bool zapi_ipset_entry_notify_decode(struct stream *s, diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index fe2778c877..01caff5b52 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -721,12 +721,23 @@ void pbr_map_policy_delete(struct pbr_map *pbrm, struct pbr_map_interface *pmi) { struct listnode *node; struct pbr_map_sequence *pbrms; + bool sent = false; for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) - pbr_send_pbr_map(pbrms, pmi, false, false); + if (pbr_send_pbr_map(pbrms, pmi, false, true)) + sent = true; /* rule removal sent to zebra */ pmi->delete = true; + + /* + * If we actually sent something for deletion, wait on zapi callback + * before clearing data. + */ + if (sent) + return; + + pbr_map_final_interface_deletion(pbrm, pmi); } /* diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index 269bd6da8d..8ef675186f 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -549,7 +549,7 @@ static void pbr_encode_pbr_map_sequence(struct stream *s, stream_put(s, ifp->name, INTERFACE_NAMSIZ); } -void pbr_send_pbr_map(struct pbr_map_sequence *pbrms, +bool pbr_send_pbr_map(struct pbr_map_sequence *pbrms, struct pbr_map_interface *pmi, bool install, bool changed) { struct pbr_map *pbrm = pbrms->parent; @@ -569,10 +569,10 @@ void pbr_send_pbr_map(struct pbr_map_sequence *pbrms, * to delete just return. */ if (install && is_installed && !changed) - return; + return false; if (!install && !is_installed) - return; + return false; s = zclient->obuf; stream_reset(s); @@ -595,4 +595,6 @@ void pbr_send_pbr_map(struct pbr_map_sequence *pbrms, stream_putw_at(s, 0, stream_get_endp(s)); zclient_send_message(zclient); + + return true; } diff --git a/pbrd/pbr_zebra.h b/pbrd/pbr_zebra.h index cc42e21abe..e8f9bff5d9 100644 --- a/pbrd/pbr_zebra.h +++ b/pbrd/pbr_zebra.h @@ -35,7 +35,7 @@ extern void route_delete(struct pbr_nexthop_group_cache *pnhgc, extern void pbr_send_rnh(struct nexthop *nhop, bool reg); -extern void pbr_send_pbr_map(struct pbr_map_sequence *pbrms, +extern bool pbr_send_pbr_map(struct pbr_map_sequence *pbrms, struct pbr_map_interface *pmi, bool install, bool changed); diff --git a/ripd/ripd.c b/ripd/ripd.c index ecadf8fb71..bcf73e8f89 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -3667,13 +3667,20 @@ static int rip_vrf_enable(struct vrf *vrf) */ if (yang_module_find("frr-ripd") && old_vrf_name) { struct lyd_node *rip_dnode; + char oldpath[XPATH_MAXLEN]; + char newpath[XPATH_MAXLEN]; rip_dnode = yang_dnode_get( running_config->dnode, "/frr-ripd:ripd/instance[vrf='%s']/vrf", old_vrf_name); if (rip_dnode) { + yang_dnode_get_path(rip_dnode->parent, oldpath, + sizeof(oldpath)); yang_dnode_change_leaf(rip_dnode, vrf->name); + yang_dnode_get_path(rip_dnode->parent, newpath, + sizeof(newpath)); + nb_running_move_tree(oldpath, newpath); running_config->version++; } } diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index bf6c6ff27b..8a7950daf4 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -2791,13 +2791,20 @@ static int ripng_vrf_enable(struct vrf *vrf) */ if (yang_module_find("frr-ripngd") && old_vrf_name) { struct lyd_node *ripng_dnode; + char oldpath[XPATH_MAXLEN]; + char newpath[XPATH_MAXLEN]; ripng_dnode = yang_dnode_get( running_config->dnode, "/frr-ripngd:ripngd/instance[vrf='%s']/vrf", old_vrf_name); if (ripng_dnode) { + yang_dnode_get_path(ripng_dnode->parent, oldpath, + sizeof(oldpath)); yang_dnode_change_leaf(ripng_dnode, vrf->name); + yang_dnode_get_path(ripng_dnode->parent, newpath, + sizeof(newpath)); + nb_running_move_tree(oldpath, newpath); running_config->version++; } } diff --git a/sharpd/sharp_globals.h b/sharpd/sharp_globals.h index 8eba57f4dd..0bd47454a9 100644 --- a/sharpd/sharp_globals.h +++ b/sharpd/sharp_globals.h @@ -31,6 +31,7 @@ struct sharp_routes { /* The nexthop info we are using for installation */ struct nexthop nhop; struct nexthop backup_nhop; + uint32_t nhgid; struct nexthop_group nhop_group; struct nexthop_group backup_nhop_group; diff --git a/sharpd/sharp_main.c b/sharpd/sharp_main.c index ccf34b10dd..4cd92c7f3d 100644 --- a/sharpd/sharp_main.c +++ b/sharpd/sharp_main.c @@ -47,6 +47,7 @@ #include "sharp_zebra.h" #include "sharp_vty.h" #include "sharp_globals.h" +#include "sharp_nht.h" DEFINE_MGROUP(SHARPD, "sharpd") @@ -164,7 +165,7 @@ int main(int argc, char **argv, char **envp) sharp_global_init(); - nexthop_group_init(NULL, NULL, NULL, NULL); + sharp_nhgroup_init(); vrf_init(NULL, NULL, NULL, NULL, NULL); sharp_zebra_init(); diff --git a/sharpd/sharp_nht.c b/sharpd/sharp_nht.c index 174f186863..7484dd3b06 100644 --- a/sharpd/sharp_nht.c +++ b/sharpd/sharp_nht.c @@ -25,11 +25,15 @@ #include "nexthop.h" #include "nexthop_group.h" #include "vty.h" +#include "typesafe.h" +#include "zclient.h" #include "sharp_nht.h" #include "sharp_globals.h" +#include "sharp_zebra.h" DEFINE_MTYPE_STATIC(SHARPD, NH_TRACKER, "Nexthop Tracker") +DEFINE_MTYPE_STATIC(SHARPD, NHG, "Nexthop Group") struct sharp_nh_tracker *sharp_nh_tracker_get(struct prefix *p) { @@ -65,3 +69,157 @@ void sharp_nh_tracker_dump(struct vty *vty) nht->updates); } } + +PREDECL_RBTREE_UNIQ(sharp_nhg_rb); + +struct sharp_nhg { + struct sharp_nhg_rb_item mylistitem; + + uint32_t id; + + char name[256]; + + bool installed; +}; + +static uint32_t nhg_id; + +static uint32_t sharp_get_next_nhid(void) +{ + zlog_debug("NHG ID assigned: %u", nhg_id); + return nhg_id++; +} + +struct sharp_nhg_rb_head nhg_head; + +static int sharp_nhg_compare_func(const struct sharp_nhg *a, + const struct sharp_nhg *b) +{ + return strncmp(a->name, b->name, strlen(a->name)); +} + +DECLARE_RBTREE_UNIQ(sharp_nhg_rb, struct sharp_nhg, mylistitem, + sharp_nhg_compare_func); + +static struct sharp_nhg *sharp_nhgroup_find_id(uint32_t id) +{ + struct sharp_nhg *lookup; + + /* Yea its just a for loop, I don't want add complexity + * to sharpd with another RB tree for just IDs + */ + + frr_each (sharp_nhg_rb, &nhg_head, lookup) { + if (lookup->id == id) + return lookup; + } + + return NULL; +} + +static void sharp_nhgroup_add_cb(const char *name) +{ + struct sharp_nhg *snhg; + + snhg = XCALLOC(MTYPE_NHG, sizeof(*snhg)); + snhg->id = sharp_get_next_nhid(); + strlcpy(snhg->name, name, sizeof(snhg->name)); + + sharp_nhg_rb_add(&nhg_head, snhg); +} + +static void sharp_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhgc, + const struct nexthop *nhop) +{ + struct sharp_nhg lookup; + struct sharp_nhg *snhg; + struct nexthop_group_cmd *bnhgc = NULL; + + strlcpy(lookup.name, nhgc->name, sizeof(lookup.name)); + snhg = sharp_nhg_rb_find(&nhg_head, &lookup); + + if (nhgc->backup_list_name[0]) + bnhgc = nhgc_find(nhgc->backup_list_name); + + nhg_add(snhg->id, &nhgc->nhg, (bnhgc ? &bnhgc->nhg : NULL)); +} + +static void sharp_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc, + const struct nexthop *nhop) +{ + struct sharp_nhg lookup; + struct sharp_nhg *snhg; + struct nexthop_group_cmd *bnhgc = NULL; + + strlcpy(lookup.name, nhgc->name, sizeof(lookup.name)); + snhg = sharp_nhg_rb_find(&nhg_head, &lookup); + + if (nhgc->backup_list_name[0]) + bnhgc = nhgc_find(nhgc->backup_list_name); + + nhg_add(snhg->id, &nhgc->nhg, (bnhgc ? &bnhgc->nhg : NULL)); +} + +static void sharp_nhgroup_delete_cb(const char *name) +{ + struct sharp_nhg lookup; + struct sharp_nhg *snhg; + + strlcpy(lookup.name, name, sizeof(lookup.name)); + snhg = sharp_nhg_rb_find(&nhg_head, &lookup); + if (!snhg) + return; + + nhg_del(snhg->id); + sharp_nhg_rb_del(&nhg_head, snhg); + XFREE(MTYPE_NHG, snhg); +} + +uint32_t sharp_nhgroup_get_id(const char *name) +{ + struct sharp_nhg lookup; + struct sharp_nhg *snhg; + + strlcpy(lookup.name, name, sizeof(lookup.name)); + snhg = sharp_nhg_rb_find(&nhg_head, &lookup); + if (!snhg) + return 0; + + return snhg->id; +} + +void sharp_nhgroup_id_set_installed(uint32_t id, bool installed) +{ + struct sharp_nhg *snhg; + + snhg = sharp_nhgroup_find_id(id); + if (!snhg) { + zlog_debug("%s: nhg %u not found", __func__, id); + return; + } + + snhg->installed = installed; +} + +bool sharp_nhgroup_id_is_installed(uint32_t id) +{ + struct sharp_nhg *snhg; + + snhg = sharp_nhgroup_find_id(id); + if (!snhg) { + zlog_debug("%s: nhg %u not found", __func__, id); + return false; + } + + return snhg->installed; +} + +void sharp_nhgroup_init(void) +{ + sharp_nhg_rb_init(&nhg_head); + nhg_id = zclient_get_nhg_start(ZEBRA_ROUTE_SHARP); + + nexthop_group_init(sharp_nhgroup_add_cb, sharp_nhgroup_add_nexthop_cb, + sharp_nhgroup_del_nexthop_cb, + sharp_nhgroup_delete_cb); +} diff --git a/sharpd/sharp_nht.h b/sharpd/sharp_nht.h index 0b00774a81..da33502878 100644 --- a/sharpd/sharp_nht.h +++ b/sharpd/sharp_nht.h @@ -35,4 +35,10 @@ struct sharp_nh_tracker { extern struct sharp_nh_tracker *sharp_nh_tracker_get(struct prefix *p); extern void sharp_nh_tracker_dump(struct vty *vty); + +extern uint32_t sharp_nhgroup_get_id(const char *name); +extern void sharp_nhgroup_id_set_installed(uint32_t id, bool installed); +extern bool sharp_nhgroup_id_is_installed(uint32_t id); + +extern void sharp_nhgroup_init(void); #endif diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index d390ea8192..d062f027ab 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -192,6 +192,7 @@ DEFPY (install_routes, struct vrf *vrf; struct prefix prefix; uint32_t rts; + uint32_t nhgid = 0; sg.r.total_routes = routes; sg.r.installed_routes = 0; @@ -244,6 +245,8 @@ DEFPY (install_routes, return CMD_WARNING; } + nhgid = sharp_nhgroup_get_id(nexthop_group); + sg.r.nhgid = nhgid; sg.r.nhop_group.nexthop = nhgc->nhg.nexthop; /* Use group's backup nexthop info if present */ @@ -296,7 +299,7 @@ DEFPY (install_routes, sg.r.inst = instance; sg.r.vrf_id = vrf->vrf_id; rts = routes; - sharp_install_routes_helper(&prefix, sg.r.vrf_id, sg.r.inst, + sharp_install_routes_helper(&prefix, sg.r.vrf_id, sg.r.inst, nhgid, &sg.r.nhop_group, &sg.r.backup_nhop_group, rts); diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index 08f5a07b7e..d167e8e277 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -217,7 +217,7 @@ int sharp_install_lsps_helper(bool install_p, bool update_p, } void sharp_install_routes_helper(struct prefix *p, vrf_id_t vrf_id, - uint8_t instance, + uint8_t instance, uint32_t nhgid, const struct nexthop_group *nhg, const struct nexthop_group *backup_nhg, uint32_t routes) @@ -239,7 +239,7 @@ void sharp_install_routes_helper(struct prefix *p, vrf_id_t vrf_id, monotime(&sg.r.t_start); for (i = 0; i < routes; i++) { - route_add(p, vrf_id, (uint8_t)instance, nhg, backup_nhg); + route_add(p, vrf_id, (uint8_t)instance, nhgid, nhg, backup_nhg); if (v4) p->u.prefix4.s_addr = htonl(++temp); else @@ -288,7 +288,7 @@ static void handle_repeated(bool installed) if (!installed) { sg.r.installed_routes = 0; sharp_install_routes_helper(&p, sg.r.vrf_id, sg.r.inst, - &sg.r.nhop_group, + sg.r.nhgid, &sg.r.nhop_group, &sg.r.backup_nhop_group, sg.r.total_routes); } @@ -357,8 +357,58 @@ void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label) zclient_send_vrf_label(zclient, vrf_id, afi, label, ZEBRA_LSP_SHARP); } -void route_add(const struct prefix *p, vrf_id_t vrf_id, - uint8_t instance, const struct nexthop_group *nhg, +void nhg_add(uint32_t id, const struct nexthop_group *nhg, + const struct nexthop_group *backup_nhg) +{ + struct zapi_nhg api_nhg = {}; + struct zapi_nexthop *api_nh; + struct nexthop *nh; + + api_nhg.id = id; + for (ALL_NEXTHOPS_PTR(nhg, nh)) { + if (api_nhg.nexthop_num >= MULTIPATH_NUM) { + zlog_warn( + "%s: number of nexthops greater than max multipath size, truncating", + __func__); + break; + } + + api_nh = &api_nhg.nexthops[api_nhg.nexthop_num]; + + zapi_nexthop_from_nexthop(api_nh, nh); + api_nhg.nexthop_num++; + } + + if (backup_nhg) { + for (ALL_NEXTHOPS_PTR(backup_nhg, nh)) { + if (api_nhg.backup_nexthop_num >= MULTIPATH_NUM) { + zlog_warn( + "%s: number of backup nexthops greater than max multipath size, truncating", + __func__); + break; + } + api_nh = &api_nhg.backup_nexthops + [api_nhg.backup_nexthop_num]; + + zapi_backup_nexthop_from_nexthop(api_nh, nh); + api_nhg.backup_nexthop_num++; + } + } + + zclient_nhg_send(zclient, ZEBRA_NHG_ADD, &api_nhg); +} + +void nhg_del(uint32_t id) +{ + struct zapi_nhg api_nhg = {}; + + api_nhg.id = id; + + zclient_nhg_send(zclient, ZEBRA_NHG_DEL, &api_nhg); +} + +void route_add(const struct prefix *p, vrf_id_t vrf_id, uint8_t instance, + uint32_t nhgid, const struct nexthop_group *nhg, const struct nexthop_group *backup_nhg) { struct zapi_route api; @@ -376,14 +426,20 @@ void route_add(const struct prefix *p, vrf_id_t vrf_id, SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); - for (ALL_NEXTHOPS_PTR(nhg, nh)) { - api_nh = &api.nexthops[i]; + /* Only send via ID if nhgroup has been successfully installed */ + if (nhgid && sharp_nhgroup_id_is_installed(nhgid)) { + SET_FLAG(api.message, ZAPI_MESSAGE_NHG); + api.nhgid = nhgid; + } else { + for (ALL_NEXTHOPS_PTR(nhg, nh)) { + api_nh = &api.nexthops[i]; - zapi_nexthop_from_nexthop(api_nh, nh); + zapi_nexthop_from_nexthop(api_nh, nh); - i++; + i++; + } + api.nexthop_num = i; } - api.nexthop_num = i; /* Include backup nexthops, if present */ if (backup_nhg && backup_nhg->nexthop) { @@ -668,6 +724,33 @@ void sharp_zebra_send_arp(const struct interface *ifp, const struct prefix *p) zclient_send_neigh_discovery_req(zclient, ifp, p); } +static int nhg_notify_owner(ZAPI_CALLBACK_ARGS) +{ + enum zapi_nhg_notify_owner note; + uint32_t id; + + if (!zapi_nhg_notify_decode(zclient->ibuf, &id, ¬e)) + return -1; + + switch (note) { + case ZAPI_NHG_INSTALLED: + sharp_nhgroup_id_set_installed(id, true); + zlog_debug("Installed nhg %u", id); + break; + case ZAPI_NHG_FAIL_INSTALL: + zlog_debug("Failed install of nhg %u", id); + break; + case ZAPI_NHG_REMOVED: + zlog_debug("Removed nhg %u", id); + break; + case ZAPI_NHG_REMOVE_FAIL: + zlog_debug("Failed removal of nhg %u", id); + break; + } + + return 0; +} + void sharp_zebra_init(void) { struct zclient_options opt = {.receive_notify = true}; @@ -684,6 +767,7 @@ void sharp_zebra_init(void) zclient->route_notify_owner = route_notify_owner; zclient->nexthop_update = sharp_nexthop_update; zclient->import_check_update = sharp_nexthop_update; + zclient->nhg_notify_owner = nhg_notify_owner; zclient->redistribute_route_add = sharp_redistribute_route; zclient->redistribute_route_del = sharp_redistribute_route; diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h index 0a44fa694f..4a767ababf 100644 --- a/sharpd/sharp_zebra.h +++ b/sharpd/sharp_zebra.h @@ -29,15 +29,18 @@ int sharp_zclient_create(uint32_t session_id); int sharp_zclient_delete(uint32_t session_id); extern void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label); +extern void nhg_add(uint32_t id, const struct nexthop_group *nhg, + const struct nexthop_group *backup_nhg); +extern void nhg_del(uint32_t id); extern void route_add(const struct prefix *p, vrf_id_t, uint8_t instance, - const struct nexthop_group *nhg, + uint32_t nhgid, const struct nexthop_group *nhg, const struct nexthop_group *backup_nhg); extern void route_delete(struct prefix *p, vrf_id_t vrf_id, uint8_t instance); extern void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import, bool watch, bool connected); extern void sharp_install_routes_helper(struct prefix *p, vrf_id_t vrf_id, - uint8_t instance, + uint8_t instance, uint32_t nhgid, const struct nexthop_group *nhg, const struct nexthop_group *backup_nhg, uint32_t routes); diff --git a/tests/subdir.am b/tests/subdir.am index 2cdb214f0b..d7318efc72 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -12,8 +12,10 @@ TESTS_BGPD = \ tests/bgpd/test_mp_attr \ tests/bgpd/test_mpath \ tests/bgpd/test_bgp_table +IGNORE_BGPD = else TESTS_BGPD = +IGNORE_BGPD = --ignore=bgpd/ endif if ISISD @@ -23,16 +25,20 @@ TESTS_ISISD = \ tests/isisd/test_isis_spf \ tests/isisd/test_isis_vertex_queue \ # end +IGNORE_ISISD = else TESTS_ISISD = +IGNORE_ISISD = --ignore=isisd/ endif if OSPF6D TESTS_OSPF6D = \ tests/ospf6d/test_lsdb \ # end +IGNORE_OSPF6D = else TESTS_OSPF6D = +IGNORE_OSPF6D = --ignore=ospf6d/ endif clippy_scan += \ @@ -368,7 +374,7 @@ EXTRA_DIST += \ .PHONY: tests/tests.xml tests/tests.xml: $(check_PROGRAMS) - ( cd tests; $(PYTHON) ../$(srcdir)/tests/runtests.py --junitxml=tests.xml -v ../$(srcdir)/tests; ) + ( cd tests; $(PYTHON) ../$(srcdir)/tests/runtests.py --junitxml=tests.xml -v ../$(srcdir)/tests $(IGNORE_BGPD) $(IGNORE_ISISD) $(IGNORE_OSPF6D); ) check: tests/tests.xml clean-local: clean-tests 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 2a00398e28..32b219283b 100755..100644 --- a/tests/topotests/all-protocol-startup/test_all_protocol_startup.py +++ b/tests/topotests/all-protocol-startup/test_all_protocol_startup.py @@ -113,8 +113,8 @@ def setup_module(module): net['r%s' % i].loadConf('ospfd', '%s/r%s/ospfd.conf' % (thisDir, i)) if net['r1'].checkRouterVersion('<', '4.0'): net['r%s' % i].loadConf('ospf6d', '%s/r%s/ospf6d.conf-pre-v4' % (thisDir, i)) - else: - net['r%s' % i].loadConf('ospf6d', '%s/r%s/ospf6d.conf' % (thisDir, i)) + else: + net['r%s' % i].loadConf('ospf6d', '%s/r%s/ospf6d.conf' % (thisDir, i)) net['r%s' % i].loadConf('isisd', '%s/r%s/isisd.conf' % (thisDir, i)) net['r%s' % i].loadConf('bgpd', '%s/r%s/bgpd.conf' % (thisDir, i)) if net['r%s' % i].daemon_available('ldpd'): @@ -374,26 +374,36 @@ def route_get_nhg_id(route_str): nhg_id = int(match.group(1)) return nhg_id -def verify_nexthop_group(nhg_id, recursive=False): +def verify_nexthop_group(nhg_id, recursive=False, ecmp=0): # Verify NHG is valid/installed output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id) match = re.search(r"Valid", output) assert match is not None, "Nexthop Group ID=%d not marked Valid" % nhg_id - # If recursive, we need to look at its resolved group - if recursive: - match = re.search(r"Depends: \((\d+)\)", output) - resolved_id = int(match.group(1)) - verify_nexthop_group(resolved_id, False) + if ecmp or recursive: + match = re.search(r"Depends:.*\n", output) + assert match is not None, "Nexthop Group ID=%d has no depends" % nhg_id + + # list of IDs in group + depends = re.findall(r"\((\d+)\)", match.group(0)) + + if ecmp: + assert (len(depends) == ecmp), "Nexthop Group ID=%d doesn't match ecmp size" % nhg_id + else: + # If recursive, we need to look at its resolved group + assert (len(depends) == 1), "Nexthop Group ID=%d should only have one recursive depend" % nhg_id + resolved_id = int(depends[0]) + verify_nexthop_group(resolved_id, False) + else: match = re.search(r"Installed", output) assert match is not None, "Nexthop Group ID=%d not marked Installed" % nhg_id -def verify_route_nexthop_group(route_str, recursive=False): +def verify_route_nexthop_group(route_str, recursive=False, ecmp=0): # Verify route and that zebra created NHGs for and they are valid/installed nhg_id = route_get_nhg_id(route_str) - verify_nexthop_group(nhg_id, recursive) + verify_nexthop_group(nhg_id, recursive, ecmp) def test_nexthop_groups(): global fatal_error @@ -611,7 +621,7 @@ def test_ospfv2_interfaces(): actual = net['r%s' % i].cmd('vtysh -c "show ip ospf interface" 2> /dev/null').rstrip() # Mask out Bandwidth portion. They may change.. actual = re.sub(r"BW [0-9]+ Mbit", "BW XX Mbit", actual) - actual = re.sub(r"ifindex [0-9]", "ifindex X", actual) + actual = re.sub(r"ifindex [0-9]", "ifindex X", actual) # Drop time in next due actual = re.sub(r"Hello due in [0-9\.]+s", "Hello due in XX.XXXs", actual) @@ -882,45 +892,45 @@ def test_bgp_ipv4(): print("******************************************\n") diffresult = {} for i in range(1, 2): - success = 0 - for refTableFile in (glob.glob( - '%s/r%s/show_bgp_ipv4*.ref' % (thisDir, i))): - if os.path.isfile(refTableFile): - # Read expected result from file - expected = open(refTableFile).read().rstrip() - # Fix newlines (make them all the same) - expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) - - # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show bgp ipv4" 2> /dev/null').rstrip() - # Remove summary line (changed recently) - actual = re.sub(r'Total number.*', '', actual) - actual = re.sub(r'Displayed.*', '', actual) - actual = actual.rstrip() - # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) - - # Generate Diff - diff = topotest.get_textdiff(actual, expected, - title1="actual SHOW BGP IPv4", - title2="expected SHOW BGP IPv4") - - # Empty string if it matches, otherwise diff contains unified diff - if diff: - diffresult[refTableFile] = diff - else: - success = 1 - print("template %s matched: r%s ok" % (refTableFile, i)) - break - - if not success: - resultstr = 'No template matched.\n' - for f in diffresult.iterkeys(): - resultstr += ( - 'template %s: r%s failed SHOW BGP IPv4 check:\n%s\n' - % (f, i, diffresult[f])) - raise AssertionError( - "SHOW BGP IPv4 failed for router r%s:\n%s" % (i, resultstr)) + success = 0 + for refTableFile in (glob.glob( + '%s/r%s/show_bgp_ipv4*.ref' % (thisDir, i))): + if os.path.isfile(refTableFile): + # Read expected result from file + expected = open(refTableFile).read().rstrip() + # Fix newlines (make them all the same) + expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) + + # Actual output from router + actual = net['r%s' % i].cmd('vtysh -c "show bgp ipv4" 2> /dev/null').rstrip() + # Remove summary line (changed recently) + actual = re.sub(r'Total number.*', '', actual) + actual = re.sub(r'Displayed.*', '', actual) + actual = actual.rstrip() + # Fix newlines (make them all the same) + actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + + # Generate Diff + diff = topotest.get_textdiff(actual, expected, + title1="actual SHOW BGP IPv4", + title2="expected SHOW BGP IPv4") + + # Empty string if it matches, otherwise diff contains unified diff + if diff: + diffresult[refTableFile] = diff + else: + success = 1 + print("template %s matched: r%s ok" % (refTableFile, i)) + break + + if not success: + resultstr = 'No template matched.\n' + for f in diffresult.iterkeys(): + resultstr += ( + 'template %s: r%s failed SHOW BGP IPv4 check:\n%s\n' + % (f, i, diffresult[f])) + raise AssertionError( + "SHOW BGP IPv4 failed for router r%s:\n%s" % (i, resultstr)) # Make sure that all daemons are running for i in range(1, 2): @@ -945,44 +955,44 @@ def test_bgp_ipv6(): print("******************************************\n") diffresult = {} for i in range(1, 2): - success = 0 - for refTableFile in (glob.glob( - '%s/r%s/show_bgp_ipv6*.ref' % (thisDir, i))): - if os.path.isfile(refTableFile): - # Read expected result from file - expected = open(refTableFile).read().rstrip() - # Fix newlines (make them all the same) - expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) - - # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show bgp ipv6" 2> /dev/null').rstrip() - # Remove summary line (changed recently) - actual = re.sub(r'Total number.*', '', actual) - actual = re.sub(r'Displayed.*', '', actual) - actual = actual.rstrip() - # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) - - # Generate Diff - diff = topotest.get_textdiff(actual, expected, - title1="actual SHOW BGP IPv6", - title2="expected SHOW BGP IPv6") - - # Empty string if it matches, otherwise diff contains unified diff - if diff: - diffresult[refTableFile] = diff - else: - success = 1 - print("template %s matched: r%s ok" % (refTableFile, i)) - - if not success: - resultstr = 'No template matched.\n' - for f in diffresult.iterkeys(): - resultstr += ( - 'template %s: r%s failed SHOW BGP IPv6 check:\n%s\n' - % (f, i, diffresult[f])) - raise AssertionError( - "SHOW BGP IPv6 failed for router r%s:\n%s" % (i, resultstr)) + success = 0 + for refTableFile in (glob.glob( + '%s/r%s/show_bgp_ipv6*.ref' % (thisDir, i))): + if os.path.isfile(refTableFile): + # Read expected result from file + expected = open(refTableFile).read().rstrip() + # Fix newlines (make them all the same) + expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) + + # Actual output from router + actual = net['r%s' % i].cmd('vtysh -c "show bgp ipv6" 2> /dev/null').rstrip() + # Remove summary line (changed recently) + actual = re.sub(r'Total number.*', '', actual) + actual = re.sub(r'Displayed.*', '', actual) + actual = actual.rstrip() + # Fix newlines (make them all the same) + actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + + # Generate Diff + diff = topotest.get_textdiff(actual, expected, + title1="actual SHOW BGP IPv6", + title2="expected SHOW BGP IPv6") + + # Empty string if it matches, otherwise diff contains unified diff + if diff: + diffresult[refTableFile] = diff + else: + success = 1 + print("template %s matched: r%s ok" % (refTableFile, i)) + + if not success: + resultstr = 'No template matched.\n' + for f in diffresult.iterkeys(): + resultstr += ( + 'template %s: r%s failed SHOW BGP IPv6 check:\n%s\n' + % (f, i, diffresult[f])) + raise AssertionError( + "SHOW BGP IPv6 failed for router r%s:\n%s" % (i, resultstr)) # Make sure that all daemons are running for i in range(1, 2): @@ -1101,6 +1111,34 @@ def test_nexthop_groups_with_route_maps(): net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE"') net["r1"].cmd('vtysh -c "c t" -c "no ip prefix-list NOPE seq 5 permit %s/32"' % permit_route_str) +def test_nexthop_group_replace(): + global fatal_error + global net + + # Skip if previous fatal error condition is raised + if (fatal_error != ""): + pytest.skip(fatal_error) + + print("\n\n** Verifying Nexthop Groups") + print("******************************************\n") + + ### Nexthop Group Tests + + ## 2-Way ECMP Directly Connected + + net["r1"].cmd('vtysh -c "c t" -c "nexthop-group replace" -c "nexthop 1.1.1.1 r1-eth1 onlink" -c "nexthop 1.1.1.2 r1-eth2 onlink"') + + # Create with sharpd using nexthop-group + net["r1"].cmd('vtysh -c "sharp install routes 3.3.3.1 nexthop-group replace 1"') + + verify_route_nexthop_group("3.3.3.1/32") + + # Change the nexthop group + net["r1"].cmd('vtysh -c "c t" -c "nexthop-group replace" -c "no nexthop 1.1.1.1 r1-eth1 onlink" -c "nexthop 1.1.1.3 r1-eth1 onlink" -c "nexthop 1.1.1.4 r1-eth4 onlink"') + + # Verify it updated. We can just check install and ecmp count here. + verify_route_nexthop_group("3.3.3.1/32", False, 3) + def test_mpls_interfaces(): global fatal_error global net diff --git a/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py b/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py index 41fa7c0a09..41fa7c0a09 100755..100644 --- a/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py +++ b/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py diff --git a/tests/topotests/bgp-evpn-mh/test_evpn_mh.py b/tests/topotests/bgp-evpn-mh/test_evpn_mh.py index ee50a422a7..9af22c06bd 100644 --- a/tests/topotests/bgp-evpn-mh/test_evpn_mh.py +++ b/tests/topotests/bgp-evpn-mh/test_evpn_mh.py @@ -477,9 +477,9 @@ def check_es(dut): vtep_ips.append(vtep["vtep_ip"]) if "local" in types: - result = check_local_es(esi, vtep_ips, dut.name, []) + result = check_local_es(esi, vtep_ips, dut.name, []) else: - result = check_remote_es(esi, vtep_ips, dut.name, []) + result = check_remote_es(esi, vtep_ips, dut.name, []) if result: return result diff --git a/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py b/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py index 607b036c6a..607b036c6a 100755..100644 --- a/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py +++ b/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py diff --git a/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py b/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py index 22952f645c..22952f645c 100755..100644 --- a/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py +++ b/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py diff --git a/tests/topotests/bgp-route-map/test_route_map_topo1.py b/tests/topotests/bgp-route-map/test_route_map_topo1.py index 1aa951edaa..1aa951edaa 100755..100644 --- a/tests/topotests/bgp-route-map/test_route_map_topo1.py +++ b/tests/topotests/bgp-route-map/test_route_map_topo1.py diff --git a/tests/topotests/bgp-route-map/test_route_map_topo2.py b/tests/topotests/bgp-route-map/test_route_map_topo2.py index 3056aa29f3..3056aa29f3 100755..100644 --- a/tests/topotests/bgp-route-map/test_route_map_topo2.py +++ b/tests/topotests/bgp-route-map/test_route_map_topo2.py diff --git a/tests/topotests/bgp_as_allow_in/test_bgp_as_allow_in.py b/tests/topotests/bgp_as_allow_in/test_bgp_as_allow_in.py index f9d22a3a36..f9d22a3a36 100755..100644 --- a/tests/topotests/bgp_as_allow_in/test_bgp_as_allow_in.py +++ b/tests/topotests/bgp_as_allow_in/test_bgp_as_allow_in.py diff --git a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py index 3ad989c601..0d99f23ad9 100755..100644 --- a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py +++ b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py @@ -143,7 +143,7 @@ def setup_module(mod): logger.info('result: '+output); router = tgen.gears['r1'] - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): if rname == 'r1': router.load_config( TopoRouter.RD_ZEBRA, diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py index fdbd317093..fdbd317093 100755..100644 --- a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py diff --git a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py index e1ec0ea81b..e1ec0ea81b 100755..100644 --- a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py +++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py diff --git a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py index dc06b7131a..dc06b7131a 100755..100644 --- a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py +++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py diff --git a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py index bb88e47415..bb88e47415 100755..100644 --- a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py +++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py diff --git a/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py b/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py index c15b88d371..c15b88d371 100755..100644 --- a/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py +++ b/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py diff --git a/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py index bb13d54019..bb13d54019 100755..100644 --- a/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py +++ b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py diff --git a/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py b/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py index 6344f7bb40..6344f7bb40 100755..100644 --- a/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py +++ b/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py diff --git a/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py index fef6eb71dc..fef6eb71dc 100755..100644 --- a/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py +++ b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py diff --git a/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py b/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py index 0fabd90341..0fabd90341 100755..100644 --- a/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py +++ b/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py index 1947548b3e..1947548b3e 100755..100644 --- a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py index 6c106060b8..6c106060b8 100755..100644 --- a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py diff --git a/tests/topotests/eigrp-topo1/test_eigrp_topo1.py b/tests/topotests/eigrp-topo1/test_eigrp_topo1.py index 70666a3d61..3ce1472ac0 100644 --- a/tests/topotests/eigrp-topo1/test_eigrp_topo1.py +++ b/tests/topotests/eigrp-topo1/test_eigrp_topo1.py @@ -252,7 +252,7 @@ def ip_eigrp_topo(node): if code not in ["P", "A", "U", "Q", "R", "r", "s"]: continue - if not result.has_key(code): + if code not in result: result[code] = {} # Split network from the rest diff --git a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py index e913105e43..e913105e43 100755..100644 --- a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py +++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py diff --git a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py index 9e385823fc..c1eb7d68bb 100755..100644 --- a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py +++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py @@ -26,9 +26,9 @@ Following tests are covered to test EVPN-Type5 functionality: 1. RD verification (manual/auto). 2. RT verification(manual) 3. In an active/standby EVPN implementation, if active DCG goes down, - secondary takes over. + secondary takes over. 4. EVPN routes are advertised/withdrawn, based on VNFs - advertising/withdrawing IP prefixes. + advertising/withdrawing IP prefixes. 5. Route-map operations for EVPN address family. 6. BGP attributes for EVPN address-family. """ diff --git a/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py index 65515f22cc..7943b94189 100644 --- a/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py +++ b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py @@ -210,7 +210,7 @@ def test_isis_linux_route_installation(): dist = platform.dist() if (dist[1] == "16.04"): - pytest.skip("Kernel not supported for vrf") + pytest.skip("Kernel not supported for vrf") "Check whether all expected routes are present and installed in the OS" tgen = get_topogen() @@ -267,7 +267,7 @@ def test_isis_linux_route6_installation(): dist = platform.dist() if (dist[1] == "16.04"): - pytest.skip("Kernel not supported for vrf") + pytest.skip("Kernel not supported for vrf") "Check whether all expected routes are present and installed in the OS" tgen = get_topogen() diff --git a/tests/topotests/ldp-topo1/test_ldp_topo1.py b/tests/topotests/ldp-topo1/test_ldp_topo1.py index 31adeafbf6..31adeafbf6 100755..100644 --- a/tests/topotests/ldp-topo1/test_ldp_topo1.py +++ b/tests/topotests/ldp-topo1/test_ldp_topo1.py diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py index a3d846edbb..72b99eeba8 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -28,6 +28,7 @@ from lib import topotest from lib.topolog import logger from lib.topogen import TopoRouter, get_topogen +from lib.topotest import frr_unicode # Import common_config to use commomnly used APIs from lib.common_config import ( @@ -393,7 +394,7 @@ def __create_bgp_unicast_neighbor( # Generating IPs for verification network_list = generate_ips(network, no_of_network) for ip in network_list: - ip = str(ipaddress.ip_network(unicode(ip))) + ip = str(ipaddress.ip_network(frr_unicode(ip))) cmd = "network {}".format(ip) if del_action: @@ -1037,7 +1038,7 @@ def verify_router_id(tgen, topo, input_dict): logger.info("Checking router %s router-id", router) show_bgp_json = run_frr_cmd(rnode, "show bgp summary json", isjson=True) router_id_out = show_bgp_json["ipv4Unicast"]["routerId"] - router_id_out = ipaddress.IPv4Address(unicode(router_id_out)) + router_id_out = ipaddress.IPv4Address(frr_unicode(router_id_out)) # Once router-id is deleted, highest interface ip should become # router-id @@ -1045,7 +1046,7 @@ def verify_router_id(tgen, topo, input_dict): router_id = find_interface_with_greater_ip(topo, router) else: router_id = input_dict[router]["bgp"]["router_id"] - router_id = ipaddress.IPv4Address(unicode(router_id)) + router_id = ipaddress.IPv4Address(frr_unicode(router_id)) if router_id == router_id_out: logger.info("Found expected router-id %s for router %s", router_id, router) @@ -2286,7 +2287,7 @@ def verify_best_path_as_per_bgp_attribute( routes = generate_ips(_network, no_of_ip) for route in routes: - route = str(ipaddress.ip_network(unicode(route))) + route = str(ipaddress.ip_network(frr_unicode(route))) if route in sh_ip_bgp_json["routes"]: route_attributes = sh_ip_bgp_json["routes"][route] @@ -2604,7 +2605,7 @@ def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None) ip_list = generate_ips(network, no_of_ip) for st_rt in ip_list: - st_rt = str(ipaddress.ip_network(unicode(st_rt))) + st_rt = str(ipaddress.ip_network(frr_unicode(st_rt))) _addr_type = validate_ip_address(st_rt) if _addr_type != addr_type: @@ -2742,7 +2743,7 @@ def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None) ip_list = generate_ips(network, no_of_network) for st_rt in ip_list: - st_rt = str(ipaddress.ip_network(unicode(st_rt))) + st_rt = str(ipaddress.ip_network(frr_unicode(st_rt))) _addr_type = validate_ip_address(st_rt) if _addr_type != addr_type: diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index 6dd8d646f3..1fa6d35101 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -31,16 +31,14 @@ from re import search as re_search from tempfile import mkdtemp import os +import io import sys -import ConfigParser import traceback import socket import ipaddress import platform - if sys.version_info[0] > 2: - import io import configparser else: import StringIO @@ -48,7 +46,7 @@ else: from lib.topolog import logger, logger_config from lib.topogen import TopoRouter, get_topogen -from lib.topotest import interface_set_status, version_cmp +from lib.topotest import interface_set_status, version_cmp, frr_unicode FRRCFG_FILE = "frr_json.conf" FRRCFG_BKUP_FILE = "frr_json_initial.conf" @@ -638,7 +636,7 @@ def get_frr_ipv6_linklocal(tgen, router, intf=None, vrf=None): ll_per_if_count = 0 # Interface ip - m1 = re_search("inet6 (fe80[:a-fA-F0-9]+[\/0-9]+)", line) + m1 = re_search("inet6 (fe80[:a-fA-F0-9]+[/0-9]+)", line) if m1: local = m1.group(1) ll_per_if_count += 1 @@ -700,18 +698,20 @@ def start_topology(tgen, daemon=None): router_list = tgen.routers() ROUTER_LIST = sorted( - router_list.keys(), key=lambda x: int(re_search("\d+", x).group(0)) + router_list.keys(), key=lambda x: int(re_search("[0-9]+", x).group(0)) ) TMPDIR = os.path.join(LOGDIR, tgen.modname) + linux_ver = '' router_list = tgen.routers() for rname in ROUTER_LIST: router = router_list[rname] # It will help in debugging the failures, will give more details on which # specific kernel version tests are failing - linux_ver = router.run("uname -a") - logger.info("Logging platform related details: \n %s \n", linux_ver) + if linux_ver == '': + linux_ver = router.run("uname -a") + logger.info("Logging platform related details: \n %s \n", linux_ver) try: os.chdir(TMPDIR) @@ -827,7 +827,7 @@ def topo_daemons(tgen, topo): router_list = tgen.routers() ROUTER_LIST = sorted( - router_list.keys(), key=lambda x: int(re_search("\d+", x).group(0)) + router_list.keys(), key=lambda x: int(re_search("[0-9]+", x).group(0)) ) for rtr in ROUTER_LIST: @@ -1152,10 +1152,10 @@ def generate_ips(network, no_of_ips): addr_type = validate_ip_address(start_ip) if addr_type == "ipv4": - start_ip = ipaddress.IPv4Address(unicode(start_ip)) + start_ip = ipaddress.IPv4Address(frr_unicode(start_ip)) step = 2 ** (32 - mask) if addr_type == "ipv6": - start_ip = ipaddress.IPv6Address(unicode(start_ip)) + start_ip = ipaddress.IPv6Address(frr_unicode(start_ip)) step = 2 ** (128 - mask) next_ip = start_ip @@ -2667,7 +2667,7 @@ def verify_rib( nh_found = False for st_rt in ip_list: - st_rt = str(ipaddress.ip_network(unicode(st_rt))) + st_rt = str(ipaddress.ip_network(frr_unicode(st_rt))) _addr_type = validate_ip_address(st_rt) if _addr_type != addr_type: @@ -2863,7 +2863,7 @@ def verify_rib( nh_found = False for st_rt in ip_list: - st_rt = str(ipaddress.ip_network(unicode(st_rt))) + st_rt = str(ipaddress.ip_network(frr_unicode(st_rt))) _addr_type = validate_ip_address(st_rt) if _addr_type != addr_type: @@ -3012,7 +3012,7 @@ def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None): nh_found = False for st_rt in ip_list: - st_rt = str(ipaddress.ip_network(unicode(st_rt))) + st_rt = str(ipaddress.ip_network(frr_unicode(st_rt))) #st_rt = str(ipaddr.IPNetwork(unicode(st_rt))) _addr_type = validate_ip_address(st_rt) @@ -3119,7 +3119,7 @@ def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None): for st_rt in ip_list: #st_rt = str(ipaddr.IPNetwork(unicode(st_rt))) - st_rt = str(ipaddress.ip_network(unicode(st_rt))) + st_rt = str(ipaddress.ip_network(frr_unicode(st_rt))) _addr_type = validate_ip_address(st_rt) if _addr_type != addr_type: @@ -3268,7 +3268,7 @@ def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None): nh_found = False for st_rt in ip_list: - st_rt = str(ipaddress.ip_network(unicode(st_rt))) + st_rt = str(ipaddress.ip_network(frr_unicode(st_rt))) _addr_type = validate_ip_address(st_rt) if _addr_type != addr_type: @@ -3373,7 +3373,7 @@ def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None): nh_found = False for st_rt in ip_list: - st_rt = str(ipaddress.ip_network(unicode(st_rt))) + st_rt = str(ipaddress.ip_network(frr_unicode(st_rt))) _addr_type = validate_ip_address(st_rt) if _addr_type != addr_type: diff --git a/tests/topotests/lib/ltemplate.py b/tests/topotests/lib/ltemplate.py index 192c121008..3c93e1ac5c 100644 --- a/tests/topotests/lib/ltemplate.py +++ b/tests/topotests/lib/ltemplate.py @@ -157,8 +157,8 @@ def ltemplateTest(script, SkipIfFailed=True, CallOnFail=None, CheckFuncStr=None, if SkipIfFailed and tgen.routers_have_failure(): pytest.skip(tgen.errors) if numEntry > 0: - if not KeepGoing: - pytest.skip("Have %d errors" % numEntry) + if not KeepGoing: + pytest.skip("Have %d errors" % numEntry) if CheckFuncStr != None: check = eval(CheckFuncStr) @@ -172,8 +172,8 @@ def ltemplateTest(script, SkipIfFailed=True, CallOnFail=None, CheckFuncStr=None, if numFail > 0: luShowFail() fatal_error = "%d tests failed" % numFail - if not KeepGoing: - assert "scripts/cleanup_all.py failed" == "See summary output above", fatal_error + if not KeepGoing: + assert "scripts/cleanup_all.py failed" == "See summary output above", fatal_error # Memory leak test template def test_memory_leak(): diff --git a/tests/topotests/lib/lutil.py b/tests/topotests/lib/lutil.py index 4ea97a3692..05ed9c007d 100755..100644 --- a/tests/topotests/lib/lutil.py +++ b/tests/topotests/lib/lutil.py @@ -204,17 +204,17 @@ Total %-4d %-4d %d\n\ self.log('WARNING: JSON load failed -- confirm command output is in JSON format.') self.log('COMMAND OUTPUT:%s:' % report) - # Experiment: can we achieve the same match behavior via DOTALL - # without converting newlines to spaces? - out_nl = out - search_nl = re.search(regexp, out_nl, re.DOTALL); - self.l_last_nl = search_nl - # Set up for comparison - if search_nl != None: - group_nl = search_nl.group() - group_nl_converted = " ".join(group_nl.splitlines()) + # Experiment: can we achieve the same match behavior via DOTALL + # without converting newlines to spaces? + out_nl = out + search_nl = re.search(regexp, out_nl, re.DOTALL); + self.l_last_nl = search_nl + # Set up for comparison + if search_nl != None: + group_nl = search_nl.group() + group_nl_converted = " ".join(group_nl.splitlines()) else: - group_nl_converted = None + group_nl_converted = None out = " ".join(out.splitlines()) search = re.search(regexp, out) @@ -234,9 +234,9 @@ Total %-4d %-4d %d\n\ success = False level = 5 self.log('found:%s:' % ret, level) - # Experiment: compare matched strings obtained each way - if self.l_dotall_experiment and (group_nl_converted != ret): - self.log('DOTALL experiment: strings differ dotall=[%s] orig=[%s]' % (group_nl_converted, ret), 9) + # Experiment: compare matched strings obtained each way + if self.l_dotall_experiment and (group_nl_converted != ret): + self.log('DOTALL experiment: strings differ dotall=[%s] orig=[%s]' % (group_nl_converted, ret), 9) if op == 'pass' or op == 'fail': self.result(target, success, result) if js != None: @@ -297,13 +297,13 @@ def luCommand(target, command, regexp='.', op='none', result='', time=10, return def luLast(usenl=False): if usenl: - if LUtil.l_last_nl != None: - LUtil.log('luLast:%s:' % LUtil.l_last_nl.group(), 7) - return LUtil.l_last_nl + if LUtil.l_last_nl != None: + LUtil.log('luLast:%s:' % LUtil.l_last_nl.group(), 7) + return LUtil.l_last_nl else: - if LUtil.l_last != None: - LUtil.log('luLast:%s:' % LUtil.l_last.group(), 7) - return LUtil.l_last + if LUtil.l_last != None: + LUtil.log('luLast:%s:' % LUtil.l_last.group(), 7) + return LUtil.l_last def luInclude(filename, CallOnFail=None): tstFile = LUtil.base_script_dir + '/' + filename diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py index a2351bf747..9d6b8fa691 100644 --- a/tests/topotests/lib/ospf.py +++ b/tests/topotests/lib/ospf.py @@ -23,7 +23,7 @@ import traceback from time import sleep from lib.topolog import logger import ipaddr - +from lib.topotest import frr_unicode # Import common_config to use commomnly used APIs from lib.common_config import (create_common_configuration, @@ -516,7 +516,7 @@ def verify_ospf_neighbor(tgen, topo, dut=None, input_dict=None, lan=False): logger.debug("Entering lib API: verify_ospf_neighbor()") result = False if input_dict: - for router, rnode in tgen.routers().iteritems(): + for router, rnode in tgen.routers().items(): if 'ospf' not in topo['routers'][router]: continue @@ -584,7 +584,7 @@ def verify_ospf_neighbor(tgen, topo, dut=None, input_dict=None, lan=False): return errormsg continue else: - for router, rnode in tgen.routers().iteritems(): + for router, rnode in tgen.routers().items(): if 'ospf' not in topo['routers'][router]: continue @@ -694,7 +694,7 @@ def verify_ospf_rib(tgen, dut, input_dict, next_hop=None, additional_nexthops_in_required_nhs = [] found_hops = [] for routerInput in input_dict.keys(): - for router, rnode in router_list.iteritems(): + for router, rnode in router_list.items(): if router != dut: continue @@ -739,7 +739,7 @@ def verify_ospf_rib(tgen, dut, input_dict, next_hop=None, nh_found = False for st_rt in ip_list: - st_rt = str(ipaddr.IPNetwork(unicode(st_rt))) + st_rt = str(ipaddr.IPNetwork(frr_unicode(st_rt))) _addr_type = validate_ip_address(st_rt) if _addr_type != 'ipv4': @@ -927,7 +927,7 @@ def verify_ospf_interface(tgen, topo, dut=None,lan=False, input_dict=None): logger.debug("Entering lib API: verify_ospf_interface()") result = False - for router, rnode in tgen.routers().iteritems(): + for router, rnode in tgen.routers().items(): if 'ospf' not in topo['routers'][router]: continue diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index 171a894b35..ffdcb683e7 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -40,6 +40,7 @@ Basic usage instructions: import os import sys +import io import logging import json @@ -97,7 +98,7 @@ tgen_defaults = { "verbosity": "info", "frrdir": "/usr/lib/frr", "routertype": "frr", - "memleak_path": None, + "memleak_path": "", } @@ -182,7 +183,7 @@ class Topogen(object): params["frrdir"] = self.config.get(self.CONFIG_SECTION, "frrdir") params["memleak_path"] = self.config.get(self.CONFIG_SECTION, "memleak_path") - if not params.has_key("routertype"): + if "routertype" not in params: params["routertype"] = self.config.get(self.CONFIG_SECTION, "routertype") self.gears[name] = TopoRouter(self, cls, name, **params) @@ -360,7 +361,7 @@ class Topogen(object): memleak_file = os.environ.get("TOPOTESTS_CHECK_MEMLEAK") or self.config.get( self.CONFIG_SECTION, "memleak_path" ) - if memleak_file is None: + if memleak_file == "" or memleak_file == None: return False return True @@ -586,7 +587,7 @@ class TopoRouter(TopoGear): self.cls = cls self.options = {} self.routertype = params.get("routertype", "frr") - if not params.has_key("privateDirs"): + if "privateDirs" not in params: params["privateDirs"] = self.PRIVATE_DIRS self.options["memleak_path"] = params.get("memleak_path", None) @@ -822,7 +823,7 @@ class TopoRouter(TopoGear): memleak_file = ( os.environ.get("TOPOTESTS_CHECK_MEMLEAK") or self.options["memleak_path"] ) - if memleak_file is None: + if memleak_file == "" or memleak_file == None: return self.stop() @@ -1011,7 +1012,7 @@ def diagnose_env_linux(): logger.info("Running environment diagnostics") # Load configuration - config = configparser.ConfigParser(tgen_defaults) + config = configparser.ConfigParser(defaults=tgen_defaults) pytestini_path = os.path.join(CWD, "../pytest.ini") config.read(pytestini_path) @@ -1106,10 +1107,10 @@ def diagnose_env_linux(): # TODO remove me when we start supporting exabgp >= 4 try: - output = subprocess.check_output(["exabgp", "-v"]) - line = output.split("\n")[0] - version = line.split(" ")[2] - if topotest.version_cmp(version, "4") >= 0: + p = os.popen("exabgp -v") + line = p.readlines() + version = line[0].split() + if topotest.version_cmp(version[2], "4") >= 0: logger.warning( "BGP topologies are still using exabgp version 3, expect failures" ) diff --git a/tests/topotests/lib/topojson.py b/tests/topotests/lib/topojson.py index b3af09aa99..6535918e36 100644 --- a/tests/topotests/lib/topojson.py +++ b/tests/topotests/lib/topojson.py @@ -216,7 +216,7 @@ def build_topo_from_json(tgen, topo): # Physical Interfaces if "links" in topo['switches'][curSwitch]: for destRouterLink, data in sorted( - topo['switches'][curSwitch]['links'].iteritems()): + topo['switches'][curSwitch]['links'].items()): # Loopback interfaces if "dst_node" in data: diff --git a/tests/topotests/lib/topolog.py b/tests/topotests/lib/topolog.py index 0dfa870930..9fde01cca0 100644 --- a/tests/topotests/lib/topolog.py +++ b/tests/topotests/lib/topolog.py @@ -93,7 +93,7 @@ class Logger(object): """ if log_level is None: log_level = self.log_level - if self.loggers.has_key(name): + if name in self.loggers: return self.loggers[name] nlogger = logging.Logger(name, level=log_level) diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index 00bfac4103..a187971e41 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -992,11 +992,11 @@ class Router(Node): # Backward compatibility: # Load configuration defaults like topogen. self.config_defaults = configparser.ConfigParser( - { + defaults = { "verbosity": "info", "frrdir": "/usr/lib/frr", "routertype": "frr", - "memleak_path": None, + "memleak_path": "", } ) self.config_defaults.read( @@ -1525,7 +1525,7 @@ class Router(Node): for daemon in self.daemons: if (self.daemons[daemon] == 1) and not (daemon in daemonsRunning): sys.stderr.write("%s: Daemon %s not running\n" % (self.name, daemon)) - if daemon is "staticd": + if daemon == "staticd": sys.stderr.write( "You may have a copy of staticd installed but are attempting to test against\n" ) @@ -1591,7 +1591,7 @@ class Router(Node): logger.info("{}: running version: {}".format(self.name, self.version)) rversion = self.version - if rversion is None: + if rversion == None: return False result = version_cmp(rversion, version) @@ -1737,3 +1737,10 @@ class LegacySwitch(OVSSwitch): def __init__(self, name, **params): OVSSwitch.__init__(self, name, failMode="standalone", **params) self.switchIP = None + +def frr_unicode(s): + '''Convert string to unicode, depending on python version''' + if sys.version_info[0] > 2: + return s + else: + return unicode(s) diff --git a/tests/topotests/ospf-topo2/test_ospf_topo2.py b/tests/topotests/ospf-topo2/test_ospf_topo2.py index 0b6f568462..79e8e6bf58 100644 --- a/tests/topotests/ospf-topo2/test_ospf_topo2.py +++ b/tests/topotests/ospf-topo2/test_ospf_topo2.py @@ -86,19 +86,19 @@ def setup_module(mod): os.path.join(CWD, '{}/ospfd.conf'.format(rname)) ) - # What is this? OSPF Unnumbered depends on the rp_filter - # being set appropriately( HA! ) - # Effectively we are putting different /32's on the interface - # the multicast packet delivery is somewhat controlled by - # the rp_filter. Setting it to '0' allows the OS to pass - # up the mcast packet not destined for the local routers - # network. - topotest.set_sysctl(tgen.net['r1'], - 'net.ipv4.conf.r1-eth1.rp_filter', 0) + # What is this? OSPF Unnumbered depends on the rp_filter + # being set appropriately( HA! ) + # Effectively we are putting different /32's on the interface + # the multicast packet delivery is somewhat controlled by + # the rp_filter. Setting it to '0' allows the OS to pass + # up the mcast packet not destined for the local routers + # network. + topotest.set_sysctl(tgen.net['r1'], + 'net.ipv4.conf.r1-eth1.rp_filter', 0) topotest.set_sysctl(tgen.net['r1'], 'net.ipv4.conf.all.rp_filter', 0) - topotest.set_sysctl(tgen.net['r2'], - 'net.ipv4.conf.r2-eth1.rp_filter', 0) + topotest.set_sysctl(tgen.net['r2'], + 'net.ipv4.conf.r2-eth1.rp_filter', 0) topotest.set_sysctl(tgen.net['r2'], 'net.ipv4.conf.all.rp_filter', 0) diff --git a/tests/topotests/pbr-topo1/test_pbr_topo1.py b/tests/topotests/pbr-topo1/test_pbr_topo1.py index 9ae4cce360..91979a8f04 100644 --- a/tests/topotests/pbr-topo1/test_pbr_topo1.py +++ b/tests/topotests/pbr-topo1/test_pbr_topo1.py @@ -88,8 +88,8 @@ def setup_module(module): krel = platform.release() if topotest.version_cmp(krel, "4.10") < 0: - tgen.errors = "Newer kernel than 4.9 needed for pbr tests" - pytest.skip(tgen.errors) + tgen.errors = "Newer kernel than 4.9 needed for pbr tests" + pytest.skip(tgen.errors) router_list = tgen.routers() for rname, router in router_list.items(): @@ -150,8 +150,8 @@ def test_pbr_data(): test_func = partial(topotest.router_json_cmp, router, "show pbr interface json", expected) _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) assertmsg = '"show pbr interface" mismatches on {}'.format(router.name) - if result is not None: - gather_pbr_data_on_error(router) + if result is not None: + gather_pbr_data_on_error(router) assert result is None, assertmsg map_file = "{}/{}/pbr-map.json".format(CWD, router.name) @@ -164,8 +164,8 @@ def test_pbr_data(): test_func = partial(topotest.router_json_cmp, router, "show pbr map json", expected) _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) assertmsg = '"show pbr map" mismatches on {}'.format(router.name) - if result is not None: - gather_pbr_data_on_error(router) + if result is not None: + gather_pbr_data_on_error(router) assert result is None, assertmsg nexthop_file = "{}/{}/pbr-nexthop-groups.json".format(CWD, router.name) @@ -178,8 +178,8 @@ def test_pbr_data(): test_func = partial(topotest.router_json_cmp, router, "show pbr nexthop-groups json", expected) _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) assertmsg = '"show pbr nexthop-groups" mismatches on {}'.format(router.name) - if result is not None: - gather_pbr_data_on_error(router) + if result is not None: + gather_pbr_data_on_error(router) assert result is None, assertmsg def test_pbr_flap(): @@ -215,8 +215,8 @@ def test_pbr_flap(): test_func = partial(topotest.router_json_cmp, router, "show pbr interface json", expected) _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) assertmsg = '"show pbr interface" mismatches on {}'.format(router.name) - if result is not None: - gather_pbr_data_on_error(router) + if result is not None: + gather_pbr_data_on_error(router) assert result is None, assertmsg diff --git a/tests/topotests/rip-topo1/test_rip_topo1.py b/tests/topotests/rip-topo1/test_rip_topo1.py index 7ff18ba524..de11b78824 100755..100644 --- a/tests/topotests/rip-topo1/test_rip_topo1.py +++ b/tests/topotests/rip-topo1/test_rip_topo1.py @@ -91,11 +91,11 @@ class NetworkTopo(Topo): switch[4] = self.addSwitch("sw4", cls=topotest.LegacySwitch) self.addLink(switch[4], router[3], intfName2="r3-eth0") - switch[5] = self.addSwitch("sw5", cls=topotest.LegacySwitch) - self.addLink(switch[5], router[1], intfName2="r1-eth2") + switch[5] = self.addSwitch("sw5", cls=topotest.LegacySwitch) + self.addLink(switch[5], router[1], intfName2="r1-eth2") - switch[6] = self.addSwitch("sw6", cls=topotest.LegacySwitch) - self.addLink(switch[6], router[1], intfName2="r1-eth3") + switch[6] = self.addSwitch("sw6", cls=topotest.LegacySwitch) + self.addLink(switch[6], router[1], intfName2="r1-eth3") ##################################################### diff --git a/tests/topotests/ripng-topo1/test_ripng_topo1.py b/tests/topotests/ripng-topo1/test_ripng_topo1.py index 2976cdefe4..2976cdefe4 100755..100644 --- a/tests/topotests/ripng-topo1/test_ripng_topo1.py +++ b/tests/topotests/ripng-topo1/test_ripng_topo1.py diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 4edbb7a889..9993e73353 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1173,12 +1173,14 @@ static char **new_completion(const char *text, int start, int end) } /* Vty node structures. */ +#ifdef HAVE_BGPD static struct cmd_node bgp_node = { .name = "bgp", .node = BGP_NODE, .parent_node = CONFIG_NODE, .prompt = "%s(config-router)# ", }; +#endif /* HAVE_BGPD */ static struct cmd_node rip_node = { .name = "rip", @@ -1187,19 +1189,23 @@ static struct cmd_node rip_node = { .prompt = "%s(config-router)# ", }; +#ifdef HAVE_ISISD static struct cmd_node isis_node = { .name = "isis", .node = ISIS_NODE, .parent_node = CONFIG_NODE, .prompt = "%s(config-router)# ", }; +#endif /* HAVE_ISISD */ +#ifdef HAVE_FABRICD static struct cmd_node openfabric_node = { .name = "openfabric", .node = OPENFABRIC_NODE, .parent_node = CONFIG_NODE, .prompt = "%s(config-router)# ", }; +#endif /* HAVE_FABRICD */ static struct cmd_node interface_node = { .name = "interface", @@ -1236,12 +1242,14 @@ static struct cmd_node rmap_node = { .prompt = "%s(config-route-map)# ", }; +#ifdef HAVE_PBRD static struct cmd_node pbr_map_node = { .name = "pbr-map", .node = PBRMAP_NODE, .parent_node = CONFIG_NODE, .prompt = "%s(config-pbr-map)# ", }; +#endif /* HAVE_PBRD */ static struct cmd_node zebra_node = { .name = "zebra", @@ -1250,6 +1258,7 @@ static struct cmd_node zebra_node = { .prompt = "%s(config-router)# ", }; +#ifdef HAVE_BGPD static struct cmd_node bgp_vpnv4_node = { .name = "bgp vpnv4", .node = BGP_VPNV4_NODE, @@ -1334,6 +1343,7 @@ static struct cmd_node bgp_ipv6l_node = { .prompt = "%s(config-router-af)# ", }; +#ifdef ENABLE_BGP_VNC static struct cmd_node bgp_vnc_defaults_node = { .name = "bgp vnc defaults", .node = BGP_VNC_DEFAULTS_NODE, @@ -1361,6 +1371,7 @@ static struct cmd_node bgp_vnc_l2_group_node = { .parent_node = BGP_NODE, .prompt = "%s(config-router-vnc-l2-group)# ", }; +#endif /* ENABLE_BGP_VNC */ static struct cmd_node bmp_node = { .name = "bmp", @@ -1368,27 +1379,34 @@ static struct cmd_node bmp_node = { .parent_node = BGP_NODE, .prompt = "%s(config-bgp-bmp)# " }; +#endif /* HAVE_BGPD */ +#ifdef HAVE_OSPFD static struct cmd_node ospf_node = { .name = "ospf", .node = OSPF_NODE, .parent_node = CONFIG_NODE, .prompt = "%s(config-router)# ", }; +#endif /* HAVE_OSPFD */ +#ifdef HAVE_EIGRPD static struct cmd_node eigrp_node = { .name = "eigrp", .node = EIGRP_NODE, .parent_node = CONFIG_NODE, .prompt = "%s(config-router)# ", }; +#endif /* HAVE_EIGRPD */ +#ifdef HAVE_BABELD static struct cmd_node babel_node = { .name = "babel", .node = BABEL_NODE, .parent_node = CONFIG_NODE, .prompt = "%s(config-router)# ", }; +#endif /* HAVE_BABELD */ static struct cmd_node ripng_node = { .name = "ripng", @@ -1397,13 +1415,16 @@ static struct cmd_node ripng_node = { .prompt = "%s(config-router)# ", }; +#ifdef HAVE_OSPF6D static struct cmd_node ospf6_node = { .name = "ospf6", .node = OSPF6_NODE, .parent_node = CONFIG_NODE, .prompt = "%s(config-ospf6)# ", }; +#endif /* HAVE_OSPF6D */ +#ifdef HAVE_LDPD static struct cmd_node ldp_node = { .name = "ldp", .node = LDP_NODE, @@ -1452,6 +1473,7 @@ static struct cmd_node ldp_pseudowire_node = { .parent_node = LDP_L2VPN_NODE, .prompt = "%s(config-l2vpn-pw)# ", }; +#endif /* HAVE_LDPD */ static struct cmd_node keychain_node = { .name = "keychain", @@ -1474,12 +1496,14 @@ struct cmd_node link_params_node = { .prompt = "%s(config-link-params)# ", }; +#ifdef HAVE_BGPD static struct cmd_node rpki_node = { .name = "rpki", .node = RPKI_NODE, .parent_node = CONFIG_NODE, .prompt = "%s(config-rpki)# ", }; +#endif /* HAVE_BGPD */ #if HAVE_BFDD > 0 static struct cmd_node bfd_node = { @@ -1528,6 +1552,7 @@ DEFUNSH(VTYSH_REALLYALL, vtysh_end_all, vtysh_end_all_cmd, "end", return vtysh_end(); } +#ifdef HAVE_BGPD DEFUNSH(VTYSH_BGPD, router_bgp, router_bgp_cmd, "router bgp [(1-4294967295) [<view|vrf> WORD]]", ROUTER_STR BGP_STR AS_STR @@ -1748,6 +1773,7 @@ DEFUNSH(VTYSH_BGPD, vnc_l2_group, vnc_l2_group_cmd, "vnc l2-group NAME", return CMD_SUCCESS; } #endif +#endif /* HAVE_BGPD */ DEFUNSH(VTYSH_KEYS, key_chain, key_chain_cmd, "key chain WORD", "Authentication key management\n" @@ -1766,20 +1792,25 @@ DEFUNSH(VTYSH_KEYS, key, key_cmd, "key (0-2147483647)", return CMD_SUCCESS; } +#ifdef HAVE_RIPD DEFUNSH(VTYSH_RIPD, router_rip, router_rip_cmd, "router rip [vrf NAME]", ROUTER_STR "RIP\n" VRF_CMD_HELP_STR) { vty->node = RIP_NODE; return CMD_SUCCESS; } +#endif /* HAVE_RIPD */ +#ifdef HAVE_RIPNGD DEFUNSH(VTYSH_RIPNGD, router_ripng, router_ripng_cmd, "router ripng [vrf NAME]", ROUTER_STR "RIPng\n" VRF_CMD_HELP_STR) { vty->node = RIPNG_NODE; return CMD_SUCCESS; } +#endif /* HAVE_RIPNGD */ +#ifdef HAVE_OSPFD DEFUNSH(VTYSH_OSPFD, router_ospf, router_ospf_cmd, "router ospf [(1-65535)] [vrf NAME]", "Enable a routing process\n" @@ -1790,7 +1821,9 @@ DEFUNSH(VTYSH_OSPFD, router_ospf, router_ospf_cmd, vty->node = OSPF_NODE; return CMD_SUCCESS; } +#endif /* HAVE_OSPFD */ +#ifdef HAVE_EIGRPD DEFUNSH(VTYSH_EIGRPD, router_eigrp, router_eigrp_cmd, "router eigrp (1-65535) [vrf NAME]", "Enable a routing process\n" "Start EIGRP configuration\n" @@ -1800,7 +1833,9 @@ DEFUNSH(VTYSH_EIGRPD, router_eigrp, router_eigrp_cmd, "router eigrp (1-65535) [v vty->node = EIGRP_NODE; return CMD_SUCCESS; } +#endif /* HAVE_EIGRPD */ +#ifdef HAVE_BABELD DEFUNSH(VTYSH_BABELD, router_babel, router_babel_cmd, "router babel", "Enable a routing process\n" "Make Babel instance command\n") @@ -1808,13 +1843,16 @@ DEFUNSH(VTYSH_BABELD, router_babel, router_babel_cmd, "router babel", vty->node = BABEL_NODE; return CMD_SUCCESS; } +#endif /* HAVE_BABELD */ +#ifdef HAVE_OSPF6D DEFUNSH(VTYSH_OSPF6D, router_ospf6, router_ospf6_cmd, "router ospf6", ROUTER_STR OSPF6_STR) { vty->node = OSPF6_NODE; return CMD_SUCCESS; } +#endif #if defined(HAVE_LDPD) DEFUNSH(VTYSH_LDPD, ldp_mpls_ldp, ldp_mpls_ldp_cmd, "mpls ldp", @@ -1892,6 +1930,7 @@ DEFUNSH(VTYSH_LDPD, ldp_member_pseudowire_ifname, } #endif +#ifdef HAVE_ISISD DEFUNSH(VTYSH_ISISD, router_isis, router_isis_cmd, "router isis WORD [vrf NAME]", ROUTER_STR @@ -1901,7 +1940,9 @@ DEFUNSH(VTYSH_ISISD, router_isis, router_isis_cmd, vty->node = ISIS_NODE; return CMD_SUCCESS; } +#endif /* HAVE_ISISD */ +#ifdef HAVE_FABRICD DEFUNSH(VTYSH_FABRICD, router_openfabric, router_openfabric_cmd, "router openfabric WORD", ROUTER_STR "OpenFabric routing protocol\n" @@ -1910,6 +1951,7 @@ DEFUNSH(VTYSH_FABRICD, router_openfabric, router_openfabric_cmd, "router openfab vty->node = OPENFABRIC_NODE; return CMD_SUCCESS; } +#endif /* HAVE_FABRICD */ DEFUNSH(VTYSH_RMAP, vtysh_route_map, vtysh_route_map_cmd, "route-map WORD <deny|permit> (1-65535)", @@ -1923,6 +1965,7 @@ DEFUNSH(VTYSH_RMAP, vtysh_route_map, vtysh_route_map_cmd, return CMD_SUCCESS; } +#ifdef HAVE_PBRD DEFUNSH(VTYSH_PBRD, vtysh_pbr_map, vtysh_pbr_map_cmd, "pbr-map PBRMAP seq (1-700)", "Create pbr-map or enter pbr-map command mode\n" @@ -1934,6 +1977,14 @@ DEFUNSH(VTYSH_PBRD, vtysh_pbr_map, vtysh_pbr_map_cmd, return CMD_SUCCESS; } +DEFSH(VTYSH_PBRD, vtysh_no_pbr_map_cmd, "no pbr-map PBRMAP [seq (1-700)]", + NO_STR + "Delete pbr-map\n" + "The name of the PBR MAP\n" + "Sequence to delete from existing pbr-map entry\n" + "Sequence number\n") +#endif /* HAVE_PBRD */ + #if HAVE_BFDD > 0 DEFUNSH(VTYSH_BFDD, bfd_enter, bfd_enter_cmd, "bfd", "Configure BFD peers\n") { @@ -1969,13 +2020,6 @@ DEFUNSH(VTYSH_BFDD, bfd_profile_enter, bfd_profile_enter_cmd, } #endif /* HAVE_BFDD */ -DEFSH(VTYSH_PBRD, vtysh_no_pbr_map_cmd, "no pbr-map PBRMAP [seq (1-700)]", - NO_STR - "Delete pbr-map\n" - "The name of the PBR MAP\n" - "Sequence to delete from existing pbr-map entry\n" - "Sequence number\n") - DEFUNSH(VTYSH_ALL, vtysh_line_vty, vtysh_line_vty_cmd, "line vty", "Configure a terminal line\n" "Virtual terminal\n") @@ -2039,6 +2083,7 @@ DEFUNSH(VTYSH_ALL, vtysh_quit_all, vtysh_quit_all_cmd, "quit", return vtysh_exit_all(self, vty, argc, argv); } +#ifdef HAVE_BGPD DEFUNSH(VTYSH_BGPD, exit_address_family, exit_address_family_cmd, "exit-address-family", "Exit from Address Family configuration mode\n") { @@ -2097,14 +2142,6 @@ DEFUNSH(VTYSH_BGPD, bmp_quit, bmp_quit_cmd, "quit", return bmp_exit(self, vty, argc, argv); } -DEFUNSH(VTYSH_VRF, exit_vrf_config, exit_vrf_config_cmd, "exit-vrf", - "Exit from VRF configuration mode\n") -{ - if (vty->node == VRF_NODE) - vty->node = CONFIG_NODE; - return CMD_SUCCESS; -} - DEFUNSH(VTYSH_BGPD, exit_vrf_policy, exit_vrf_policy_cmd, "exit-vrf-policy", "Exit from VRF policy configuration mode\n") { @@ -2112,7 +2149,17 @@ DEFUNSH(VTYSH_BGPD, exit_vrf_policy, exit_vrf_policy_cmd, "exit-vrf-policy", vty->node = BGP_NODE; return CMD_SUCCESS; } +#endif /* HAVE_BGPD */ +DEFUNSH(VTYSH_VRF, exit_vrf_config, exit_vrf_config_cmd, "exit-vrf", + "Exit from VRF configuration mode\n") +{ + if (vty->node == VRF_NODE) + vty->node = CONFIG_NODE; + return CMD_SUCCESS; +} + +#ifdef HAVE_RIPD DEFUNSH(VTYSH_RIPD, vtysh_exit_ripd, vtysh_exit_ripd_cmd, "exit", "Exit current mode and down to previous mode\n") { @@ -2124,7 +2171,9 @@ DEFUNSH(VTYSH_RIPD, vtysh_quit_ripd, vtysh_quit_ripd_cmd, "quit", { return vtysh_exit_ripd(self, vty, argc, argv); } +#endif /* HAVE_RIPD */ +#ifdef HAVE_RIPNGD DEFUNSH(VTYSH_RIPNGD, vtysh_exit_ripngd, vtysh_exit_ripngd_cmd, "exit", "Exit current mode and down to previous mode\n") { @@ -2136,6 +2185,7 @@ DEFUNSH(VTYSH_RIPNGD, vtysh_quit_ripngd, vtysh_quit_ripngd_cmd, "quit", { return vtysh_exit_ripngd(self, vty, argc, argv); } +#endif /* HAVE_RIPNGD */ DEFUNSH(VTYSH_RMAP, vtysh_exit_rmap, vtysh_exit_rmap_cmd, "exit", "Exit current mode and down to previous mode\n") @@ -2149,6 +2199,7 @@ DEFUNSH(VTYSH_RMAP, vtysh_quit_rmap, vtysh_quit_rmap_cmd, "quit", return vtysh_exit_rmap(self, vty, argc, argv); } +#ifdef HAVE_PBRD DEFUNSH(VTYSH_PBRD, vtysh_exit_pbr_map, vtysh_exit_pbr_map_cmd, "exit", "Exit current mode and down to previous mode\n") { @@ -2160,7 +2211,9 @@ DEFUNSH(VTYSH_PBRD, vtysh_quit_pbr_map, vtysh_quit_pbr_map_cmd, "quit", { return vtysh_exit_rmap(self, vty, argc, argv); } +#endif /* HAVE_PBRD */ +#ifdef HAVE_BGPD DEFUNSH(VTYSH_BGPD, vtysh_exit_bgpd, vtysh_exit_bgpd_cmd, "exit", "Exit current mode and down to previous mode\n") { @@ -2172,7 +2225,9 @@ DEFUNSH(VTYSH_BGPD, vtysh_quit_bgpd, vtysh_quit_bgpd_cmd, "quit", { return vtysh_exit_bgpd(self, vty, argc, argv); } +#endif /* HAVE_BGPD */ +#ifdef HAVE_OSPFD DEFUNSH(VTYSH_OSPFD, vtysh_exit_ospfd, vtysh_exit_ospfd_cmd, "exit", "Exit current mode and down to previous mode\n") { @@ -2184,7 +2239,9 @@ DEFUNSH(VTYSH_OSPFD, vtysh_quit_ospfd, vtysh_quit_ospfd_cmd, "quit", { return vtysh_exit_ospfd(self, vty, argc, argv); } +#endif /* HAVE_OSPFD */ +#ifdef HAVE_EIGRPD DEFUNSH(VTYSH_EIGRPD, vtysh_exit_eigrpd, vtysh_exit_eigrpd_cmd, "exit", "Exit current mode and down to previous mode\n") { @@ -2196,8 +2253,10 @@ DEFUNSH(VTYSH_EIGRPD, vtysh_quit_eigrpd, vtysh_quit_eigrpd_cmd, "quit", { return vtysh_exit(vty); } +#endif /* HAVE_EIGRPD */ -DEFUNSH(VTYSH_EIGRPD, vtysh_exit_babeld, vtysh_exit_babeld_cmd, "exit", +#ifdef HAVE_BABELD +DEFUNSH(VTYSH_BABELD, vtysh_exit_babeld, vtysh_exit_babeld_cmd, "exit", "Exit current mode and down to previous mode\n") { return vtysh_exit(vty); @@ -2208,7 +2267,9 @@ DEFUNSH(VTYSH_BABELD, vtysh_quit_babeld, vtysh_quit_babeld_cmd, "quit", { return vtysh_exit(vty); } +#endif /* HAVE_BABELD */ +#ifdef HAVE_OSPF6D DEFUNSH(VTYSH_OSPF6D, vtysh_exit_ospf6d, vtysh_exit_ospf6d_cmd, "exit", "Exit current mode and down to previous mode\n") { @@ -2220,6 +2281,7 @@ DEFUNSH(VTYSH_OSPF6D, vtysh_quit_ospf6d, vtysh_quit_ospf6d_cmd, "quit", { return vtysh_exit_ospf6d(self, vty, argc, argv); } +#endif /* HAVE_OSPF6D */ #if defined(HAVE_LDPD) DEFUNSH(VTYSH_LDPD, vtysh_exit_ldpd, vtysh_exit_ldpd_cmd, "exit", @@ -2232,6 +2294,7 @@ ALIAS(vtysh_exit_ldpd, vtysh_quit_ldpd_cmd, "quit", "Exit current mode and down to previous mode\n") #endif +#ifdef HAVE_ISISD DEFUNSH(VTYSH_ISISD, vtysh_exit_isisd, vtysh_exit_isisd_cmd, "exit", "Exit current mode and down to previous mode\n") { @@ -2243,6 +2306,7 @@ DEFUNSH(VTYSH_ISISD, vtysh_quit_isisd, vtysh_quit_isisd_cmd, "quit", { return vtysh_exit_isisd(self, vty, argc, argv); } +#endif /* HAVE_ISISD */ #if HAVE_BFDD > 0 DEFUNSH(VTYSH_BFDD, vtysh_exit_bfdd, vtysh_exit_bfdd_cmd, "exit", @@ -2255,6 +2319,7 @@ ALIAS(vtysh_exit_bfdd, vtysh_quit_bfdd_cmd, "quit", "Exit current mode and down to previous mode\n") #endif +#ifdef HAVE_FABRICD DEFUNSH(VTYSH_FABRICD, vtysh_exit_fabricd, vtysh_exit_fabricd_cmd, "exit", "Exit current mode and down to previous mode\n") { @@ -2266,6 +2331,19 @@ DEFUNSH(VTYSH_FABRICD, vtysh_quit_fabricd, vtysh_quit_fabricd_cmd, "quit", { return vtysh_exit_fabricd(self, vty, argc, argv); } +#endif /* HAVE_FABRICD */ + +DEFUNSH(VTYSH_KEYS, vtysh_exit_keys, vtysh_exit_keys_cmd, "exit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit(vty); +} + +DEFUNSH(VTYSH_KEYS, vtysh_quit_keys, vtysh_quit_keys_cmd, "quit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit_keys(self, vty, argc, argv); +} DEFUNSH(VTYSH_ALL, vtysh_exit_line_vty, vtysh_exit_line_vty_cmd, "exit", "Exit current mode and down to previous mode\n") @@ -3771,331 +3849,394 @@ void vtysh_init_vty(void) cmd_init(0); cmd_variable_handler_register(vtysh_var_handler); - /* Install nodes. */ + /* bgpd */ +#ifdef HAVE_BGPD install_node(&bgp_node); - install_node(&rip_node); - install_node(&interface_node); - install_node(&pw_node); - install_node(&link_params_node); - install_node(&vrf_node); - install_node(&nh_group_node); - install_node(&rmap_node); - install_node(&pbr_map_node); - install_node(&zebra_node); + install_element(CONFIG_NODE, &router_bgp_cmd); + install_element(BGP_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_NODE, &vtysh_end_all_cmd); + install_node(&bgp_vpnv4_node); + install_element(BGP_NODE, &address_family_ipv4_vpn_cmd); +#ifdef KEEP_OLD_VPN_COMMANDS + install_element(BGP_NODE, &address_family_vpnv4_cmd); +#endif /* KEEP_OLD_VPN_COMMANDS */ + install_element(BGP_VPNV4_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_VPNV4_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_VPNV4_NODE, &vtysh_end_all_cmd); + install_element(BGP_VPNV4_NODE, &exit_address_family_cmd); + install_node(&bgp_vpnv6_node); + install_element(BGP_NODE, &address_family_ipv6_vpn_cmd); +#ifdef KEEP_OLD_VPN_COMMANDS + install_element(BGP_NODE, &address_family_vpnv6_cmd); +#endif /* KEEP_OLD_VPN_COMMANDS */ + install_element(BGP_VPNV6_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_VPNV6_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_VPNV6_NODE, &vtysh_end_all_cmd); + install_element(BGP_VPNV6_NODE, &exit_address_family_cmd); + install_node(&bgp_flowspecv4_node); + install_element(BGP_NODE, &address_family_flowspecv4_cmd); + install_element(BGP_FLOWSPECV4_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_FLOWSPECV4_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_FLOWSPECV4_NODE, &vtysh_end_all_cmd); + install_element(BGP_FLOWSPECV4_NODE, &exit_address_family_cmd); + install_node(&bgp_flowspecv6_node); + install_element(BGP_NODE, &address_family_flowspecv6_cmd); + install_element(BGP_FLOWSPECV6_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_FLOWSPECV6_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_FLOWSPECV6_NODE, &vtysh_end_all_cmd); + install_element(BGP_FLOWSPECV6_NODE, &exit_address_family_cmd); + install_node(&bgp_ipv4_node); + install_element(BGP_NODE, &address_family_ipv4_cmd); + install_element(BGP_IPV4_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_IPV4_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_IPV4_NODE, &vtysh_end_all_cmd); + install_element(BGP_IPV4_NODE, &exit_address_family_cmd); + install_node(&bgp_ipv4m_node); + install_element(BGP_NODE, &address_family_ipv4_multicast_cmd); + install_element(BGP_IPV4M_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_IPV4M_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_IPV4M_NODE, &vtysh_end_all_cmd); + install_element(BGP_IPV4M_NODE, &exit_address_family_cmd); + install_node(&bgp_ipv4l_node); + install_element(BGP_NODE, &address_family_ipv4_labeled_unicast_cmd); + install_element(BGP_IPV4L_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_IPV4L_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_IPV4L_NODE, &vtysh_end_all_cmd); + install_element(BGP_IPV4L_NODE, &exit_address_family_cmd); + install_node(&bgp_ipv6_node); + install_element(BGP_NODE, &address_family_ipv6_cmd); + install_element(BGP_IPV6_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_IPV6_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_IPV6_NODE, &vtysh_end_all_cmd); + install_element(BGP_IPV6_NODE, &exit_address_family_cmd); + install_node(&bgp_ipv6m_node); + install_element(BGP_NODE, &address_family_ipv6_multicast_cmd); + install_element(BGP_IPV6M_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_IPV6M_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_IPV6M_NODE, &vtysh_end_all_cmd); + install_element(BGP_IPV6M_NODE, &exit_address_family_cmd); + install_node(&bgp_ipv6l_node); + install_element(BGP_NODE, &address_family_ipv6_labeled_unicast_cmd); + install_element(BGP_IPV6L_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_IPV6L_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_IPV6L_NODE, &vtysh_end_all_cmd); + install_element(BGP_IPV6L_NODE, &exit_address_family_cmd); + +#if defined(ENABLE_BGP_VNC) install_node(&bgp_vrf_policy_node); - install_node(&bgp_evpn_node); - install_node(&bgp_evpn_vni_node); + install_element(BGP_NODE, &vnc_vrf_policy_cmd); + install_element(BGP_VRF_POLICY_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_VRF_POLICY_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_VRF_POLICY_NODE, &vtysh_end_all_cmd); + install_element(BGP_VRF_POLICY_NODE, &exit_vrf_policy_cmd); + install_node(&bgp_vnc_defaults_node); + install_element(BGP_NODE, &vnc_defaults_cmd); + install_element(BGP_VNC_DEFAULTS_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_VNC_DEFAULTS_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_VNC_DEFAULTS_NODE, &vtysh_end_all_cmd); + install_element(BGP_VNC_DEFAULTS_NODE, &exit_vnc_config_cmd); + install_node(&bgp_vnc_nve_group_node); + install_element(BGP_NODE, &vnc_nve_group_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, &vtysh_end_all_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, &exit_vnc_config_cmd); + install_node(&bgp_vnc_l2_group_node); - install_node(&ospf_node); - install_node(&eigrp_node); - install_node(&babel_node); - install_node(&ripng_node); - install_node(&ospf6_node); - install_node(&ldp_node); - install_node(&ldp_ipv4_node); - install_node(&ldp_ipv6_node); - install_node(&ldp_ipv4_iface_node); - install_node(&ldp_ipv6_iface_node); - install_node(&ldp_l2vpn_node); - install_node(&ldp_pseudowire_node); - install_node(&keychain_node); - install_node(&keychain_key_node); - install_node(&isis_node); - install_node(&openfabric_node); - install_node(&vty_node); - install_node(&rpki_node); - install_node(&bmp_node); -#if HAVE_BFDD > 0 - install_node(&bfd_node); - install_node(&bfd_peer_node); - install_node(&bfd_profile_node); -#endif /* HAVE_BFDD */ + install_element(BGP_NODE, &vnc_l2_group_cmd); + install_element(BGP_VNC_L2_GROUP_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_VNC_L2_GROUP_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_VNC_L2_GROUP_NODE, &vtysh_end_all_cmd); + install_element(BGP_VNC_L2_GROUP_NODE, &exit_vnc_config_cmd); +#endif - struct cmd_node *node; - for (unsigned int i = 0; i < vector_active(cmdvec); i++) { - node = vector_slot(cmdvec, i); - if (!node || node->node == VIEW_NODE) - continue; - vtysh_install_default(node->node); - } + install_node(&bgp_evpn_node); + install_element(BGP_NODE, &address_family_evpn_cmd); +#if defined(HAVE_CUMULUS) + install_element(BGP_NODE, &address_family_evpn2_cmd); +#endif + install_element(BGP_EVPN_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_EVPN_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_EVPN_NODE, &vtysh_end_all_cmd); + install_element(BGP_EVPN_NODE, &exit_address_family_cmd); - install_element(VIEW_NODE, &vtysh_enable_cmd); - install_element(ENABLE_NODE, &vtysh_config_terminal_cmd); - install_element(ENABLE_NODE, &vtysh_disable_cmd); + install_node(&bgp_evpn_vni_node); + install_element(BGP_EVPN_NODE, &bgp_evpn_vni_cmd); + install_element(BGP_EVPN_VNI_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_EVPN_VNI_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_EVPN_VNI_NODE, &vtysh_end_all_cmd); + install_element(BGP_EVPN_VNI_NODE, &exit_vni_cmd); - /* "exit" command. */ - install_element(VIEW_NODE, &vtysh_exit_all_cmd); - install_element(CONFIG_NODE, &vtysh_exit_all_cmd); - install_element(VIEW_NODE, &vtysh_quit_all_cmd); - install_element(CONFIG_NODE, &vtysh_quit_all_cmd); + install_node(&rpki_node); + install_element(CONFIG_NODE, &rpki_cmd); + install_element(RPKI_NODE, &rpki_exit_cmd); + install_element(RPKI_NODE, &rpki_quit_cmd); + install_element(RPKI_NODE, &vtysh_end_all_cmd); + + install_node(&bmp_node); + install_element(BGP_NODE, &bmp_targets_cmd); + install_element(BMP_NODE, &bmp_exit_cmd); + install_element(BMP_NODE, &bmp_quit_cmd); + install_element(BMP_NODE, &vtysh_end_all_cmd); +#endif /* HAVE_BGPD */ + + /* ripd */ + install_node(&rip_node); +#ifdef HAVE_RIPD + install_element(CONFIG_NODE, &router_rip_cmd); install_element(RIP_NODE, &vtysh_exit_ripd_cmd); install_element(RIP_NODE, &vtysh_quit_ripd_cmd); + install_element(RIP_NODE, &vtysh_end_all_cmd); +#endif /* HAVE_RIPD */ + + /* ripngd */ + install_node(&ripng_node); +#ifdef HAVE_RIPNGD + install_element(CONFIG_NODE, &router_ripng_cmd); install_element(RIPNG_NODE, &vtysh_exit_ripngd_cmd); install_element(RIPNG_NODE, &vtysh_quit_ripngd_cmd); + install_element(RIPNG_NODE, &vtysh_end_all_cmd); +#endif /* HAVE_RIPNGD */ + + /* ospfd */ +#ifdef HAVE_OSPFD + install_node(&ospf_node); + install_element(CONFIG_NODE, &router_ospf_cmd); install_element(OSPF_NODE, &vtysh_exit_ospfd_cmd); install_element(OSPF_NODE, &vtysh_quit_ospfd_cmd); - install_element(EIGRP_NODE, &vtysh_exit_eigrpd_cmd); - install_element(EIGRP_NODE, &vtysh_quit_eigrpd_cmd); - install_element(BABEL_NODE, &vtysh_exit_babeld_cmd); - install_element(BABEL_NODE, &vtysh_quit_babeld_cmd); + install_element(OSPF_NODE, &vtysh_end_all_cmd); +#endif /* HAVE_OSPFD */ + + /* ospf6d */ +#ifdef HAVE_OSPF6D + install_node(&ospf6_node); + install_element(CONFIG_NODE, &router_ospf6_cmd); install_element(OSPF6_NODE, &vtysh_exit_ospf6d_cmd); install_element(OSPF6_NODE, &vtysh_quit_ospf6d_cmd); + install_element(OSPF6_NODE, &vtysh_end_all_cmd); +#endif /* HAVE_OSPF6D */ + + /* ldpd */ #if defined(HAVE_LDPD) + install_node(&ldp_node); + install_element(CONFIG_NODE, &ldp_mpls_ldp_cmd); install_element(LDP_NODE, &vtysh_exit_ldpd_cmd); install_element(LDP_NODE, &vtysh_quit_ldpd_cmd); + install_element(LDP_NODE, &vtysh_end_all_cmd); + + install_node(&ldp_ipv4_node); + install_element(LDP_NODE, &ldp_address_family_ipv4_cmd); install_element(LDP_IPV4_NODE, &vtysh_exit_ldpd_cmd); install_element(LDP_IPV4_NODE, &vtysh_quit_ldpd_cmd); install_element(LDP_IPV4_NODE, &ldp_exit_address_family_cmd); + install_element(LDP_IPV4_NODE, &vtysh_end_all_cmd); + + install_node(&ldp_ipv6_node); + install_element(LDP_NODE, &ldp_address_family_ipv6_cmd); install_element(LDP_IPV6_NODE, &vtysh_exit_ldpd_cmd); install_element(LDP_IPV6_NODE, &vtysh_quit_ldpd_cmd); install_element(LDP_IPV6_NODE, &ldp_exit_address_family_cmd); + install_element(LDP_IPV6_NODE, &vtysh_end_all_cmd); + + install_node(&ldp_ipv4_iface_node); + install_element(LDP_IPV4_NODE, &ldp_interface_ifname_cmd); install_element(LDP_IPV4_IFACE_NODE, &vtysh_exit_ldpd_cmd); install_element(LDP_IPV4_IFACE_NODE, &vtysh_quit_ldpd_cmd); + install_element(LDP_IPV4_IFACE_NODE, &vtysh_end_all_cmd); + + install_node(&ldp_ipv6_iface_node); + install_element(LDP_IPV6_NODE, &ldp_interface_ifname_cmd); install_element(LDP_IPV6_IFACE_NODE, &vtysh_exit_ldpd_cmd); install_element(LDP_IPV6_IFACE_NODE, &vtysh_quit_ldpd_cmd); + install_element(LDP_IPV6_IFACE_NODE, &vtysh_end_all_cmd); + + install_node(&ldp_l2vpn_node); + install_element(CONFIG_NODE, &ldp_l2vpn_word_type_vpls_cmd); install_element(LDP_L2VPN_NODE, &vtysh_exit_ldpd_cmd); install_element(LDP_L2VPN_NODE, &vtysh_quit_ldpd_cmd); + install_element(LDP_L2VPN_NODE, &vtysh_end_all_cmd); + + install_node(&ldp_pseudowire_node); + install_element(LDP_L2VPN_NODE, &ldp_member_pseudowire_ifname_cmd); install_element(LDP_PSEUDOWIRE_NODE, &vtysh_exit_ldpd_cmd); install_element(LDP_PSEUDOWIRE_NODE, &vtysh_quit_ldpd_cmd); + install_element(LDP_PSEUDOWIRE_NODE, &vtysh_end_all_cmd); #endif - install_element(BGP_NODE, &vtysh_exit_bgpd_cmd); - install_element(BGP_NODE, &vtysh_quit_bgpd_cmd); - install_element(BGP_VPNV4_NODE, &vtysh_exit_bgpd_cmd); - install_element(BGP_VPNV4_NODE, &vtysh_quit_bgpd_cmd); - install_element(BGP_VPNV6_NODE, &vtysh_exit_bgpd_cmd); - install_element(BGP_VPNV6_NODE, &vtysh_quit_bgpd_cmd); - install_element(BGP_FLOWSPECV4_NODE, &vtysh_exit_bgpd_cmd); - install_element(BGP_FLOWSPECV4_NODE, &vtysh_quit_bgpd_cmd); - install_element(BGP_FLOWSPECV6_NODE, &vtysh_exit_bgpd_cmd); - install_element(BGP_FLOWSPECV6_NODE, &vtysh_quit_bgpd_cmd); - install_element(BGP_IPV4_NODE, &vtysh_exit_bgpd_cmd); - install_element(BGP_IPV4_NODE, &vtysh_quit_bgpd_cmd); - install_element(BGP_IPV4M_NODE, &vtysh_exit_bgpd_cmd); - install_element(BGP_IPV4M_NODE, &vtysh_quit_bgpd_cmd); - install_element(BGP_IPV4L_NODE, &vtysh_exit_bgpd_cmd); - install_element(BGP_IPV4L_NODE, &vtysh_quit_bgpd_cmd); - install_element(BGP_IPV6_NODE, &vtysh_exit_bgpd_cmd); - install_element(BGP_IPV6_NODE, &vtysh_quit_bgpd_cmd); - install_element(BGP_IPV6M_NODE, &vtysh_exit_bgpd_cmd); - install_element(BGP_IPV6M_NODE, &vtysh_quit_bgpd_cmd); - install_element(BGP_EVPN_NODE, &vtysh_quit_bgpd_cmd); - install_element(BGP_EVPN_NODE, &vtysh_exit_bgpd_cmd); - install_element(BGP_EVPN_VNI_NODE, &vtysh_exit_bgpd_cmd); - install_element(BGP_EVPN_VNI_NODE, &vtysh_quit_bgpd_cmd); - install_element(BGP_IPV6L_NODE, &vtysh_exit_bgpd_cmd); - install_element(BGP_IPV6L_NODE, &vtysh_quit_bgpd_cmd); -#if defined(ENABLE_BGP_VNC) - install_element(BGP_VRF_POLICY_NODE, &vtysh_exit_bgpd_cmd); - install_element(BGP_VRF_POLICY_NODE, &vtysh_quit_bgpd_cmd); - install_element(BGP_VNC_DEFAULTS_NODE, &vtysh_exit_bgpd_cmd); - install_element(BGP_VNC_DEFAULTS_NODE, &vtysh_quit_bgpd_cmd); - install_element(BGP_VNC_NVE_GROUP_NODE, &vtysh_exit_bgpd_cmd); - install_element(BGP_VNC_NVE_GROUP_NODE, &vtysh_quit_bgpd_cmd); - install_element(BGP_VNC_L2_GROUP_NODE, &vtysh_exit_bgpd_cmd); - install_element(BGP_VNC_L2_GROUP_NODE, &vtysh_quit_bgpd_cmd); -#endif + + /* eigrpd */ +#ifdef HAVE_EIGRPD + install_node(&eigrp_node); + install_element(CONFIG_NODE, &router_eigrp_cmd); + install_element(EIGRP_NODE, &vtysh_exit_eigrpd_cmd); + install_element(EIGRP_NODE, &vtysh_quit_eigrpd_cmd); + install_element(EIGRP_NODE, &vtysh_end_all_cmd); +#endif /* HAVE_EIGRPD */ + + /* babeld */ +#ifdef HAVE_BABELD + install_node(&babel_node); + install_element(CONFIG_NODE, &router_babel_cmd); + install_element(BABEL_NODE, &vtysh_exit_babeld_cmd); + install_element(BABEL_NODE, &vtysh_quit_babeld_cmd); + install_element(BABEL_NODE, &vtysh_end_all_cmd); +#endif /* HAVE_BABELD */ + + /* isisd */ +#ifdef HAVE_ISISD + install_node(&isis_node); + install_element(CONFIG_NODE, &router_isis_cmd); install_element(ISIS_NODE, &vtysh_exit_isisd_cmd); install_element(ISIS_NODE, &vtysh_quit_isisd_cmd); + install_element(ISIS_NODE, &vtysh_end_all_cmd); +#endif /* HAVE_ISISD */ + + /* fabricd */ +#ifdef HAVE_FABRICD + install_node(&openfabric_node); + install_element(CONFIG_NODE, &router_openfabric_cmd); install_element(OPENFABRIC_NODE, &vtysh_exit_fabricd_cmd); install_element(OPENFABRIC_NODE, &vtysh_quit_fabricd_cmd); - install_element(KEYCHAIN_NODE, &vtysh_exit_ripd_cmd); - install_element(KEYCHAIN_NODE, &vtysh_quit_ripd_cmd); - install_element(KEYCHAIN_KEY_NODE, &vtysh_exit_ripd_cmd); - install_element(KEYCHAIN_KEY_NODE, &vtysh_quit_ripd_cmd); - install_element(RMAP_NODE, &vtysh_exit_rmap_cmd); - install_element(RMAP_NODE, &vtysh_quit_rmap_cmd); + install_element(OPENFABRIC_NODE, &vtysh_end_all_cmd); +#endif /* HAVE_FABRICD */ + + /* pbrd */ +#ifdef HAVE_PBRD + install_node(&pbr_map_node); + install_element(CONFIG_NODE, &vtysh_pbr_map_cmd); + install_element(CONFIG_NODE, &vtysh_no_pbr_map_cmd); install_element(PBRMAP_NODE, &vtysh_exit_pbr_map_cmd); install_element(PBRMAP_NODE, &vtysh_quit_pbr_map_cmd); + install_element(PBRMAP_NODE, &vtysh_end_all_cmd); +#endif /* HAVE_PBRD */ + + /* bfdd */ #if HAVE_BFDD > 0 - /* Enter node. */ + install_node(&bfd_node); install_element(CONFIG_NODE, &bfd_enter_cmd); - install_element(BFD_NODE, &bfd_peer_enter_cmd); - install_element(BFD_NODE, &bfd_profile_enter_cmd); - - /* Exit/quit node. */ install_element(BFD_NODE, &vtysh_exit_bfdd_cmd); install_element(BFD_NODE, &vtysh_quit_bfdd_cmd); + install_element(BFD_NODE, &vtysh_end_all_cmd); + + install_node(&bfd_peer_node); + install_element(BFD_NODE, &bfd_peer_enter_cmd); install_element(BFD_PEER_NODE, &vtysh_exit_bfdd_cmd); install_element(BFD_PEER_NODE, &vtysh_quit_bfdd_cmd); + install_element(BFD_PEER_NODE, &vtysh_end_all_cmd); + + install_node(&bfd_profile_node); + install_element(BFD_NODE, &bfd_profile_enter_cmd); install_element(BFD_PROFILE_NODE, &vtysh_exit_bfdd_cmd); install_element(BFD_PROFILE_NODE, &vtysh_quit_bfdd_cmd); - - /* End/exit all. */ - install_element(BFD_NODE, &vtysh_end_all_cmd); - install_element(BFD_PEER_NODE, &vtysh_end_all_cmd); install_element(BFD_PROFILE_NODE, &vtysh_end_all_cmd); #endif /* HAVE_BFDD */ - install_element(VTY_NODE, &vtysh_exit_line_vty_cmd); - install_element(VTY_NODE, &vtysh_quit_line_vty_cmd); - /* "end" command. */ - install_element(CONFIG_NODE, &vtysh_end_all_cmd); - install_element(ENABLE_NODE, &vtysh_end_all_cmd); - install_element(RIP_NODE, &vtysh_end_all_cmd); - install_element(RIPNG_NODE, &vtysh_end_all_cmd); - install_element(OSPF_NODE, &vtysh_end_all_cmd); - install_element(EIGRP_NODE, &vtysh_end_all_cmd); - install_element(BABEL_NODE, &vtysh_end_all_cmd); - install_element(OSPF6_NODE, &vtysh_end_all_cmd); - install_element(LDP_NODE, &vtysh_end_all_cmd); - install_element(LDP_IPV4_NODE, &vtysh_end_all_cmd); - install_element(LDP_IPV6_NODE, &vtysh_end_all_cmd); - install_element(LDP_IPV4_IFACE_NODE, &vtysh_end_all_cmd); - install_element(LDP_IPV6_IFACE_NODE, &vtysh_end_all_cmd); - install_element(LDP_L2VPN_NODE, &vtysh_end_all_cmd); - install_element(LDP_PSEUDOWIRE_NODE, &vtysh_end_all_cmd); - install_element(BGP_NODE, &vtysh_end_all_cmd); - install_element(BGP_IPV4_NODE, &vtysh_end_all_cmd); - install_element(BGP_IPV4M_NODE, &vtysh_end_all_cmd); - install_element(BGP_IPV4L_NODE, &vtysh_end_all_cmd); - install_element(BGP_VPNV4_NODE, &vtysh_end_all_cmd); - install_element(BGP_VPNV6_NODE, &vtysh_end_all_cmd); - install_element(BGP_FLOWSPECV4_NODE, &vtysh_end_all_cmd); - install_element(BGP_FLOWSPECV6_NODE, &vtysh_end_all_cmd); - install_element(BGP_IPV6_NODE, &vtysh_end_all_cmd); - install_element(BGP_IPV6M_NODE, &vtysh_end_all_cmd); - install_element(BGP_IPV6L_NODE, &vtysh_end_all_cmd); - install_element(BGP_VRF_POLICY_NODE, &vtysh_end_all_cmd); - install_element(BGP_EVPN_NODE, &vtysh_end_all_cmd); - install_element(BGP_EVPN_VNI_NODE, &vtysh_end_all_cmd); - install_element(BGP_VNC_DEFAULTS_NODE, &vtysh_end_all_cmd); - install_element(BGP_VNC_NVE_GROUP_NODE, &vtysh_end_all_cmd); - install_element(BGP_VNC_L2_GROUP_NODE, &vtysh_end_all_cmd); - install_element(ISIS_NODE, &vtysh_end_all_cmd); - install_element(OPENFABRIC_NODE, &vtysh_end_all_cmd); + /* keychain */ + install_node(&keychain_node); + install_element(CONFIG_NODE, &key_chain_cmd); + install_element(KEYCHAIN_NODE, &key_chain_cmd); + install_element(KEYCHAIN_NODE, &vtysh_exit_keys_cmd); + install_element(KEYCHAIN_NODE, &vtysh_quit_keys_cmd); install_element(KEYCHAIN_NODE, &vtysh_end_all_cmd); + + install_node(&keychain_key_node); + install_element(KEYCHAIN_NODE, &key_cmd); + install_element(KEYCHAIN_KEY_NODE, &key_chain_cmd); + install_element(KEYCHAIN_KEY_NODE, &vtysh_exit_keys_cmd); + install_element(KEYCHAIN_KEY_NODE, &vtysh_quit_keys_cmd); install_element(KEYCHAIN_KEY_NODE, &vtysh_end_all_cmd); - install_element(RMAP_NODE, &vtysh_end_all_cmd); - install_element(PBRMAP_NODE, &vtysh_end_all_cmd); - install_element(VTY_NODE, &vtysh_end_all_cmd); + /* nexthop-group */ + install_node(&nh_group_node); + install_element(CONFIG_NODE, &vtysh_nexthop_group_cmd); + install_element(CONFIG_NODE, &vtysh_no_nexthop_group_cmd); + install_element(NH_GROUP_NODE, &vtysh_end_all_cmd); + install_element(NH_GROUP_NODE, &vtysh_exit_nexthop_group_cmd); + install_element(NH_GROUP_NODE, &vtysh_quit_nexthop_group_cmd); + + /* zebra and all */ + install_node(&zebra_node); + + install_node(&interface_node); + install_element(CONFIG_NODE, &vtysh_interface_cmd); install_element(INTERFACE_NODE, &vtysh_end_all_cmd); install_element(INTERFACE_NODE, &vtysh_exit_interface_cmd); + install_element(INTERFACE_NODE, &vtysh_quit_interface_cmd); + + install_node(&link_params_node); + install_element(INTERFACE_NODE, &vtysh_link_params_cmd); install_element(LINK_PARAMS_NODE, &exit_link_params_cmd); install_element(LINK_PARAMS_NODE, &vtysh_end_all_cmd); install_element(LINK_PARAMS_NODE, &vtysh_exit_interface_cmd); - install_element(INTERFACE_NODE, &vtysh_quit_interface_cmd); + install_node(&pw_node); + install_element(CONFIG_NODE, &vtysh_pseudowire_cmd); install_element(PW_NODE, &vtysh_end_all_cmd); install_element(PW_NODE, &vtysh_exit_interface_cmd); install_element(PW_NODE, &vtysh_quit_interface_cmd); - install_element(CONFIG_NODE, &vtysh_nexthop_group_cmd); - install_element(NH_GROUP_NODE, &vtysh_end_all_cmd); - install_element(NH_GROUP_NODE, &vtysh_exit_nexthop_group_cmd); - install_element(NH_GROUP_NODE, &vtysh_quit_nexthop_group_cmd); - + install_node(&vrf_node); + install_element(CONFIG_NODE, &vtysh_vrf_cmd); + install_element(VRF_NODE, &vtysh_vrf_netns_cmd); + install_element(VRF_NODE, &vtysh_no_vrf_netns_cmd); + install_element(VRF_NODE, &exit_vrf_config_cmd); install_element(VRF_NODE, &vtysh_end_all_cmd); install_element(VRF_NODE, &vtysh_exit_vrf_cmd); install_element(VRF_NODE, &vtysh_quit_vrf_cmd); + + install_node(&rmap_node); + install_element(CONFIG_NODE, &vtysh_route_map_cmd); + install_element(RMAP_NODE, &vtysh_exit_rmap_cmd); + install_element(RMAP_NODE, &vtysh_quit_rmap_cmd); + install_element(RMAP_NODE, &vtysh_end_all_cmd); - install_element(CONFIG_NODE, &router_eigrp_cmd); - install_element(CONFIG_NODE, &router_babel_cmd); - install_element(CONFIG_NODE, &router_rip_cmd); - install_element(CONFIG_NODE, &router_ripng_cmd); - install_element(CONFIG_NODE, &router_ospf_cmd); - install_element(CONFIG_NODE, &router_ospf6_cmd); -#if defined(HAVE_LDPD) - install_element(CONFIG_NODE, &ldp_mpls_ldp_cmd); - install_element(LDP_NODE, &ldp_address_family_ipv4_cmd); - install_element(LDP_NODE, &ldp_address_family_ipv6_cmd); - install_element(LDP_IPV4_NODE, &ldp_interface_ifname_cmd); - install_element(LDP_IPV6_NODE, &ldp_interface_ifname_cmd); - install_element(CONFIG_NODE, &ldp_l2vpn_word_type_vpls_cmd); - install_element(LDP_L2VPN_NODE, &ldp_member_pseudowire_ifname_cmd); -#endif - install_element(CONFIG_NODE, &router_isis_cmd); - install_element(CONFIG_NODE, &router_openfabric_cmd); - install_element(CONFIG_NODE, &router_bgp_cmd); -#ifdef KEEP_OLD_VPN_COMMANDS - install_element(BGP_NODE, &address_family_vpnv4_cmd); - install_element(BGP_NODE, &address_family_vpnv6_cmd); -#endif /* KEEP_OLD_VPN_COMMANDS */ -#if defined(ENABLE_BGP_VNC) - install_element(BGP_NODE, &vnc_vrf_policy_cmd); - install_element(BGP_NODE, &vnc_defaults_cmd); - install_element(BGP_NODE, &vnc_nve_group_cmd); - install_element(BGP_NODE, &vnc_l2_group_cmd); -#endif - install_element(BGP_NODE, &address_family_ipv4_cmd); - install_element(BGP_NODE, &address_family_ipv4_multicast_cmd); - install_element(BGP_NODE, &address_family_ipv4_vpn_cmd); - install_element(BGP_NODE, &address_family_ipv4_labeled_unicast_cmd); - install_element(BGP_NODE, &address_family_ipv6_cmd); - install_element(BGP_NODE, &address_family_ipv6_multicast_cmd); - install_element(BGP_NODE, &address_family_ipv6_vpn_cmd); - install_element(BGP_NODE, &address_family_ipv6_labeled_unicast_cmd); - install_element(BGP_NODE, &address_family_evpn_cmd); - install_element(BGP_NODE, &address_family_flowspecv4_cmd); - install_element(BGP_NODE, &address_family_flowspecv6_cmd); -#if defined(HAVE_CUMULUS) - install_element(BGP_NODE, &address_family_evpn2_cmd); -#endif - install_element(BGP_VPNV4_NODE, &exit_address_family_cmd); - install_element(BGP_VPNV6_NODE, &exit_address_family_cmd); - install_element(BGP_IPV4_NODE, &exit_address_family_cmd); - install_element(BGP_IPV4M_NODE, &exit_address_family_cmd); - install_element(BGP_IPV4L_NODE, &exit_address_family_cmd); - install_element(BGP_IPV6_NODE, &exit_address_family_cmd); - install_element(BGP_IPV6M_NODE, &exit_address_family_cmd); - install_element(BGP_EVPN_NODE, &exit_address_family_cmd); - install_element(BGP_IPV6L_NODE, &exit_address_family_cmd); - install_element(BGP_FLOWSPECV4_NODE, &exit_address_family_cmd); - install_element(BGP_FLOWSPECV6_NODE, &exit_address_family_cmd); + install_node(&vty_node); + install_element(CONFIG_NODE, &vtysh_line_vty_cmd); + install_element(VTY_NODE, &vtysh_exit_line_vty_cmd); + install_element(VTY_NODE, &vtysh_quit_line_vty_cmd); + install_element(VTY_NODE, &vtysh_end_all_cmd); - install_element(BGP_NODE, &bmp_targets_cmd); - install_element(BMP_NODE, &bmp_exit_cmd); - install_element(BMP_NODE, &bmp_quit_cmd); - install_element(BMP_NODE, &vtysh_end_all_cmd); - install_element(CONFIG_NODE, &rpki_cmd); - install_element(RPKI_NODE, &rpki_exit_cmd); - install_element(RPKI_NODE, &rpki_quit_cmd); - install_element(RPKI_NODE, &vtysh_end_all_cmd); + struct cmd_node *node; + for (unsigned int i = 0; i < vector_active(cmdvec); i++) { + node = vector_slot(cmdvec, i); + if (!node || node->node == VIEW_NODE) + continue; + vtysh_install_default(node->node); + } - /* EVPN commands */ - install_element(BGP_EVPN_NODE, &bgp_evpn_vni_cmd); - install_element(BGP_EVPN_VNI_NODE, &exit_vni_cmd); + /* vtysh */ - install_element(BGP_VRF_POLICY_NODE, &exit_vrf_policy_cmd); - install_element(BGP_VNC_DEFAULTS_NODE, &exit_vnc_config_cmd); - install_element(BGP_VNC_NVE_GROUP_NODE, &exit_vnc_config_cmd); - install_element(BGP_VNC_L2_GROUP_NODE, &exit_vnc_config_cmd); + install_element(VIEW_NODE, &vtysh_enable_cmd); + install_element(ENABLE_NODE, &vtysh_config_terminal_cmd); + install_element(ENABLE_NODE, &vtysh_disable_cmd); + + /* "exit" command. */ + install_element(VIEW_NODE, &vtysh_exit_all_cmd); + install_element(CONFIG_NODE, &vtysh_exit_all_cmd); + install_element(VIEW_NODE, &vtysh_quit_all_cmd); + install_element(CONFIG_NODE, &vtysh_quit_all_cmd); + + /* "end" command. */ + install_element(CONFIG_NODE, &vtysh_end_all_cmd); + install_element(ENABLE_NODE, &vtysh_end_all_cmd); - install_element(CONFIG_NODE, &key_chain_cmd); - install_element(CONFIG_NODE, &vtysh_route_map_cmd); - install_element(CONFIG_NODE, &vtysh_pbr_map_cmd); - install_element(CONFIG_NODE, &vtysh_no_pbr_map_cmd); - install_element(CONFIG_NODE, &vtysh_line_vty_cmd); - install_element(KEYCHAIN_NODE, &key_cmd); - install_element(KEYCHAIN_NODE, &key_chain_cmd); - install_element(KEYCHAIN_KEY_NODE, &key_chain_cmd); - install_element(CONFIG_NODE, &vtysh_interface_cmd); - install_element(CONFIG_NODE, &vtysh_pseudowire_cmd); - install_element(INTERFACE_NODE, &vtysh_link_params_cmd); install_element(ENABLE_NODE, &vtysh_show_running_config_cmd); install_element(ENABLE_NODE, &vtysh_copy_running_config_cmd); install_element(ENABLE_NODE, &vtysh_copy_to_running_cmd); - install_element(CONFIG_NODE, &vtysh_vrf_cmd); - install_element(VRF_NODE, &vtysh_vrf_netns_cmd); - install_element(VRF_NODE, &vtysh_no_vrf_netns_cmd); - install_element(VRF_NODE, &exit_vrf_config_cmd); - - install_element(CONFIG_NODE, &vtysh_no_nexthop_group_cmd); - /* "write terminal" command. */ install_element(ENABLE_NODE, &vtysh_write_terminal_cmd); diff --git a/yang/frr-bfdd.yang b/yang/frr-bfdd.yang index 4b7785e690..5b434162d0 100644 --- a/yang/frr-bfdd.yang +++ b/yang/frr-bfdd.yang @@ -12,6 +12,9 @@ module frr-bfdd { import frr-interface { prefix frr-interface; } + import frr-vrf { + prefix frr-vrf; + } import frr-route-types { prefix frr-route-types; } @@ -396,14 +399,12 @@ module frr-bfdd { } leaf interface { - type string { - length "0..16"; - } + type frr-interface:interface-ref; description "Interface to use to contact peer"; } leaf vrf { - type string; + type frr-vrf:vrf-ref; description "Virtual Routing Domain name"; } @@ -441,14 +442,12 @@ module frr-bfdd { } leaf interface { - type string { - length "0..16"; - } + type frr-interface:interface-ref; description "Interface to use to contact peer"; } leaf vrf { - type string; + type frr-vrf:vrf-ref; description "Virtual Routing Domain name"; } diff --git a/yang/frr-bgp-common-structure.yang b/yang/frr-bgp-common-structure.yang index 8162527e90..6543b1d1c3 100644 --- a/yang/frr-bgp-common-structure.yang +++ b/yang/frr-bgp-common-structure.yang @@ -9,6 +9,10 @@ submodule frr-bgp-common-structure { prefix inet; } + import frr-route-map { + prefix frr-route-map; + } + import frr-interface { prefix frr-interface; } @@ -371,7 +375,7 @@ submodule frr-bgp-common-structure { } leaf rmap-policy-export { - type frr-bt:rmap-ref; + type frr-route-map:route-map-ref; description "Route-map to specify criteria to originate default."; } diff --git a/yang/frr-bgp-common.yang b/yang/frr-bgp-common.yang index 188cf856db..96ec9dc969 100644 --- a/yang/frr-bgp-common.yang +++ b/yang/frr-bgp-common.yang @@ -17,6 +17,10 @@ submodule frr-bgp-common { prefix frr-bt; } + import frr-route-map { + prefix frr-route-map; + } + import frr-route-types { prefix frr-rt-type; } @@ -73,25 +77,25 @@ submodule frr-bgp-common { grouping rmap-policy-import { leaf rmap-import { - type frr-bt:rmap-ref; + type frr-route-map:route-map-ref; } } grouping rmap-policy-export { leaf rmap-export { - type frr-bt:rmap-ref; + type frr-route-map:route-map-ref; } } grouping unsupress-map-policy-import { leaf unsupress-map-import { - type frr-bt:rmap-ref; + type frr-route-map:route-map-ref; } } grouping unsupress-map-policy-export { leaf unsupress-map-export { - type frr-bt:rmap-ref; + type frr-route-map:route-map-ref; } } @@ -728,7 +732,7 @@ submodule frr-bgp-common { } leaf rmap-policy-import { - type frr-bt:rmap-ref; + type frr-route-map:route-map-ref; description "Route-map to be applied for redistributed routes into the bgp."; } @@ -743,7 +747,7 @@ submodule frr-bgp-common { } leaf rmap-policy-export { - type frr-bt:rmap-ref; + type frr-route-map:route-map-ref; description "Route-map to modify the attributes for Routes going out via BGP updates."; @@ -771,7 +775,7 @@ submodule frr-bgp-common { } leaf rmap-policy-export { - type frr-bt:rmap-ref; + type frr-route-map:route-map-ref; description "Apply route map to aggregate network."; } @@ -1097,7 +1101,7 @@ submodule frr-bgp-common { } leaf rmap-policy-export { - type frr-bt:rmap-ref; + type frr-route-map:route-map-ref; description "Route-map to modify the attributes for Routes going out via BGP updates."; diff --git a/yang/frr-bgp-types.yang b/yang/frr-bgp-types.yang index 0afdea1ba6..55834df2ee 100644 --- a/yang/frr-bgp-types.yang +++ b/yang/frr-bgp-types.yang @@ -41,10 +41,6 @@ module frr-bgp-types { "Initial revision."; } - typedef rmap-ref { - type string; - } - typedef plist-ref { type string; } diff --git a/yang/frr-eigrpd.yang b/yang/frr-eigrpd.yang index 3d1bf3baa5..2127ac4988 100644 --- a/yang/frr-eigrpd.yang +++ b/yang/frr-eigrpd.yang @@ -12,6 +12,12 @@ module frr-eigrpd { import frr-interface { prefix frr-interface; } + import frr-vrf { + prefix frr-vrf; + } + import frr-route-map { + prefix frr-route-map; + } import frr-route-types { prefix frr-route-types; } @@ -109,9 +115,7 @@ module frr-eigrpd { leaf vrf { description "Virtual Routing Domain name"; - type string { - length "0..16"; - } + type frr-vrf:vrf-ref; } /* @@ -235,9 +239,7 @@ module frr-eigrpd { "Applies the conditions of the specified route-map to routes that are redistributed into the EIGRP routing instance"; - type string { - length "1..max"; - } + type frr-route-map:route-map-ref; } container metrics { diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang index cc959bd9fe..79941a50e7 100644 --- a/yang/frr-isisd.yang +++ b/yang/frr-isisd.yang @@ -15,6 +15,14 @@ module frr-isisd { prefix frr-interface; } + import frr-vrf { + prefix frr-vrf; + } + + import frr-route-map { + prefix frr-route-map; + } + import frr-route-types { prefix frr-route-types; } @@ -240,9 +248,7 @@ module frr-isisd { description "Common optional attributes of any redistribute entry."; leaf route-map { - type string { - length "1..max"; - } + type frr-route-map:route-map-ref; description "Applies the conditions of the specified route-map to routes that are redistributed into this routing instance."; @@ -341,7 +347,7 @@ module frr-isisd { } leaf vrf { - type string; + type frr-vrf:vrf-ref; default "default"; description "VRF NAME."; @@ -830,7 +836,7 @@ module frr-isisd { } leaf vrf { - type string; + type frr-vrf:vrf-ref; description "VRF NAME."; } diff --git a/yang/frr-nexthop.yang b/yang/frr-nexthop.yang index 0cb0f93ee4..52155dcd16 100644 --- a/yang/frr-nexthop.yang +++ b/yang/frr-nexthop.yang @@ -156,7 +156,7 @@ module frr-nexthop { } leaf vrf { - type string; + type frr-vrf:vrf-ref; description "The nexthop vrf name, if different from the route."; } @@ -167,7 +167,7 @@ module frr-nexthop { } leaf interface { - type string; + type frr-interface:interface-ref; description "The nexthop egress interface."; } @@ -275,7 +275,7 @@ module frr-nexthop { description "List of nexthop groups, each contains group of nexthops"; leaf name { - type string; + type nexthop-group-ref; description "The nexthop-group name."; } diff --git a/yang/frr-ospfd.yang b/yang/frr-ospfd.yang index 466dd42ce4..42a7e8784c 100644 --- a/yang/frr-ospfd.yang +++ b/yang/frr-ospfd.yang @@ -15,6 +15,10 @@ module frr-ospfd { prefix frr-interface; } + import frr-route-map { + prefix frr-route-map; + } + import frr-route-types { prefix frr-route-types; } @@ -41,10 +45,6 @@ module frr-ospfd { } /* Policy types to be removed later, once policy Yang finalized */ - typedef rmap-ref { - type string; - } - typedef plist-ref { type string; } @@ -425,7 +425,7 @@ module frr-ospfd { } leaf route-map { - type rmap-ref; + type frr-route-map:route-map-ref; description "Route map reference."; } diff --git a/yang/frr-ripd.yang b/yang/frr-ripd.yang index f5775ab968..929c916069 100644 --- a/yang/frr-ripd.yang +++ b/yang/frr-ripd.yang @@ -12,6 +12,12 @@ module frr-ripd { import frr-interface { prefix frr-interface; } + import frr-vrf { + prefix frr-vrf; + } + import frr-route-map { + prefix frr-route-map; + } import frr-route-types { prefix frr-route-types; } @@ -72,7 +78,7 @@ module frr-ripd { "RIP routing instance."; leaf vrf { - type string; + type frr-vrf:vrf-ref; description "VRF name."; } @@ -227,9 +233,7 @@ module frr-ripd { must '. != "rip"'; } leaf route-map { - type string { - length "1..max"; - } + type frr-route-map:route-map-ref; description "Applies the conditions of the specified route-map to routes that are redistributed into the RIP routing @@ -396,7 +400,7 @@ module frr-ripd { "Next hop IPv4 address."; } leaf interface { - type string; + type frr-interface:interface-ref; description "The interface that the route uses."; } @@ -587,7 +591,7 @@ module frr-ripd { input { leaf vrf { - type string; + type frr-vrf:vrf-ref; description "VRF name identifying a specific RIP instance. This leaf is optional for the rpc. @@ -608,7 +612,7 @@ module frr-ripd { receives a PDU with the wrong authentication type field."; leaf interface-name { - type string; + type frr-interface:interface-ref; description "Describes the name of the RIP interface."; } @@ -624,7 +628,7 @@ module frr-ripd { receives a PDU with the wrong authentication information."; leaf interface-name { - type string; + type frr-interface:interface-ref; description "Describes the name of the RIP interface."; } diff --git a/yang/frr-ripngd.yang b/yang/frr-ripngd.yang index 52e208b2c9..07d38bd416 100644 --- a/yang/frr-ripngd.yang +++ b/yang/frr-ripngd.yang @@ -12,6 +12,12 @@ module frr-ripngd { import frr-interface { prefix frr-interface; } + import frr-vrf { + prefix frr-vrf; + } + import frr-route-map { + prefix frr-route-map; + } import frr-route-types { prefix frr-route-types; } @@ -71,7 +77,7 @@ module frr-ripngd { "RIPng routing instance."; leaf vrf { - type string; + type frr-vrf:vrf-ref; description "VRF name."; } @@ -170,9 +176,7 @@ module frr-ripngd { must '. != "ripng"'; } leaf route-map { - type string { - length "1..max"; - } + type frr-route-map:route-map-ref; description "Applies the conditions of the specified route-map to routes that are redistributed into the RIPng routing @@ -298,7 +302,7 @@ module frr-ripngd { "Next hop IPv6 address."; } leaf interface { - type string; + type frr-interface:interface-ref; description "The interface that the route uses."; } @@ -359,7 +363,7 @@ module frr-ripngd { input { leaf vrf { - type string; + type frr-vrf:vrf-ref; description "VRF name identifying a specific RIPng instance. This leaf is optional for the rpc. diff --git a/yang/frr-routing.yang b/yang/frr-routing.yang index 52607f9ad0..f8441669af 100644 --- a/yang/frr-routing.yang +++ b/yang/frr-routing.yang @@ -249,7 +249,7 @@ module frr-routing { instance."; } leaf vrf { - type string; + type frr-vrf:vrf-ref; description "vrf for control-plane protocol"; } diff --git a/yang/frr-test-module.yang b/yang/frr-test-module.yang index 61915b1349..2f89ebca88 100644 --- a/yang/frr-test-module.yang +++ b/yang/frr-test-module.yang @@ -57,7 +57,7 @@ module frr-test-module { } container interfaces { leaf-list interface { - type string; + type frr-interface:interface-ref; } } container routes { @@ -69,7 +69,7 @@ module frr-test-module { type inet:ipv4-address; } leaf interface { - type string; + type frr-interface:interface-ref; } leaf metric { type uint8; diff --git a/yang/frr-zebra.yang b/yang/frr-zebra.yang index 2efc45c146..782e0a4b74 100644 --- a/yang/frr-zebra.yang +++ b/yang/frr-zebra.yang @@ -2137,7 +2137,7 @@ module frr-zebra { "The admin distance to use for imported routes."; } leaf route-map { - type string; + type frr-route-map:route-map-ref; description "A route-map to filter imported routes."; } diff --git a/zebra/rib.h b/zebra/rib.h index be680a112f..9ddc35b091 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -333,6 +333,10 @@ extern void route_entry_copy_nexthops(struct route_entry *re, int route_entry_update_nhe(struct route_entry *re, struct nhg_hash_entry *new_nhghe); +/* NHG replace has happend, we have to update route_entry pointers to new one */ +void rib_handle_nhg_replace(struct nhg_hash_entry *old, + struct nhg_hash_entry *new); + #define route_entry_dump(prefix, src, re) _route_entry_dump(__func__, prefix, src, re) extern void _route_entry_dump(const char *func, union prefixconstptr pp, union prefixconstptr src_pp, diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 146650f602..1ce3c435fe 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -126,6 +126,31 @@ static bool kernel_nexthops_supported(void) } /* + * Some people may only want to use NHGs created by protos and not + * implicitly created by Zebra. This check accounts for that. + */ +static bool proto_nexthops_only(void) +{ + return zebra_nhg_proto_nexthops_only(); +} + +/* Is this a proto created NHG? */ +static bool is_proto_nhg(uint32_t id, int type) +{ + /* If type is available, use it as the source of truth */ + if (type) { + if (type != ZEBRA_ROUTE_NHG) + return true; + return false; + } + + if (id >= ZEBRA_NHG_PROTO_LOWER) + return true; + + return false; +} + +/* * The ipv4_ll data structure is used for all 5549 * additions to the kernel. Let's figure out the * correct value one time instead for every @@ -1748,7 +1773,10 @@ ssize_t netlink_route_multipath_msg_encode(int cmd, nl_attr_nest_end(&req->n, nest); } - if ((!fpm && kernel_nexthops_supported()) || (fpm && force_nhg)) { + if ((!fpm && kernel_nexthops_supported() + && (!proto_nexthops_only() + || is_proto_nhg(dplane_ctx_get_nhe_id(ctx), 0))) + || (fpm && force_nhg)) { /* Kernel supports nexthop objects */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("%s: %pFX nhg_id is %u", __func__, p, @@ -2072,6 +2100,35 @@ ssize_t netlink_nexthop_msg_encode(uint16_t cmd, mpls_lse_t out_lse[MPLS_MAX_LABELS]; char label_buf[256]; int num_labels = 0; + uint32_t id = dplane_ctx_get_nhe_id(ctx); + int type = dplane_ctx_get_nhe_type(ctx); + + if (!id) { + flog_err( + EC_ZEBRA_NHG_FIB_UPDATE, + "Failed trying to update a nexthop group in the kernel that does not have an ID"); + return -1; + } + + /* + * Nothing to do if the kernel doesn't support nexthop objects or + * we dont want to install this type of NHG + */ + if (!kernel_nexthops_supported()) { + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_NHG) + zlog_debug( + "%s: nhg_id %u (%s): kernel nexthops not supported, ignoring", + __func__, id, zebra_route_string(type)); + return 0; + } + + if (proto_nexthops_only() && !is_proto_nhg(id, type)) { + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_NHG) + zlog_debug( + "%s: nhg_id %u (%s): proto-based nexthops only, ignoring", + __func__, id, zebra_route_string(type)); + return 0; + } label_buf[0] = '\0'; @@ -2092,15 +2149,6 @@ ssize_t netlink_nexthop_msg_encode(uint16_t cmd, req->nhm.nh_family = AF_UNSPEC; /* TODO: Scope? */ - uint32_t id = dplane_ctx_get_nhe_id(ctx); - - if (!id) { - flog_err( - EC_ZEBRA_NHG_FIB_UPDATE, - "Failed trying to update a nexthop group in the kernel that does not have an ID"); - return -1; - } - if (!nl_attr_put32(&req->n, buflen, NHA_ID, id)) return 0; @@ -2221,8 +2269,7 @@ nexthop_done: nh->vrf_id, label_buf); } - req->nhm.nh_protocol = - zebra2proto(dplane_ctx_get_nhe_type(ctx)); +req->nhm.nh_protocol = zebra2proto(type); } else if (cmd != RTM_DELNEXTHOP) { flog_err( diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index e436e5a288..c33bca3d86 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -712,6 +712,34 @@ static int zsend_ipv4_nexthop_lookup_mrib(struct zserv *client, return zserv_send_message(client, s); } +static int nhg_notify(uint16_t type, uint16_t instance, uint32_t id, + enum zapi_nhg_notify_owner note) +{ + struct zserv *client; + struct stream *s; + + client = zserv_find_client(type, instance); + if (!client) { + if (IS_ZEBRA_DEBUG_PACKET) { + zlog_debug("Not Notifying Owner: %u(%u) about %u(%d)", + type, instance, id, note); + } + return 0; + } + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + stream_reset(s); + + zclient_create_header(s, ZEBRA_NHG_NOTIFY_OWNER, VRF_DEFAULT); + + stream_put(s, ¬e, sizeof(note)); + stream_putl(s, id); + + stream_putw_at(s, 0, stream_get_endp(s)); + + return zserv_send_message(client, s); +} + /* * Common utility send route notification, called from a path using a * route_entry and from a path using a dataplane context. @@ -1411,27 +1439,32 @@ stream_failure: return; } - -void zserv_nexthop_num_warn(const char *caller, const struct prefix *p, +bool zserv_nexthop_num_warn(const char *caller, const struct prefix *p, const unsigned int nexthop_num) { if (nexthop_num > zrouter.multipath_num) { char buff[PREFIX2STR_BUFFER]; - prefix2str(p, buff, sizeof(buff)); + if (p) + prefix2str(p, buff, sizeof(buff)); + flog_warn( EC_ZEBRA_MORE_NH_THAN_MULTIPATH, "%s: Prefix %s has %d nexthops, but we can only use the first %d", - caller, buff, nexthop_num, zrouter.multipath_num); + caller, (p ? buff : "(NULL)"), nexthop_num, + zrouter.multipath_num); + return true; } + + return false; } /* * Create a new nexthop based on a zapi nexthop. */ -static struct nexthop *nexthop_from_zapi(struct route_entry *re, - const struct zapi_nexthop *api_nh, - const struct zapi_route *api) +static struct nexthop *nexthop_from_zapi(const struct zapi_nexthop *api_nh, + uint32_t flags, struct prefix *p, + uint16_t backup_nexthop_num) { struct nexthop *nexthop = NULL; struct ipaddr vtep_ip; @@ -1469,14 +1502,13 @@ static struct nexthop *nexthop_from_zapi(struct route_entry *re, /* Special handling for IPv4 routes sourced from EVPN: * the nexthop and associated MAC need to be installed. */ - if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) { + if (CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE)) { memset(&vtep_ip, 0, sizeof(struct ipaddr)); vtep_ip.ipa_type = IPADDR_V4; memcpy(&(vtep_ip.ipaddr_v4), &(api_nh->gate.ipv4), sizeof(struct in_addr)); zebra_vxlan_evpn_vrf_route_add( - api_nh->vrf_id, &api_nh->rmac, - &vtep_ip, &api->prefix); + api_nh->vrf_id, &api_nh->rmac, &vtep_ip, p); } break; case NEXTHOP_TYPE_IPV6: @@ -1503,14 +1535,13 @@ static struct nexthop *nexthop_from_zapi(struct route_entry *re, /* Special handling for IPv6 routes sourced from EVPN: * the nexthop and associated MAC need to be installed. */ - if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) { + if (CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE)) { memset(&vtep_ip, 0, sizeof(struct ipaddr)); vtep_ip.ipa_type = IPADDR_V6; memcpy(&vtep_ip.ipaddr_v6, &(api_nh->gate.ipv6), sizeof(struct in6_addr)); zebra_vxlan_evpn_vrf_route_add( - api_nh->vrf_id, &api_nh->rmac, - &vtep_ip, &api->prefix); + api_nh->vrf_id, &api_nh->rmac, &vtep_ip, p); } break; case NEXTHOP_TYPE_BLACKHOLE: @@ -1558,7 +1589,7 @@ static struct nexthop *nexthop_from_zapi(struct route_entry *re, for (i = 0; i < api_nh->backup_num; i++) { /* Validate backup index */ - if (api_nh->backup_idx[i] < api->backup_nexthop_num) { + if (api_nh->backup_idx[i] < backup_nexthop_num) { nexthop->backup_idx[i] = api_nh->backup_idx[i]; } else { if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_EVENT) @@ -1576,99 +1607,69 @@ done: return nexthop; } -static void zread_route_add(ZAPI_HANDLER_ARGS) +static bool zapi_read_nexthops(struct zserv *client, struct prefix *p, + struct zapi_nexthop *nhops, uint32_t flags, + uint32_t message, uint16_t nexthop_num, + uint16_t backup_nh_num, + struct nexthop_group **png, + struct nhg_backup_info **pbnhg) { - struct stream *s; - struct zapi_route api; - struct zapi_nexthop *api_nh; - afi_t afi; - struct prefix_ipv6 *src_p = NULL; - struct route_entry *re; - struct nexthop *nexthop = NULL, *last_nh; struct nexthop_group *ng = NULL; struct nhg_backup_info *bnhg = NULL; - int i, ret; - vrf_id_t vrf_id; - struct nhg_hash_entry nhe; - enum lsp_types_t label_type; - char nhbuf[NEXTHOP_STRLEN]; - char labelbuf[MPLS_LABEL_STRLEN]; - - s = msg; - if (zapi_route_decode(s, &api) < 0) { - if (IS_ZEBRA_DEBUG_RECV) - zlog_debug("%s: Unable to decode zapi_route sent", - __func__); - return; - } - - vrf_id = zvrf_id(zvrf); - - if (IS_ZEBRA_DEBUG_RECV) { - char buf_prefix[PREFIX_STRLEN]; + uint16_t i; + struct nexthop *last_nh = NULL; - prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix)); - zlog_debug("%s: p=(%u:%u)%s, msg flags=0x%x, flags=0x%x", - __func__, vrf_id, api.tableid, buf_prefix, (int)api.message, api.flags); - } - - /* Allocate new route. */ - re = XCALLOC(MTYPE_RE, sizeof(struct route_entry)); - re->type = api.type; - re->instance = api.instance; - re->flags = api.flags; - re->uptime = monotime(NULL); - re->vrf_id = vrf_id; - - if (api.tableid) - re->table = api.tableid; - else - re->table = zvrf->table_id; + assert(!(png && pbnhg)); - if (!CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) - || api.nexthop_num == 0) { - flog_warn(EC_ZEBRA_RX_ROUTE_NO_NEXTHOPS, - "%s: received a route without nexthops for prefix %pFX from client %s", - __func__, &api.prefix, - zebra_route_string(client->proto)); + if (png) + ng = nexthop_group_new(); - XFREE(MTYPE_RE, re); - return; - } + if (pbnhg && backup_nh_num > 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: adding %d backup nexthops", __func__, + backup_nh_num); - /* Report misuse of the backup flag */ - if (CHECK_FLAG(api.message, ZAPI_MESSAGE_BACKUP_NEXTHOPS) && - api.backup_nexthop_num == 0) { - if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_EVENT) - zlog_debug("%s: client %s: BACKUP flag set but no backup nexthops, prefix %pFX", - __func__, - zebra_route_string(client->proto), &api.prefix); + bnhg = zebra_nhg_backup_alloc(); } - /* Use temporary list of nexthops */ - ng = nexthop_group_new(); - /* * TBD should _all_ of the nexthop add operations use * api_nh->vrf_id instead of re->vrf_id ? I only changed * for cases NEXTHOP_TYPE_IPV4 and NEXTHOP_TYPE_IPV6. */ - for (i = 0; i < api.nexthop_num; i++) { - api_nh = &api.nexthops[i]; + for (i = 0; i < nexthop_num; i++) { + struct nexthop *nexthop; + enum lsp_types_t label_type; + char nhbuf[NEXTHOP_STRLEN]; + char labelbuf[MPLS_LABEL_STRLEN]; + struct zapi_nexthop *api_nh = &nhops[i]; /* Convert zapi nexthop */ - nexthop = nexthop_from_zapi(re, api_nh, &api); + nexthop = nexthop_from_zapi(api_nh, flags, p, backup_nh_num); if (!nexthop) { flog_warn( EC_ZEBRA_NEXTHOP_CREATION_FAILED, - "%s: Nexthops Specified: %d but we failed to properly create one", - __func__, api.nexthop_num); - nexthop_group_delete(&ng); - XFREE(MTYPE_RE, re); - return; + "%s: Nexthops Specified: %u(%u) but we failed to properly create one", + __func__, nexthop_num, i); + if (ng) + nexthop_group_delete(&ng); + if (bnhg) + zebra_nhg_backup_free(&bnhg); + return false; } - if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRTE)) { + if (bnhg + && CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + if (IS_ZEBRA_DEBUG_RECV) { + nexthop2str(nexthop, nhbuf, sizeof(nhbuf)); + zlog_debug("%s: backup nh %s with BACKUP flag!", + __func__, nhbuf); + } + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP); + nexthop->backup_num = 0; + } + + if (CHECK_FLAG(message, ZAPI_MESSAGE_SRTE)) { SET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE); nexthop->srte_color = api_nh->srte_color; } @@ -1703,100 +1704,266 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) __func__, nhbuf, api_nh->vrf_id, labelbuf); } - /* Add new nexthop to temporary list. This list is - * canonicalized - sorted - so that it can be hashed later - * in route processing. We expect that the sender has sent - * the list sorted, and the zapi client api attempts to enforce - * that, so this should be inexpensive - but it is necessary - * to support shared nexthop-groups. - */ - nexthop_group_add_sorted(ng, nexthop); + if (ng) { + /* Add new nexthop to temporary list. This list is + * canonicalized - sorted - so that it can be hashed + * later in route processing. We expect that the sender + * has sent the list sorted, and the zapi client api + * attempts to enforce that, so this should be + * inexpensive - but it is necessary to support shared + * nexthop-groups. + */ + nexthop_group_add_sorted(ng, nexthop); + } + if (bnhg) { + /* Note that the order of the backup nexthops is + * significant, so we don't sort this list as we do the + * primary nexthops, we just append. + */ + if (last_nh) + NEXTHOP_APPEND(last_nh, nexthop); + else + bnhg->nhe->nhg.nexthop = nexthop; + + last_nh = nexthop; + } } - /* Allocate temporary list of backup nexthops, if necessary */ - if (api.backup_nexthop_num > 0) { - if (IS_ZEBRA_DEBUG_RECV) - zlog_debug("%s: adding %d backup nexthops", - __func__, api.backup_nexthop_num); - bnhg = zebra_nhg_backup_alloc(); - nexthop = NULL; - last_nh = NULL; + /* succesfully read, set caller pointers now */ + if (png) + *png = ng; + + if (pbnhg) + *pbnhg = bnhg; + + return true; +} + +int zapi_nhg_decode(struct stream *s, int cmd, struct zapi_nhg *api_nhg) +{ + uint16_t i; + struct zapi_nexthop *znh; + + STREAM_GETW(s, api_nhg->proto); + STREAM_GETL(s, api_nhg->id); + + if (cmd == ZEBRA_NHG_DEL) + goto done; + + /* Nexthops */ + STREAM_GETW(s, api_nhg->nexthop_num); + + if (zserv_nexthop_num_warn(__func__, NULL, api_nhg->nexthop_num)) + return -1; + + if (api_nhg->nexthop_num <= 0) { + flog_warn(EC_ZEBRA_NEXTHOP_CREATION_FAILED, + "%s: No nexthops sent", __func__); + return -1; } - /* Copy backup nexthops also, if present */ - for (i = 0; i < api.backup_nexthop_num; i++) { - api_nh = &api.backup_nexthops[i]; + for (i = 0; i < api_nhg->nexthop_num; i++) { + znh = &((api_nhg->nexthops)[i]); - /* Convert zapi backup nexthop */ - nexthop = nexthop_from_zapi(re, api_nh, &api); - if (!nexthop) { - flog_warn( - EC_ZEBRA_NEXTHOP_CREATION_FAILED, - "%s: Backup Nexthops Specified: %d but we failed to properly create one", - __func__, api.backup_nexthop_num); - nexthop_group_delete(&ng); - zebra_nhg_backup_free(&bnhg); - XFREE(MTYPE_RE, re); - return; + if (zapi_nexthop_decode(s, znh, 0, 0) != 0) { + flog_warn(EC_ZEBRA_NEXTHOP_CREATION_FAILED, + "%s: Nexthop creation failed", __func__); + return -1; } + } - /* Backup nexthops can't have backups; that's not valid. */ - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { - if (IS_ZEBRA_DEBUG_RECV) { - nexthop2str(nexthop, nhbuf, sizeof(nhbuf)); - zlog_debug("%s: backup nh %s with BACKUP flag!", - __func__, nhbuf); - } - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP); - nexthop->backup_num = 0; - } + /* Backup Nexthops */ + STREAM_GETW(s, api_nhg->backup_nexthop_num); - if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRTE)) { - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE); - nexthop->srte_color = api_nh->srte_color; - } + if (zserv_nexthop_num_warn(__func__, NULL, api_nhg->backup_nexthop_num)) + return -1; - /* MPLS labels for BGP-LU or Segment Routing */ - if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL) - && api_nh->type != NEXTHOP_TYPE_IFINDEX - && api_nh->type != NEXTHOP_TYPE_BLACKHOLE - && api_nh->label_num > 0) { + for (i = 0; i < api_nhg->backup_nexthop_num; i++) { + znh = &((api_nhg->backup_nexthops)[i]); - label_type = lsp_type_from_re_type(client->proto); - nexthop_add_labels(nexthop, label_type, - api_nh->label_num, - &api_nh->labels[0]); + if (zapi_nexthop_decode(s, znh, 0, 0) != 0) { + flog_warn(EC_ZEBRA_NEXTHOP_CREATION_FAILED, + "%s: Backup Nexthop creation failed", + __func__); + return -1; } + } - if (IS_ZEBRA_DEBUG_RECV) { - labelbuf[0] = '\0'; - nhbuf[0] = '\0'; +done: + return 0; - nexthop2str(nexthop, nhbuf, sizeof(nhbuf)); +stream_failure: + flog_warn( + EC_ZEBRA_NEXTHOP_CREATION_FAILED, + "%s: Nexthop Group decode failed with some sort of stream read failure", + __func__); + return -1; +} - if (nexthop->nh_label && - nexthop->nh_label->num_labels > 0) { - mpls_label2str(nexthop->nh_label->num_labels, - nexthop->nh_label->label, - labelbuf, sizeof(labelbuf), - false); - } +static void zread_nhg_del(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_nhg api_nhg = {}; + struct nhg_hash_entry *nhe; - zlog_debug("%s: backup nh=%s, vrf_id=%d %s", - __func__, nhbuf, api_nh->vrf_id, labelbuf); - } + s = msg; + if (zapi_nhg_decode(s, hdr->command, &api_nhg) < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to decode zapi_nhg sent", + __func__); + return; + } - /* Note that the order of the backup nexthops is significant, - * so we don't sort this list as we do the primary nexthops, - * we just append. - */ - if (last_nh) - NEXTHOP_APPEND(last_nh, nexthop); - else - bnhg->nhe->nhg.nexthop = nexthop; + /* + * Delete the received nhg id + */ + + nhe = zebra_nhg_proto_del(api_nhg.id, api_nhg.proto); + + if (nhe) { + zebra_nhg_decrement_ref(nhe); + nhg_notify(api_nhg.proto, client->instance, api_nhg.id, + ZAPI_NHG_REMOVED); + } else + nhg_notify(api_nhg.proto, client->instance, api_nhg.id, + ZAPI_NHG_REMOVE_FAIL); +} + +static void zread_nhg_add(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_nhg api_nhg = {}; + struct nexthop_group *nhg = NULL; + struct nhg_backup_info *bnhg = NULL; + struct nhg_hash_entry *nhe; - last_nh = nexthop; + s = msg; + if (zapi_nhg_decode(s, hdr->command, &api_nhg) < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to decode zapi_nhg sent", + __func__); + return; + } + + if ((!zapi_read_nexthops(client, NULL, api_nhg.nexthops, 0, 0, + api_nhg.nexthop_num, + api_nhg.backup_nexthop_num, &nhg, NULL)) + || (!zapi_read_nexthops(client, NULL, api_nhg.backup_nexthops, 0, 0, + api_nhg.backup_nexthop_num, + api_nhg.backup_nexthop_num, NULL, &bnhg))) { + + flog_warn(EC_ZEBRA_NEXTHOP_CREATION_FAILED, + "%s: Nexthop Group Creation failed", __func__); + return; + } + + /* + * Create the nhg + */ + nhe = zebra_nhg_proto_add(api_nhg.id, api_nhg.proto, nhg, 0); + + nexthop_group_delete(&nhg); + zebra_nhg_backup_free(&bnhg); + + /* + * TODO: + * Assume fully resolved for now and install. + * + * Resolution is going to need some more work. + */ + if (nhe) + nhg_notify(api_nhg.proto, client->instance, api_nhg.id, + ZAPI_NHG_INSTALLED); + else + nhg_notify(api_nhg.proto, client->instance, api_nhg.id, + ZAPI_NHG_FAIL_INSTALL); +} + +static void zread_route_add(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_route api; + afi_t afi; + struct prefix_ipv6 *src_p = NULL; + struct route_entry *re; + struct nexthop_group *ng = NULL; + struct nhg_backup_info *bnhg = NULL; + int ret; + vrf_id_t vrf_id; + struct nhg_hash_entry nhe; + + s = msg; + if (zapi_route_decode(s, &api) < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to decode zapi_route sent", + __func__); + return; + } + + vrf_id = zvrf_id(zvrf); + + if (IS_ZEBRA_DEBUG_RECV) { + char buf_prefix[PREFIX_STRLEN]; + + prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix)); + zlog_debug("%s: p=(%u:%u)%s, msg flags=0x%x, flags=0x%x", + __func__, vrf_id, api.tableid, buf_prefix, + (int)api.message, api.flags); + } + + /* Allocate new route. */ + re = XCALLOC(MTYPE_RE, sizeof(struct route_entry)); + re->type = api.type; + re->instance = api.instance; + re->flags = api.flags; + re->uptime = monotime(NULL); + re->vrf_id = vrf_id; + + if (api.tableid) + re->table = api.tableid; + else + re->table = zvrf->table_id; + + if (!CHECK_FLAG(api.message, ZAPI_MESSAGE_NHG) + && (!CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) + || api.nexthop_num == 0)) { + flog_warn( + EC_ZEBRA_RX_ROUTE_NO_NEXTHOPS, + "%s: received a route without nexthops for prefix %pFX from client %s", + __func__, &api.prefix, + zebra_route_string(client->proto)); + + XFREE(MTYPE_RE, re); + return; + } + + /* Report misuse of the backup flag */ + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_BACKUP_NEXTHOPS) + && api.backup_nexthop_num == 0) { + if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_EVENT) + zlog_debug( + "%s: client %s: BACKUP flag set but no backup nexthops, prefix %pFX", + __func__, zebra_route_string(client->proto), + &api.prefix); + } + + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NHG)) + re->nhe_id = api.nhgid; + + if (!re->nhe_id + && (!zapi_read_nexthops(client, &api.prefix, api.nexthops, + api.flags, api.message, api.nexthop_num, + api.backup_nexthop_num, &ng, NULL) + || !zapi_read_nexthops(client, &api.prefix, api.backup_nexthops, + api.flags, api.message, + api.backup_nexthop_num, + api.backup_nexthop_num, NULL, &bnhg))) { + + nexthop_group_delete(&ng); + zebra_nhg_backup_free(&bnhg); + XFREE(MTYPE_RE, re); + return; } if (CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE)) @@ -1831,13 +1998,21 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) return; } - /* Include backup info with the route. We use a temporary nhe here; + /* + * If we have an ID, this proto owns the NHG it sent along with the + * route, so we just send the ID into rib code with it. + * + * Havent figured out how to handle backup NHs with this yet, so lets + * keep that separate. + * Include backup info with the route. We use a temporary nhe here; * if this is a new/unknown nhe, a new copy will be allocated * and stored. */ - zebra_nhe_init(&nhe, afi, ng->nexthop); - nhe.nhg.nexthop = ng->nexthop; - nhe.backup_info = bnhg; + if (!re->nhe_id) { + zebra_nhe_init(&nhe, afi, ng->nexthop); + nhe.nhg.nexthop = ng->nexthop; + nhe.backup_info = bnhg; + } ret = rib_add_multipath_nhe(afi, api.safi, &api.prefix, src_p, re, &nhe); @@ -3118,7 +3293,10 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_MLAG_CLIENT_UNREGISTER] = zebra_mlag_client_unregister, [ZEBRA_MLAG_FORWARD_MSG] = zebra_mlag_forward_client_msg, [ZEBRA_CLIENT_CAPABILITIES] = zread_client_capabilities, - [ZEBRA_NEIGH_DISCOVER] = zread_neigh_discover}; + [ZEBRA_NEIGH_DISCOVER] = zread_neigh_discover, + [ZEBRA_NHG_ADD] = zread_nhg_add, + [ZEBRA_NHG_DEL] = zread_nhg_del, +}; /* * Process a batch of zapi messages. diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index e7c755c2bf..9f23a313bf 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -90,7 +90,7 @@ zsend_ipset_entry_notify_owner(struct zebra_pbr_ipset_entry *ipset, enum zapi_ipset_entry_notify_owner note); extern void zsend_iptable_notify_owner(struct zebra_pbr_iptable *iptable, enum zapi_iptable_notify_owner note); -extern void zserv_nexthop_num_warn(const char *caller, const struct prefix *p, +extern bool zserv_nexthop_num_warn(const char *caller, const struct prefix *p, const unsigned int nexthop_num); extern void zsend_capabilities_all_clients(void); diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index abd0adb64e..76d7d00e4a 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -73,6 +73,7 @@ const uint32_t DPLANE_DEFAULT_NEW_WORK = 100; */ struct dplane_nexthop_info { uint32_t id; + uint32_t old_id; afi_t afi; vrf_id_t vrf_id; int type; @@ -1242,6 +1243,12 @@ uint32_t dplane_ctx_get_nhe_id(const struct zebra_dplane_ctx *ctx) return ctx->u.rinfo.nhe.id; } +uint32_t dplane_ctx_get_old_nhe_id(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.rinfo.nhe.old_id; +} + afi_t dplane_ctx_get_nhe_afi(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -1912,6 +1919,7 @@ int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, struct nhg_hash_entry *nhe = zebra_nhg_resolve(re->nhe); ctx->u.rinfo.nhe.id = nhe->id; + ctx->u.rinfo.nhe.old_id = 0; /* * Check if the nhe is installed/queued before doing anything * with this route. @@ -2328,6 +2336,7 @@ dplane_route_update_internal(struct route_node *rn, ctx->u.rinfo.zd_old_instance = old_re->instance; ctx->u.rinfo.zd_old_distance = old_re->distance; ctx->u.rinfo.zd_old_metric = old_re->metric; + ctx->u.rinfo.nhe.old_id = old_re->nhe->id; #ifndef HAVE_NETLINK /* For bsd, capture previous re's nexthops too, sigh. @@ -2349,6 +2358,40 @@ dplane_route_update_internal(struct route_node *rn, #endif /* !HAVE_NETLINK */ } + /* + * If the old and new context type, and nexthop group id + * are the same there is no need to send down a route replace + * as that we know we have sent a nexthop group replace + * or an upper level protocol has sent us the exact + * same route again. + */ + if ((dplane_ctx_get_type(ctx) == dplane_ctx_get_old_type(ctx)) + && (dplane_ctx_get_nhe_id(ctx) + == dplane_ctx_get_old_nhe_id(ctx)) + && (dplane_ctx_get_nhe_id(ctx) >= ZEBRA_NHG_PROTO_LOWER)) { + struct nexthop *nexthop; + + if (IS_ZEBRA_DEBUG_DPLANE) + zlog_debug( + "%s: Ignoring Route exactly the same", + __func__); + + for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), + nexthop)) { + if (CHECK_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (CHECK_FLAG(nexthop->flags, + NEXTHOP_FLAG_ACTIVE)) + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_FIB); + } + + dplane_ctx_free(&ctx); + return ZEBRA_DPLANE_REQUEST_SUCCESS; + } + /* Enqueue context for processing */ ret = dplane_update_enqueue(ctx); } diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 1d852b1bac..fd70211f7c 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -316,6 +316,7 @@ dplane_ctx_get_old_backup_ng(const struct zebra_dplane_ctx *ctx); /* Accessors for nexthop information */ uint32_t dplane_ctx_get_nhe_id(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_get_old_nhe_id(const struct zebra_dplane_ctx *ctx); afi_t dplane_ctx_get_nhe_afi(const struct zebra_dplane_ctx *ctx); vrf_id_t dplane_ctx_get_nhe_vrf_id(const struct zebra_dplane_ctx *ctx); int dplane_ctx_get_nhe_type(const struct zebra_dplane_ctx *ctx); diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index aabbd875ec..de79c59caa 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -53,14 +53,15 @@ uint32_t id_counter; /* */ static bool g_nexthops_enabled = true; +static bool proto_nexthops_only; -static struct nhg_hash_entry *depends_find(const struct nexthop *nh, - afi_t afi); +static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi, + int type); static void depends_add(struct nhg_connected_tree_head *head, struct nhg_hash_entry *depend); static struct nhg_hash_entry * depends_find_add(struct nhg_connected_tree_head *head, struct nexthop *nh, - afi_t afi); + afi_t afi, int type); static struct nhg_hash_entry * depends_find_id_add(struct nhg_connected_tree_head *head, uint32_t id); static void depends_decrement_free(struct nhg_connected_tree_head *head); @@ -68,6 +69,35 @@ static void depends_decrement_free(struct nhg_connected_tree_head *head); static struct nhg_backup_info * nhg_backup_copy(const struct nhg_backup_info *orig); +/* Helper function for getting the next allocatable ID */ +static uint32_t nhg_get_next_id(void) +{ + while (1) { + id_counter++; + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: ID %u checking", __func__, id_counter); + + if (id_counter == ZEBRA_NHG_PROTO_LOWER) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: ID counter wrapped", __func__); + + id_counter = 0; + continue; + } + + if (zebra_nhg_lookup_id(id_counter)) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: ID already exists", __func__); + + continue; + } + + break; + } + + return id_counter; +} static void nhg_connected_free(struct nhg_connected *dep) { @@ -431,7 +461,6 @@ static void *zebra_nhg_hash_alloc(void *arg) nhe->nhg.nexthop->vrf_id, nhe->id); } - zebra_nhg_insert_id(nhe); return nhe; } @@ -439,17 +468,17 @@ static void *zebra_nhg_hash_alloc(void *arg) uint32_t zebra_nhg_hash_key(const void *arg) { const struct nhg_hash_entry *nhe = arg; - uint32_t val, key = 0x5a351234; + uint32_t key = 0x5a351234; + uint32_t primary = 0; + uint32_t backup = 0; - val = nexthop_group_hash(&(nhe->nhg)); - if (nhe->backup_info) { - val = jhash_2words(val, - nexthop_group_hash( - &(nhe->backup_info->nhe->nhg)), - key); - } + primary = nexthop_group_hash(&(nhe->nhg)); + if (nhe->backup_info) + backup = nexthop_group_hash(&(nhe->backup_info->nhe->nhg)); + + key = jhash_3words(primary, backup, nhe->type, key); - key = jhash_3words(nhe->vrf_id, nhe->afi, val, key); + key = jhash_2words(nhe->vrf_id, nhe->afi, key); return key; } @@ -512,6 +541,9 @@ bool zebra_nhg_hash_equal(const void *arg1, const void *arg2) if (nhe1->id && nhe2->id && (nhe1->id == nhe2->id)) return true; + if (nhe1->type != nhe2->type) + return false; + if (nhe1->vrf_id != nhe2->vrf_id) return false; @@ -611,7 +643,7 @@ static int zebra_nhg_process_grp(struct nexthop_group *nhg, } static void handle_recursive_depend(struct nhg_connected_tree_head *nhg_depends, - struct nexthop *nh, afi_t afi) + struct nexthop *nh, afi_t afi, int type) { struct nhg_hash_entry *depend = NULL; struct nexthop_group resolved_ng = {}; @@ -622,7 +654,7 @@ static void handle_recursive_depend(struct nhg_connected_tree_head *nhg_depends, zlog_debug("%s: head %p, nh %pNHv", __func__, nhg_depends, nh); - depend = zebra_nhg_rib_find(0, &resolved_ng, afi); + depend = zebra_nhg_rib_find(0, &resolved_ng, afi, type); if (IS_ZEBRA_DEBUG_NHG_DETAIL) zlog_debug("%s: nh %pNHv => %p (%u)", @@ -671,8 +703,26 @@ static bool zebra_nhe_find(struct nhg_hash_entry **nhe, /* return value */ * assign the next global id value if necessary. */ if (lookup->id == 0) - lookup->id = ++id_counter; - newnhe = hash_get(zrouter.nhgs, lookup, zebra_nhg_hash_alloc); + lookup->id = nhg_get_next_id(); + + if (lookup->id < ZEBRA_NHG_PROTO_LOWER) { + /* + * This is a zebra hashed/owned NHG. + * + * It goes in HASH and ID table. + */ + newnhe = hash_get(zrouter.nhgs, lookup, zebra_nhg_hash_alloc); + zebra_nhg_insert_id(newnhe); + } else { + /* + * This is upperproto owned NHG and should not be hashed to. + * + * It goes in ID table. + */ + newnhe = + hash_get(zrouter.nhgs_id, lookup, zebra_nhg_hash_alloc); + } + created = true; /* Mail back the new object */ @@ -709,14 +759,16 @@ static bool zebra_nhe_find(struct nhg_hash_entry **nhe, /* return value */ if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE)) SET_FLAG(newnhe->flags, NEXTHOP_GROUP_VALID); - if (nh->next == NULL) { + if (nh->next == NULL && newnhe->id < ZEBRA_NHG_PROTO_LOWER) { if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) { /* Single recursive nexthop */ handle_recursive_depend(&newnhe->nhg_depends, - nh->resolved, afi); + nh->resolved, afi, + newnhe->type); recursive = true; } } else { + /* Proto-owned are groups by default */ /* List of nexthops */ for (nh = newnhe->nhg.nexthop; nh; nh = nh->next) { if (IS_ZEBRA_DEBUG_NHG_DETAIL) @@ -726,7 +778,8 @@ static bool zebra_nhe_find(struct nhg_hash_entry **nhe, /* return value */ NEXTHOP_FLAG_RECURSIVE) ? "(R)" : ""); - depends_find_add(&newnhe->nhg_depends, nh, afi); + depends_find_add(&newnhe->nhg_depends, nh, afi, + newnhe->type); } } @@ -753,8 +806,8 @@ static bool zebra_nhe_find(struct nhg_hash_entry **nhe, /* return value */ __func__, nh); /* Single recursive nexthop */ - handle_recursive_depend(&backup_nhe->nhg_depends, - nh->resolved, afi); + handle_recursive_depend(&backup_nhe->nhg_depends, nh->resolved, + afi, backup_nhe->type); recursive = true; } else { /* One or more backup NHs */ @@ -766,8 +819,8 @@ static bool zebra_nhe_find(struct nhg_hash_entry **nhe, /* return value */ NEXTHOP_FLAG_RECURSIVE) ? "(R)" : ""); - depends_find_add(&backup_nhe->nhg_depends, - nh, afi); + depends_find_add(&backup_nhe->nhg_depends, nh, afi, + backup_nhe->type); } } @@ -960,30 +1013,6 @@ static struct nhg_ctx *nhg_ctx_init(uint32_t id, struct nexthop *nh, return ctx; } -static bool zebra_nhg_contains_unhashable(struct nhg_hash_entry *nhe) -{ - struct nhg_connected *rb_node_dep = NULL; - - frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) { - if (CHECK_FLAG(rb_node_dep->nhe->flags, - NEXTHOP_GROUP_UNHASHABLE)) - return true; - } - - return false; -} - -static void zebra_nhg_set_unhashable(struct nhg_hash_entry *nhe) -{ - SET_FLAG(nhe->flags, NEXTHOP_GROUP_UNHASHABLE); - SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); - - flog(LOG_INFO, - EC_ZEBRA_DUPLICATE_NHG_MESSAGE, - "Nexthop Group with ID (%d) is a duplicate, therefore unhashable, ignoring", - nhe->id); -} - static void zebra_nhg_set_valid(struct nhg_hash_entry *nhe) { struct nhg_connected *rb_node_dep; @@ -1025,23 +1054,27 @@ done: zebra_nhg_set_invalid(nhe); } - -static void zebra_nhg_release(struct nhg_hash_entry *nhe) +static void zebra_nhg_release_all_deps(struct nhg_hash_entry *nhe) { - if (IS_ZEBRA_DEBUG_NHG_DETAIL) - zlog_debug("%s: nhe %p (%u)", __func__, nhe, nhe->id); - /* Remove it from any lists it may be on */ zebra_nhg_depends_release(nhe); zebra_nhg_dependents_release(nhe); if (nhe->ifp) if_nhg_dependents_del(nhe->ifp, nhe); +} + +static void zebra_nhg_release(struct nhg_hash_entry *nhe) +{ + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nhe %p (%u)", __func__, nhe, nhe->id); + + zebra_nhg_release_all_deps(nhe); /* - * If its unhashable, we didn't store it here and have to be + * If its not zebra owned, we didn't store it here and have to be * sure we don't clear one thats actually being used. */ - if (!CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_UNHASHABLE)) + if (nhe->id < ZEBRA_NHG_PROTO_LOWER) hash_release(zrouter.nhgs, nhe); hash_release(zrouter.nhgs_id, nhe); @@ -1117,8 +1150,8 @@ static int nhg_ctx_process_new(struct nhg_ctx *ctx) return -ENOENT; } - if (!zebra_nhg_find(&nhe, id, nhg, &nhg_depends, vrf_id, type, - afi)) + if (!zebra_nhg_find(&nhe, id, nhg, &nhg_depends, vrf_id, afi, + type)) depends_decrement_free(&nhg_depends); /* These got copied over in zebra_nhg_alloc() */ @@ -1127,54 +1160,7 @@ static int nhg_ctx_process_new(struct nhg_ctx *ctx) nhe = zebra_nhg_find_nexthop(id, nhg_ctx_get_nh(ctx), afi, type); - if (nhe) { - if (id != nhe->id) { - struct nhg_hash_entry *kernel_nhe = NULL; - - /* Duplicate but with different ID from - * the kernel - */ - - /* The kernel allows duplicate nexthops - * as long as they have different IDs. - * We are ignoring those to prevent - * syncing problems with the kernel - * changes. - * - * We maintain them *ONLY* in the ID hash table to - * track them and set the flag to indicated - * their attributes are unhashable. - */ - - kernel_nhe = zebra_nhe_copy(nhe, id); - - if (IS_ZEBRA_DEBUG_NHG_DETAIL) - zlog_debug("%s: copying kernel nhe (%u), dup of %u", - __func__, id, nhe->id); - - zebra_nhg_insert_id(kernel_nhe); - zebra_nhg_set_unhashable(kernel_nhe); - } else if (zebra_nhg_contains_unhashable(nhe)) { - /* The group we got contains an unhashable/duplicated - * depend, so lets mark this group as unhashable as well - * and release it from the non-ID hash. - */ - if (IS_ZEBRA_DEBUG_NHG_DETAIL) - zlog_debug("%s: nhe %p (%u) unhashable", - __func__, nhe, nhe->id); - - hash_release(zrouter.nhgs, nhe); - zebra_nhg_set_unhashable(nhe); - } else { - /* It actually created a new nhe */ - if (IS_ZEBRA_DEBUG_NHG_DETAIL) - zlog_debug("%s: nhe %p (%u) is new", - __func__, nhe, nhe->id); - - SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID); - SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); - } - } else { + if (!nhe) { flog_err( EC_ZEBRA_TABLE_LOOKUP_FAILED, "Zebra failed to find or create a nexthop hash entry for ID (%u)", @@ -1182,6 +1168,12 @@ static int nhg_ctx_process_new(struct nhg_ctx *ctx) return -1; } + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nhe %p (%u) is new", __func__, nhe, nhe->id); + + SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID); + SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); + return 0; } @@ -1283,7 +1275,7 @@ int zebra_nhg_kernel_find(uint32_t id, struct nexthop *nh, struct nh_grp *grp, zlog_debug("%s: nh %pNHv, id %u, count %d", __func__, nh, id, (int)count); - if (id > id_counter) + if (id > id_counter && id < ZEBRA_NHG_PROTO_LOWER) /* Increase our counter so we don't try to create * an ID that already exists */ @@ -1326,14 +1318,14 @@ int zebra_nhg_kernel_del(uint32_t id, vrf_id_t vrf_id) /* Some dependency helper functions */ static struct nhg_hash_entry *depends_find_recursive(const struct nexthop *nh, - afi_t afi) + afi_t afi, int type) { struct nhg_hash_entry *nhe; struct nexthop *lookup = NULL; lookup = nexthop_dup(nh, NULL); - nhe = zebra_nhg_find_nexthop(0, lookup, afi, 0); + nhe = zebra_nhg_find_nexthop(0, lookup, afi, type); nexthops_free(lookup); @@ -1341,7 +1333,7 @@ static struct nhg_hash_entry *depends_find_recursive(const struct nexthop *nh, } static struct nhg_hash_entry *depends_find_singleton(const struct nexthop *nh, - afi_t afi) + afi_t afi, int type) { struct nhg_hash_entry *nhe; struct nexthop lookup = {}; @@ -1351,7 +1343,7 @@ static struct nhg_hash_entry *depends_find_singleton(const struct nexthop *nh, */ nexthop_copy_no_recurse(&lookup, nh, NULL); - nhe = zebra_nhg_find_nexthop(0, &lookup, afi, 0); + nhe = zebra_nhg_find_nexthop(0, &lookup, afi, type); /* The copy may have allocated labels; free them if necessary. */ nexthop_del_labels(&lookup); @@ -1363,7 +1355,8 @@ static struct nhg_hash_entry *depends_find_singleton(const struct nexthop *nh, return nhe; } -static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi) +static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi, + int type) { struct nhg_hash_entry *nhe = NULL; @@ -1374,9 +1367,9 @@ static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi) * in the non-recursive case (by not alloc/freeing) */ if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) - nhe = depends_find_recursive(nh, afi); + nhe = depends_find_recursive(nh, afi, type); else - nhe = depends_find_singleton(nh, afi); + nhe = depends_find_singleton(nh, afi, type); if (IS_ZEBRA_DEBUG_NHG_DETAIL) { @@ -1409,11 +1402,11 @@ static void depends_add(struct nhg_connected_tree_head *head, static struct nhg_hash_entry * depends_find_add(struct nhg_connected_tree_head *head, struct nexthop *nh, - afi_t afi) + afi_t afi, int type) { struct nhg_hash_entry *depend = NULL; - depend = depends_find(nh, afi); + depend = depends_find(nh, afi, type); if (IS_ZEBRA_DEBUG_NHG_DETAIL) zlog_debug("%s: nh %pNHv => %p", @@ -1445,8 +1438,9 @@ static void depends_decrement_free(struct nhg_connected_tree_head *head) } /* Find an nhe based on a list of nexthops */ -struct nhg_hash_entry * -zebra_nhg_rib_find(uint32_t id, struct nexthop_group *nhg, afi_t rt_afi) +struct nhg_hash_entry *zebra_nhg_rib_find(uint32_t id, + struct nexthop_group *nhg, + afi_t rt_afi, int type) { struct nhg_hash_entry *nhe = NULL; vrf_id_t vrf_id; @@ -1458,7 +1452,7 @@ zebra_nhg_rib_find(uint32_t id, struct nexthop_group *nhg, afi_t rt_afi) assert(nhg->nexthop); vrf_id = !vrf_is_backend_netns() ? VRF_DEFAULT : nhg->nexthop->vrf_id; - zebra_nhg_find(&nhe, id, nhg, NULL, vrf_id, rt_afi, 0); + zebra_nhg_find(&nhe, id, nhg, NULL, vrf_id, rt_afi, type); if (IS_ZEBRA_DEBUG_NHG_DETAIL) zlog_debug("%s: => nhe %p (%u)", @@ -1829,13 +1823,13 @@ static int nexthop_active(afi_t afi, struct route_entry *re, if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) { ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); if (!ifp) { - if (IS_ZEBRA_DEBUG_NHG_DETAIL) + if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug("nexthop %pNHv marked onlink but nhif %u doesn't exist", nexthop, nexthop->ifindex); return 0; } if (!if_is_operative(ifp)) { - if (IS_ZEBRA_DEBUG_NHG_DETAIL) + if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug("nexthop %pNHv marked onlink but nhif %s is not operational", nexthop, ifp->name); return 0; @@ -1991,6 +1985,11 @@ static int nexthop_active(afi_t afi, struct route_entry *re, || nexthop->type == NEXTHOP_TYPE_IPV6) nexthop->ifindex = newhop->ifindex; else if (nexthop->ifindex != newhop->ifindex) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + "%s: %pNHv given ifindex does not match nexthops ifindex found found: %pNHv", + __func__, nexthop, + newhop); /* * NEXTHOP_TYPE_*_IFINDEX but ifindex * doesn't match what we found. @@ -2012,7 +2011,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, /* Only useful if installed */ if (!CHECK_FLAG(match->status, ROUTE_ENTRY_INSTALLED)) { - if (IS_ZEBRA_DEBUG_NHG_DETAIL) + if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug("%s: match %p (%u) not installed", __func__, match, match->nhe->id); @@ -2326,6 +2325,22 @@ static uint32_t nexthop_list_active_update(struct route_node *rn, return counter; } + +static uint32_t proto_nhg_nexthop_active_update(struct nexthop_group *nhg) +{ + struct nexthop *nh; + uint32_t curr_active = 0; + + /* Assume all active for now */ + + for (nh = nhg->nexthop; nh; nh = nh->next) { + SET_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE); + curr_active++; + } + + return curr_active; +} + /* * Iterate over all nexthops of the given RIB entry and refresh their * ACTIVE flag. If any nexthop is found to toggle the ACTIVE flag, @@ -2338,6 +2353,9 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re) struct nhg_hash_entry *curr_nhe; uint32_t curr_active = 0, backup_active = 0; + if (re->nhe->id >= ZEBRA_NHG_PROTO_LOWER) + return proto_nhg_nexthop_active_update(&re->nhe->nhg); + afi_t rt_afi = family2afi(rn->p.family); UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED); @@ -2543,7 +2561,8 @@ void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe) && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED) && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED)) { /* Change its type to us since we are installing it */ - nhe->type = ZEBRA_ROUTE_NHG; + if (!ZEBRA_NHG_CREATED(nhe)) + nhe->type = ZEBRA_ROUTE_NHG; int ret = dplane_nexthop_add(nhe); @@ -2700,3 +2719,251 @@ bool zebra_nhg_kernel_nexthops_enabled(void) { return g_nexthops_enabled; } + +/* + * Global control to only use kernel nexthops for protocol created NHGs. + * There are some use cases where you may not want zebra to implicitly + * create kernel nexthops for all routes and only create them for NHGs + * passed down by upper level protos. + * + * Default is off. + */ +void zebra_nhg_set_proto_nexthops_only(bool set) +{ + proto_nexthops_only = set; +} + +bool zebra_nhg_proto_nexthops_only(void) +{ + return proto_nexthops_only; +} + +/* Add NHE from upper level proto */ +struct nhg_hash_entry *zebra_nhg_proto_add(uint32_t id, int type, + struct nexthop_group *nhg, afi_t afi) +{ + struct nhg_hash_entry lookup; + struct nhg_hash_entry *new, *old; + struct nhg_connected *rb_node_dep = NULL; + struct nexthop *newhop; + bool replace = false; + + if (!nhg->nexthop) { + if (IS_ZEBRA_DEBUG_NHG) + zlog_debug("%s: id %u, no nexthops passed to add", + __func__, id); + return NULL; + } + + + /* Set nexthop list as active, since they wont go through rib + * processing. + * + * Assuming valid/onlink for now. + * + * Once resolution is figured out, we won't need this! + */ + for (ALL_NEXTHOPS_PTR(nhg, newhop)) { + if (CHECK_FLAG(newhop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + if (IS_ZEBRA_DEBUG_NHG) + zlog_debug( + "%s: id %u, backup nexthops not supported", + __func__, id); + return NULL; + } + + if (newhop->type == NEXTHOP_TYPE_BLACKHOLE) { + if (IS_ZEBRA_DEBUG_NHG) + zlog_debug( + "%s: id %u, blackhole nexthop not supported", + __func__, id); + return NULL; + } + + if (newhop->type == NEXTHOP_TYPE_IFINDEX) { + if (IS_ZEBRA_DEBUG_NHG) + zlog_debug( + "%s: id %u, nexthop without gateway not supported", + __func__, id); + return NULL; + } + + if (!newhop->ifindex) { + if (IS_ZEBRA_DEBUG_NHG) + zlog_debug( + "%s: id %u, nexthop without ifindex is not supported", + __func__, id); + return NULL; + } + SET_FLAG(newhop->flags, NEXTHOP_FLAG_ACTIVE); + } + + zebra_nhe_init(&lookup, afi, nhg->nexthop); + lookup.nhg.nexthop = nhg->nexthop; + lookup.id = id; + lookup.type = type; + + old = zebra_nhg_lookup_id(id); + + if (old) { + /* + * This is a replace, just release NHE from ID for now, The + * depends/dependents may still be used in the replacement. + */ + replace = true; + hash_release(zrouter.nhgs_id, old); + } + + new = zebra_nhg_rib_find_nhe(&lookup, afi); + + zebra_nhg_increment_ref(new); + + zebra_nhg_set_valid_if_active(new); + + zebra_nhg_install_kernel(new); + + if (old) { + /* + * Check to handle recving DEL while routes still in use then + * a replace. + * + * In this case we would have decremented the refcnt already + * but set the FLAG here. Go ahead and increment once to fix + * the misordering we have been sent. + */ + if (CHECK_FLAG(old->flags, NEXTHOP_GROUP_PROTO_RELEASED)) + zebra_nhg_increment_ref(old); + + rib_handle_nhg_replace(old, new); + + /* if this != 1 at this point, we have a bug */ + assert(old->refcnt == 1); + + /* We have to decrement its singletons + * because some might not exist in NEW. + */ + if (!zebra_nhg_depends_is_empty(old)) { + frr_each (nhg_connected_tree, &old->nhg_depends, + rb_node_dep) + zebra_nhg_decrement_ref(rb_node_dep->nhe); + } + + /* Free all the things */ + zebra_nhg_release_all_deps(old); + + /* Dont call the dec API, we dont want to uninstall the ID */ + old->refcnt = 0; + zebra_nhg_free(old); + old = NULL; + } + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: %s nhe %p (%u), vrf %d, type %s", __func__, + (replace ? "replaced" : "added"), new, new->id, + new->vrf_id, zebra_route_string(new->type)); + + return new; +} + +/* Delete NHE from upper level proto, caller must decrement ref */ +struct nhg_hash_entry *zebra_nhg_proto_del(uint32_t id, int type) +{ + struct nhg_hash_entry *nhe; + + nhe = zebra_nhg_lookup_id(id); + + if (!nhe) { + if (IS_ZEBRA_DEBUG_NHG) + zlog_debug("%s: id %u, lookup failed", __func__, id); + + return NULL; + } + + if (type != nhe->type) { + if (IS_ZEBRA_DEBUG_NHG) + zlog_debug( + "%s: id %u, type %s mismatch, sent by %s, ignoring", + __func__, id, zebra_route_string(nhe->type), + zebra_route_string(type)); + return NULL; + } + + if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_PROTO_RELEASED)) { + if (IS_ZEBRA_DEBUG_NHG) + zlog_debug("%s: id %u, already released", __func__, id); + + return NULL; + } + + SET_FLAG(nhe->flags, NEXTHOP_GROUP_PROTO_RELEASED); + + if (nhe->refcnt > 1) { + if (IS_ZEBRA_DEBUG_NHG) + zlog_debug( + "%s: id %u, still being used by routes refcnt %u", + __func__, nhe->id, nhe->refcnt); + return nhe; + } + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: deleted nhe %p (%u), vrf %d, type %s", __func__, + nhe, nhe->id, nhe->vrf_id, + zebra_route_string(nhe->type)); + + return nhe; +} + +struct nhg_score_proto_iter { + int type; + struct list *found; +}; + +static void zebra_nhg_score_proto_entry(struct hash_bucket *bucket, void *arg) +{ + struct nhg_hash_entry *nhe; + struct nhg_score_proto_iter *iter; + + nhe = (struct nhg_hash_entry *)bucket->data; + iter = arg; + + /* Needs to match type and outside zebra ID space */ + if (nhe->type == iter->type && nhe->id >= ZEBRA_NHG_PROTO_LOWER) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug( + "%s: found nhe %p (%u), vrf %d, type %s after client disconnect", + __func__, nhe, nhe->id, nhe->vrf_id, + zebra_route_string(nhe->type)); + + /* Add to removal list */ + listnode_add(iter->found, nhe); + } +} + +/* Remove specific by proto NHGs */ +unsigned long zebra_nhg_score_proto(int type) +{ + struct nhg_hash_entry *nhe; + struct nhg_score_proto_iter iter = {}; + struct listnode *ln; + unsigned long count; + + iter.type = type; + iter.found = list_new(); + + /* Find matching entries to remove */ + hash_iterate(zrouter.nhgs_id, zebra_nhg_score_proto_entry, &iter); + + /* Now remove them */ + for (ALL_LIST_ELEMENTS_RO(iter.found, ln, nhe)) { + /* + * This should be the last ref if we remove client routes too, + * and thus should remove and free them. + */ + zebra_nhg_decrement_ref(nhe); + } + + count = iter.found->count; + list_delete(&iter.found); + + return count; +} diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h index de5f097472..052fa65d06 100644 --- a/zebra/zebra_nhg.h +++ b/zebra/zebra_nhg.h @@ -102,20 +102,23 @@ struct nhg_hash_entry { * Is this a nexthop that is recursively resolved? */ #define NEXTHOP_GROUP_RECURSIVE (1 << 3) -/* - * This is a nexthop group we got from the kernel, it is identical to - * one we already have. (The kernel allows duplicate nexthops, we don't - * since we hash on them). We are only tracking it in our ID table, - * it is unusable by our created routes but may be used by routes we get - * from the kernel. Therefore, it is unhashable. - */ -#define NEXTHOP_GROUP_UNHASHABLE (1 << 4) /* * Backup nexthop support - identify groups that are backups for * another group. */ -#define NEXTHOP_GROUP_BACKUP (1 << 5) +#define NEXTHOP_GROUP_BACKUP (1 << 4) + +/* + * The NHG has been release by an upper level protocol via the + * `zebra_nhg_proto_del()` API. + * + * We use this flag to track this state in case the NHG is still being used + * by routes therefore holding their refcnts as well. Otherwise, the NHG will + * be removed and uninstalled. + * + */ +#define NEXTHOP_GROUP_PROTO_RELEASED (1 << 5) /* * Track FPM installation status.. @@ -124,7 +127,11 @@ struct nhg_hash_entry { }; /* Was this one we created, either this session or previously? */ -#define ZEBRA_NHG_CREATED(NHE) ((NHE->type) == ZEBRA_ROUTE_NHG) +#define ZEBRA_NHG_CREATED(NHE) \ + (((NHE->type) <= ZEBRA_ROUTE_MAX) && (NHE->type != ZEBRA_ROUTE_KERNEL)) + +/* Is this an NHE owned by zebra and not an upper level protocol? */ +#define ZEBRA_OWNED(NHE) (NHE->type == ZEBRA_ROUTE_NHG) /* * Backup nexthops: this is a group object itself, so @@ -186,6 +193,10 @@ struct nhg_ctx { void zebra_nhg_enable_kernel_nexthops(bool set); bool zebra_nhg_kernel_nexthops_enabled(void); +/* Global control for zebra to only use proto-owned nexthops */ +void zebra_nhg_set_proto_nexthops_only(bool set); +bool zebra_nhg_proto_nexthops_only(void); + /** * NHE abstracted tree functions. * Use these where possible instead of direct access. @@ -249,13 +260,50 @@ extern int zebra_nhg_kernel_find(uint32_t id, struct nexthop *nh, extern int zebra_nhg_kernel_del(uint32_t id, vrf_id_t vrf_id); /* Find an nhe based on a nexthop_group */ -extern struct nhg_hash_entry * -zebra_nhg_rib_find(uint32_t id, struct nexthop_group *nhg, afi_t rt_afi); +extern struct nhg_hash_entry *zebra_nhg_rib_find(uint32_t id, + struct nexthop_group *nhg, + afi_t rt_afi, int type); /* Find an nhe based on a route's nhe, used during route creation */ struct nhg_hash_entry * zebra_nhg_rib_find_nhe(struct nhg_hash_entry *rt_nhe, afi_t rt_afi); + +/** + * Functions for Add/Del/Replace via protocol NHG creation. + * + * The NHEs will not be hashed. They will only be present in the + * ID table and therefore not sharable. + * + * It is the owning protocols job to manage these. + */ + +/* + * Add NHE. If already exists, Replace. + * + * Returns allocated NHE on success, otherwise NULL. + */ +struct nhg_hash_entry *zebra_nhg_proto_add(uint32_t id, int type, + struct nexthop_group *nhg, + afi_t afi); + +/* + * Del NHE. + * + * Returns deleted NHE on success, otherwise NULL. + * + * Caller must decrement ref with zebra_nhg_decrement_ref() when done. + */ +struct nhg_hash_entry *zebra_nhg_proto_del(uint32_t id, int type); + +/* + * Remove specific by proto NHGs. + * + * Called after client disconnect. + * + */ +unsigned long zebra_nhg_score_proto(int type); + /* Reference counter functions */ extern void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe); extern void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index ff30de18a3..aac0e628fe 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -235,7 +235,7 @@ int route_entry_update_nhe(struct route_entry *re, goto done; } - if ((re->nhe_id != 0) && (re->nhe_id != new_nhghe->id)) { + if ((re->nhe_id != 0) && re->nhe && (re->nhe != new_nhghe)) { old = re->nhe; route_entry_attach_ref(re, new_nhghe); @@ -250,6 +250,29 @@ done: return ret; } +void rib_handle_nhg_replace(struct nhg_hash_entry *old, + struct nhg_hash_entry *new) +{ + struct zebra_router_table *zrt; + struct route_node *rn; + struct route_entry *re, *next; + + if (IS_ZEBRA_DEBUG_RIB_DETAILED || IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: replacing routes nhe (%u) OLD %p NEW %p", + __func__, new->id, new, old); + + /* We have to do them ALL */ + RB_FOREACH (zrt, zebra_router_table_head, &zrouter.tables) { + for (rn = route_top(zrt->table); rn; + rn = srcdest_route_next(rn)) { + RNODE_FOREACH_RE_SAFE (rn, re, next) { + if (re->nhe && re->nhe == old) + route_entry_update_nhe(re, new); + } + } + } +} + struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, union g_addr *addr, struct route_node **rn_out) { @@ -2906,14 +2929,14 @@ int rib_add_multipath_nhe(afi_t afi, safi_t safi, struct prefix *p, if (!table) return -1; - if (re_nhe->id > 0) { - nhe = zebra_nhg_lookup_id(re_nhe->id); + if (re->nhe_id > 0) { + nhe = zebra_nhg_lookup_id(re->nhe_id); if (!nhe) { flog_err( EC_ZEBRA_TABLE_LOOKUP_FAILED, "Zebra failed to find the nexthop hash entry for id=%u in a route entry", - re_nhe->id); + re->nhe_id); return -1; } diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 0088b49512..6785151705 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -1300,13 +1300,10 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe) struct nhg_connected *rb_node_dep = NULL; struct nexthop_group *backup_nhg; - vty_out(vty, "ID: %u\n", nhe->id); + vty_out(vty, "ID: %u (%s)\n", nhe->id, zebra_route_string(nhe->type)); vty_out(vty, " RefCnt: %d\n", nhe->refcnt); vty_out(vty, " VRF: %s\n", vrf_id_to_name(nhe->vrf_id)); - if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_UNHASHABLE)) - vty_out(vty, " Duplicate - from kernel not hashable\n"); - if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_VALID)) { vty_out(vty, " Valid"); if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED)) @@ -1569,6 +1566,17 @@ DEFPY_HIDDEN(nexthop_group_use_enable, return CMD_SUCCESS; } +DEFPY_HIDDEN(proto_nexthop_group_only, proto_nexthop_group_only_cmd, + "[no] zebra nexthop proto only", + NO_STR ZEBRA_STR + "Nexthop configuration\n" + "Configure exclusive use of proto nexthops\n" + "Only use proto nexthops\n") +{ + zebra_nhg_set_proto_nexthops_only(!no); + return CMD_SUCCESS; +} + DEFUN (no_ip_nht_default_route, no_ip_nht_default_route_cmd, "no ip nht resolve-via-default", @@ -3451,6 +3459,9 @@ static int config_write_protocol(struct vty *vty) if (!zebra_nhg_kernel_nexthops_enabled()) vty_out(vty, "no zebra nexthop kernel enable\n"); + if (zebra_nhg_proto_nexthops_only()) + vty_out(vty, "zebra nexthop proto only\n"); + #ifdef HAVE_NETLINK /* Include netlink info */ netlink_config_write_helper(vty); @@ -3885,6 +3896,7 @@ void zebra_vty_init(void) install_element(CONFIG_NODE, &zebra_packet_process_cmd); install_element(CONFIG_NODE, &no_zebra_packet_process_cmd); install_element(CONFIG_NODE, &nexthop_group_use_enable_cmd); + install_element(CONFIG_NODE, &proto_nexthop_group_only_cmd); install_element(VIEW_NODE, &show_nexthop_group_cmd); install_element(VIEW_NODE, &show_interface_nexthop_group_cmd); diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 61498973e9..d8ed58edef 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -4594,6 +4594,11 @@ int zebra_vxlan_if_down(struct interface *ifp) assert(zevpn->vxlan_if == ifp); + /* remove from l3-vni list */ + zl3vni = zl3vni_from_vrf(zevpn->vrf_id); + if (zl3vni) + listnode_delete(zl3vni->l2vnis, zevpn); + /* Delete this VNI from BGP. */ zebra_evpn_send_del_to_client(zevpn); @@ -4668,7 +4673,7 @@ int zebra_vxlan_if_up(struct interface *ifp) zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); if (zl3vni) - listnode_add_sort(zl3vni->l2vnis, zevpn); + listnode_add_sort_nodup(zl3vni->l2vnis, zevpn); } /* If part of a bridge, inform BGP about this VNI. */ @@ -5007,7 +5012,7 @@ int zebra_vxlan_if_add(struct interface *ifp) zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); if (zl3vni) - listnode_add_sort(zl3vni->l2vnis, zevpn); + listnode_add_sort_nodup(zl3vni->l2vnis, zevpn); } if (IS_ZEBRA_DEBUG_VXLAN) { diff --git a/zebra/zserv.c b/zebra/zserv.c index 4c8656af0d..44f4641fcf 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -590,6 +590,7 @@ static void zserv_client_free(struct zserv *client) /* Close file descriptor. */ if (client->sock) { unsigned long nroutes; + unsigned long nnhgs; close(client->sock); @@ -600,6 +601,13 @@ static void zserv_client_free(struct zserv *client) "client %d disconnected %lu %s routes removed from the rib", client->sock, nroutes, zebra_route_string(client->proto)); + + /* Not worrying about instance for now */ + nnhgs = zebra_nhg_score_proto(client->proto); + zlog_notice( + "client %d disconnected %lu %s nhgs removed from the rib", + client->sock, nnhgs, + zebra_route_string(client->proto)); } client->sock = -1; } |
